Skip to content

Commit

Permalink
Increased robustness of getting supports attribute from ResultHandlers
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Meisrimel authored and Peter Meisrimel committed Jan 10, 2025
1 parent 5d0eebe commit 80cf63f
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 9 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
--- CHANGELOG ---
--- Future ---
--- PyFMI-FUTURE ---
* Increased robustness of getting the `supports` attribute from ResultHandlers.

--- PyFMI-2.16.1 ---
* Fixed an issue with `get_variables_data` returning full trajectories when mixed with `get_variable_data` calls.

--- PyFMI-2.16.0 ---
Expand Down
4 changes: 2 additions & 2 deletions src/common/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def diagnostics_point(self, diag_data = None):
"""
Generates a data point for diagnostics data.
"""
if self.supports.get('dynamic_diagnostics'):
if self.supports.get('dynamic_diagnostics', False):
raise NotImplementedError

def simulation_end(self):
Expand Down Expand Up @@ -2936,7 +2936,7 @@ def get_result_handler(model, opts):
else:
raise fmi.FMUException("Unknown option to result_handling.")

if (opts.get("result_max_size", 0) > 0) and not result_handler.supports["result_max_size"]:
if (opts.get("result_max_size", 0) > 0) and not result_handler.supports.get("result_max_size", False):
logging_module.warning("The chosen result handler does not support limiting the result size. Ignoring option 'result_max_size'.")

return result_handler
8 changes: 4 additions & 4 deletions src/pyfmi/fmi_algorithm_drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ def __init__(self,
self._set_absolute_tolerance_options()

number_of_diagnostics_variables = 0
if self.result_handler.supports.get('dynamic_diagnostics'):
if self.result_handler.supports.get('dynamic_diagnostics', False):
_diagnostics_params, _diagnostics_vars = setup_diagnostics_variables(model = self.model,
start_time = self.start_time,
options = self.options,
Expand All @@ -377,7 +377,7 @@ def __init__(self,
self.timings["initializing_fmu"] = time_end - time_start - time_res_init
time_start = time_end

if self.result_handler.supports.get('dynamic_diagnostics'):
if self.result_handler.supports.get('dynamic_diagnostics', False):
self.result_handler.simulation_start(_diagnostics_params, _diagnostics_vars)
else:
self.result_handler.simulation_start()
Expand Down Expand Up @@ -512,13 +512,13 @@ def _set_options(self):
if self.options["dynamic_diagnostics"]:
## Result handler must have supports['dynamic_diagnostics'] = True
## e.g., result_handling = 'binary' = ResultHandlerBinaryFile
if not self.result_handler.supports.get('dynamic_diagnostics'):
if not self.result_handler.supports.get('dynamic_diagnostics', False):
err_msg = ("The chosen result_handler does not support dynamic_diagnostics."
" Try using e.g., ResultHandlerBinaryFile.")
raise fmi.InvalidOptionException(err_msg)
self.options['logging'] = True
elif self.options['logging']:
if self.result_handler.supports.get('dynamic_diagnostics'):
if self.result_handler.supports.get('dynamic_diagnostics', False):
self.options["dynamic_diagnostics"] = True

# solver options
Expand Down
4 changes: 2 additions & 2 deletions src/pyfmi/simulation/assimulo_interface.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class FMIODE(Explicit_Problem):
self._logging = logging

## If result handler support is available, logging turns into dynamic_diagnostics
self._logging_as_dynamic_diagnostics = self._logging and result_handler.supports.get("dynamic_diagnostics")
self._logging_as_dynamic_diagnostics = self._logging and result_handler.supports.get("dynamic_diagnostics", False)

#Stores the first time point
#[r,i,b] = self._model.save_time_point()
Expand Down Expand Up @@ -634,7 +634,7 @@ cdef class FMIODE2(cExplicit_Problem):
self._with_jacobian = with_jacobian

## If result handler support is available, logging turns into dynamic_diagnostics
self._logging_as_dynamic_diagnostics = self._logging and result_handler.supports.get("dynamic_diagnostics")
self._logging_as_dynamic_diagnostics = self._logging and result_handler.supports.get("dynamic_diagnostics", False)
self._number_of_diagnostics_variables = number_of_diagnostics_variables

self.jac_use = False
Expand Down
42 changes: 42 additions & 0 deletions tests/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import numpy as np
import time
import math
import re
from io import StringIO, BytesIO
from collections import OrderedDict

Expand All @@ -28,6 +29,7 @@
FMUModelME2,
FMI2_PARAMETER,
FMI2_CONSTANT,
InvalidOptionException
)
from pyfmi.common.io import (
ResultHandler,
Expand Down Expand Up @@ -2156,3 +2158,43 @@ def test_large_size_memory(self):

def test_large_size_memory_stream(self):
self._test_result("memory", StringIO())


class ResultHandlerCustomNoSupport(ResultHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.supports = {} # Don't do that

class TestCustomResultHandlerMissingSupport:
"""Test that ResultHandlers fail gracefully, even if one overwrites the supports attribute."""
# caplog = pytest.LogCaptureFixture
def test_limit_result_size(self, caplog):
"""Test limiting the result size when support is missing."""
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)

opts = model.simulate_options()
opts["result_handling"] = "custom"
opts["result_handler"] = ResultHandlerCustomNoSupport(model)
opts["result_file_name"] = "res.mat"
opts["result_max_size"] = 1000

with pytest.raises(NotImplementedError):
model.simulate(options = opts) # missing get_result implementation

msg = "The chosen result handler does not support limiting the result size. Ignoring option 'result_max_size'."
assert msg in caplog.text

def test_dynamic_diags(self):
"""Test simulation with DynamicDiagnostics."""
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)

opts = model.simulate_options()
opts["result_handling"] = "custom"
opts["result_handler"] = ResultHandlerCustomNoSupport(model)
opts["result_file_name"] = "res.mat"
opts["result_max_size"] = 0
opts["dynamic_diagnostics"] = True

msg = "The chosen result_handler does not support dynamic_diagnostics. Try using e.g., ResultHandlerBinaryFile."
with pytest.raises(InvalidOptionException, match = re.escape(msg)):
model.simulate(options = opts) # missing get_result implementation

0 comments on commit 80cf63f

Please sign in to comment.