Skip to content

Commit

Permalink
Fixed review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
modelonrobinandersson committed Feb 20, 2025
1 parent a4ac66b commit 2b3b66e
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 29 deletions.
9 changes: 6 additions & 3 deletions src/pyfmi/fmi3.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ cdef class FMUModelBase3(FMI_BASE.ModelBase):
cdef FMIL3.fmi3_import_t* _fmu
cdef FMIL3.fmi3_fmu_kind_enu_t _fmu_kind
cdef FMIL.fmi_version_enu_t _version
cdef FMIL.size_t _nEventIndicators
cdef FMIL.size_t _nContinuousStates


# Internal values
cdef public float _last_accepted_time
Expand All @@ -39,8 +38,12 @@ cdef class FMUModelBase3(FMI_BASE.ModelBase):
cdef int _allow_unzipped_fmu
cdef int _allocated_context, _allocated_dll, _allocated_fmu, _allocated_xml

cdef int _initialized_fmu
cdef object _has_entered_init_mode # this is public in FMI2 but I don't see why

cdef class FMUModelME3(FMUModelBase3):
pass
cdef FMIL.size_t _nEventIndicators
cdef FMIL.size_t _nContinuousStates

cdef void _cleanup_on_load_error(
FMIL3.fmi3_import_t* fmu_3,
Expand Down
50 changes: 36 additions & 14 deletions src/pyfmi/fmi3.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -206,18 +206,6 @@ cdef class FMUModelBase3(FMI_BASE.ModelBase):

self._modelName = pyfmi_util.decode(FMIL3.fmi3_import_get_model_name(self._fmu))

self._log_handler.capi_start_callback(self._max_log_size_msg_sent, self._current_log_size)
status = FMIL3.fmi3_import_get_number_of_event_indicators(self._fmu, &self._nEventIndicators)
self._log_handler.capi_end_callback(self._max_log_size_msg_sent, self._current_log_size)
if status != FMIL3.fmi3_status_ok:
raise InvalidFMUException("The FMU could not be instantiated, error retrieving number of event indicators.")

self._log_handler.capi_start_callback(self._max_log_size_msg_sent, self._current_log_size)
status = FMIL3.fmi3_import_get_number_of_continuous_states(self._fmu, &self._nContinuousStates)
self._log_handler.capi_end_callback(self._max_log_size_msg_sent, self._current_log_size)
if status != FMIL3.fmi3_status_ok:
raise InvalidFMUException("The FMU could not be instantiated, error retrieving number of continuous states.")

# TODO: The code below is identical between FMUModelBase2 and FMUModelBase3, perhaps we can refactor this
if not isinstance(log_file_name, str):
self._set_log_stream(log_file_name)
Expand Down Expand Up @@ -268,8 +256,26 @@ cdef class FMUModelBase3(FMI_BASE.ModelBase):
self._log_stream = None

def reset(self):
""" TODO """
""" Resets the FMU back to its original state. Note that the environment
has to initialize the FMU again after this function-call. """
cdef FMIL3.fmi3_status_t status

self._log_handler.capi_start_callback(self._max_log_size_msg_sent, self._current_log_size)
status = FMIL3.fmi3_import_reset(self._fmu)
self._log_handler.capi_end_callback(self._max_log_size_msg_sent, self._current_log_size)
if status != 0:
raise FMUException('An error occured when reseting the model, see the log for possible more information')


#Default values
self._t = None
self._has_entered_init_mode = False

#Reseting the allocation flags
self._initialized_fmu = 0

#Internal values
self._log = []

def _get_fmu_kind(self):
raise FMUException("FMUModelBase3 cannot be used directly, use FMUModelME3.")
Expand Down Expand Up @@ -366,6 +372,19 @@ cdef class FMUModelME3(FMUModelBase3):
# Call super on base class
FMUModelBase3.__init__(self, fmu, log_file_name, log_level,
_unzipped_dir, _connect_dll, allow_unzipped_fmu)

self._log_handler.capi_start_callback(self._max_log_size_msg_sent, self._current_log_size)
status = FMIL3.fmi3_import_get_number_of_event_indicators(self._fmu, &self._nEventIndicators)
self._log_handler.capi_end_callback(self._max_log_size_msg_sent, self._current_log_size)
if status != FMIL3.fmi3_status_ok:
raise InvalidFMUException("The FMU could not be instantiated, error retrieving number of event indicators.")

self._log_handler.capi_start_callback(self._max_log_size_msg_sent, self._current_log_size)
status = FMIL3.fmi3_import_get_number_of_continuous_states(self._fmu, &self._nContinuousStates)
self._log_handler.capi_end_callback(self._max_log_size_msg_sent, self._current_log_size)
if status != FMIL3.fmi3_status_ok:
raise InvalidFMUException("The FMU could not be instantiated, error retrieving number of continuous states.")

if _connect_dll:
self.instantiate()

Expand Down Expand Up @@ -427,7 +446,6 @@ cdef class FMUModelME3(FMUModelBase3):
):
"""
Initializes the model and computes initial values for all variables.
Additionally calls the setup experiment, if not already called.
Args:
tolerance_defined --
Expand Down Expand Up @@ -477,6 +495,8 @@ cdef class FMUModelME3(FMUModelBase3):
if not log_open and self.get_log_level() > 2:
self._close_log_file()

self._initialized_fmu = 1

def enter_initialization_mode(
self,
tolerance_defined=True,
Expand Down Expand Up @@ -523,6 +543,8 @@ cdef class FMUModelME3(FMUModelBase3):
if status != FMIL3.fmi3_status_ok:
raise FMUException("Failed to enter initialization mode")

self._has_entered_init_mode = True

def exit_initialization_mode(self):
"""
Exit initialization mode by calling the low level FMI function
Expand Down
2 changes: 1 addition & 1 deletion src/pyfmi/fmil3_import.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ cdef extern from 'fmilib.h':
fmi3_status_t fmi3_import_exit_initialization_mode(fmi3_import_t* fmu)
fmi3_status_t fmi3_import_enter_event_mode(fmi3_import_t* fmu)
fmi3_status_t fmi3_import_enter_continuous_time_mode(fmi3_import_t* fmu)
fmi3_status_t fmi3_import_enter_event_mode(fmi3_import_t* fmu)
# misc
char* fmi3_import_get_version(fmi3_import_t*)
fmi3_status_t fmi3_import_reset(fmi3_import_t* fmu)

