diff --git a/src/pyfmi/fmi_algorithm_drivers.py b/src/pyfmi/fmi_algorithm_drivers.py index a0fb1ae6..404b5a77 100644 --- a/src/pyfmi/fmi_algorithm_drivers.py +++ b/src/pyfmi/fmi_algorithm_drivers.py @@ -374,11 +374,22 @@ def __init__(self, solver_options = self.solver_options) number_of_diagnostics_variables = len(_diagnostics_vars) - #See if there is an time event at start time + #Check for events at start time if isinstance(self.model, FMUModelME1): event_info = self.model.get_event_info() if event_info.upcomingTimeEvent and event_info.nextEventTime == model.time: self.model.event_update() + elif isinstance(self.model, (FMUModelME2, CoupledFMUModelME2, FMUModelME3)): + if not self.options['initialize']: + self.model.enter_event_mode() + self.model.event_update() + self.model.enter_continuous_time_mode() + else: + event_info = self.model.get_event_info() + if event_info.nextEventTimeDefined and abs(event_info.nextEventTime - model.time) <= 1e-14: + self.model.enter_event_mode() + self.model.event_update() + self.model.enter_continuous_time_mode() if abs(start_time - model.time) > 1e-14: logging_module.warning('The simulation start time (%f) and the current time in the model (%f) is different. Is the simulation start time correctly set?'%(start_time, model.time)) diff --git a/src/pyfmi/test_util.pyx b/src/pyfmi/test_util.pyx index 6e5350bb..4300c89c 100644 --- a/src/pyfmi/test_util.pyx +++ b/src/pyfmi/test_util.pyx @@ -396,6 +396,9 @@ class Dummy_FMUModelME2(_ForTestingFMUModelME2): def event_update(self, *args, **kwargs): pass + def enter_event_mode(self, *args, **kwargs): + pass + def enter_continuous_time_mode(self, *args, **kwargs): pass diff --git a/tests/test_fmi2.py b/tests/test_fmi2.py index 1264bba2..ced22d02 100644 --- a/tests/test_fmi2.py +++ b/tests/test_fmi2.py @@ -1126,4 +1126,21 @@ def test_no_state_fmu_eval_failure_caught(fmu_path): fmu = load_fmu(fmu_path) expected_err = "The right-hand side function had repeated recoverable errors" with pytest.raises(CVodeError, match = re.escape(expected_err)): - fmu.simulate() \ No newline at end of file + fmu.simulate() + +def test_consecutive_simulation_with_initialize_false(): + """Test that consecutive simulate() calls with initialize=False work. + + The initialize=False path invokes enter_event_mode + event_update + + enter_continuous_time_mode before resuming integration. Regression + test to ensure this does not crash or corrupt the FMU state. + """ + fmu = load_fmu(REFERENCE_FMU_FMI2_PATH / "Feedthrough.fmu") + + fmu.set('Int32_input', 42) + fmu.simulate(0, 0.1, options={"ncp": 1}) + assert fmu.get('Int32_output')[0] == 42 + + # Continue without re-initializing — exercises the fix + fmu.simulate(0.1, 0.2, options={"ncp": 1, "initialize": False}) + assert fmu.get('Int32_output')[0] == 42 \ No newline at end of file diff --git a/tests/test_fmi3_sim.py b/tests/test_fmi3_sim.py index 8f0aa5c3..40edd516 100644 --- a/tests/test_fmi3_sim.py +++ b/tests/test_fmi3_sim.py @@ -353,3 +353,20 @@ def test_dynamic_diagnostics_no_time_per_step_should_not_set_cpu_time(self): res = model.simulate(options = opts) assert "@Diagnostics.cpu_time" not in res.keys() + + def test_consecutive_simulation_with_initialize_false(self): + """Test that consecutive simulate() calls with initialize=False work. + + The initialize=False path invokes enter_event_mode + event_update + + enter_continuous_time_mode before resuming integration. Regression + test to ensure this does not crash or corrupt the FMU state. + """ + fmu = load_fmu(FMI3_REF_FMU_PATH / "Feedthrough.fmu") + + fmu.set('Int32_input', 42) + fmu.simulate(0, 0.1, options={"ncp": 1}) + assert fmu.get('Int32_output')[0] == 42 + + # Continue without re-initializing — exercises the fix + fmu.simulate(0.1, 0.2, options={"ncp": 1, "initialize": False}) + assert fmu.get('Int32_output')[0] == 42