# setting
fmi3_status_t fmi3_import_set_time(fmi3_import_t *, fmi3_float64_t)
Expand Down
50 changes: 39 additions & 11 deletions tests/test_fmi3.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,12 @@ def test_load_kind_SE(self, ref_fmu):

def test_get_model_identifier(self):
"""Test that model identifier is retrieved as expected."""
fmu = load_fmu(FMI3_REF_FMU_PATH / "VanDerPol.fmu") # any FMI3 ME would suffice
fmu = load_fmu(FMI3_REF_FMU_PATH / "VanDerPol.fmu")
assert fmu.get_identifier() == 'VanDerPol'

def test_get_get_version(self):
"""Test that FMI version is retrieved as expected."""
fmu = load_fmu(FMI3_REF_FMU_PATH / "VanDerPol.fmu") # any FMI3 ME would suffice
fmu = load_fmu(FMI3_REF_FMU_PATH / "VanDerPol.fmu")
assert fmu.get_version() == '3.0'

def test_instantiation(self, tmpdir):
Expand All @@ -107,18 +107,12 @@ def test_instantiation(self, tmpdir):
substring_to_find = 'Successfully loaded all the interface functions'

with temp_dir_context(tmpdir) as temp_path:
# any FMI3 ME would suffice, log_level set to 5 required by test
# log_level set to 5 required by test
fmu = load_fmu(FMI3_REF_FMU_PATH / "VanDerPol.fmu", log_level=5)
with open(fmu.get_log_filename(), 'r') as f:
contents = f.readlines()

for line in contents:
if substring_to_find in line:
found_substring = True
break

log_file = ''.join(contents)
assert found_substring, f"Unable to locate substring '{substring_to_find}' in file with contents '{log_file}'"
assert any(substring_to_find in line for line in fmu.get_log())

@pytest.mark.parametrize("ref_fmu", [
FMI3_REF_FMU_PATH / "BouncingBall.fmu",
Expand All @@ -129,11 +123,45 @@ def test_instantiation(self, tmpdir):
FMI3_REF_FMU_PATH / "Stair.fmu",
FMI3_REF_FMU_PATH / "VanDerPol.fmu",
])
def test_load_kind_auto(self, ref_fmu):
def test_initialize(self, ref_fmu):
"""Test initialize all the ME reference FMUs. """
fmu = load_fmu(ref_fmu)
fmu.initialize() # Should simply pass without any exceptions


@pytest.mark.parametrize("ref_fmu", [
FMI3_REF_FMU_PATH / "BouncingBall.fmu",
FMI3_REF_FMU_PATH / "Dahlquist.fmu",
FMI3_REF_FMU_PATH / "Resource.fmu",
FMI3_REF_FMU_PATH / "StateSpace.fmu",
FMI3_REF_FMU_PATH / "Feedthrough.fmu",
FMI3_REF_FMU_PATH / "Stair.fmu",
FMI3_REF_FMU_PATH / "VanDerPol.fmu",
])
def test_initialize_manually(self, ref_fmu):
"""Test initialize all the ME reference FMUs by entering/exiting initialization mode manually. """
fmu = load_fmu(ref_fmu)
# Should simply pass without any exceptions
fmu.enter_initialization_mode()
fmu.exit_initialization_mode()

def test_get_default_experiment_start_time(self):
"""Test retrieving default experiment start time. """
fmu = load_fmu(FMI3_REF_FMU_PATH / "VanDerPol.fmu")
assert fmu.get_default_experiment_start_time() == 0.0


def test_get_default_experiment_stop_time(self):
"""Test retrieving default experiment stop time. """
fmu = load_fmu(FMI3_REF_FMU_PATH / "VanDerPol.fmu")
assert fmu.get_default_experiment_stop_time() == 20.0


def test_get_default_experiment_tolerance(self):
"""Test retrieving default experiment tolerance. """
fmu = load_fmu(FMI3_REF_FMU_PATH / "VanDerPol.fmu")
assert fmu.get_default_experiment_tolerance() == 0.0001

class Test_FMI3ME:
"""Basic unit tests for FMI3 import directly via the FMUModelME3 class."""
@pytest.mark.parametrize("ref_fmu", [FMI3_REF_FMU_PATH / "VanDerPol.fmu"])
Expand Down

0 comments on commit 2b3b66e

Please sign in to comment.