Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 30 additions & 15 deletions rocketpy/simulation/monte_carlo.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@ def simulate(
that reports a failure, and logs that do not hold every simulation
asked for are each refused, since a run that lost work must not be
reported as one that completed.
KeyboardInterrupt
If the run is interrupted. The logs written so far are kept and
reloaded first, so the object agrees with its own files and the
run can be continued with ``append=True``, but the interrupt then
reaches the caller rather than being reported as a finished study.

Notes
-----
Expand Down Expand Up @@ -349,12 +354,13 @@ def simulate(

self.__setup_files(append)

if parallel:
self.__run_in_parallel(n_workers)
else:
self.__run_in_serial()

self.__terminate_simulation()
try:
if parallel:
self.__run_in_parallel(n_workers)
else:
self.__run_in_serial()
finally:
self.__terminate_simulation()

def __setup_files(self, append):
"""
Expand Down Expand Up @@ -410,16 +416,22 @@ def _append_simulation_record(self, inputs_json, outputs_json):
previous_input_size = os.path.getsize(input_path)
except OSError:
previous_input_size = 0
try:
previous_output_size = os.path.getsize(output_path)
except OSError:
previous_output_size = 0

with open(input_path, "a", encoding="utf-8") as f:
f.write(inputs_json)

try:
with open(output_path, "a", encoding="utf-8") as f:
f.write(outputs_json)
except Exception:
except BaseException:
with open(input_path, "rb+") as f:
f.truncate(previous_input_size)
with open(output_path, "rb+") as f:
f.truncate(previous_output_size)
raise

def __run_in_serial(self):
Expand All @@ -435,6 +447,7 @@ def __run_in_serial(self):
n_simulations=self.number_of_simulations,
start_time=time(),
)
inputs_json = ""
try:
while sim_monitor.keep_simulating():
sim_monitor.increment()
Expand All @@ -445,15 +458,18 @@ def __run_in_serial(self):
outputs_json = self.__evaluate_flight_outputs(flight, sim_monitor.count)

self._append_simulation_record(inputs_json, outputs_json)
inputs_json = ""

sim_monitor.print_update_status()

sim_monitor.print_final_status()

except KeyboardInterrupt:
print("Keyboard interrupt received. Files saved.")
with open(self._error_file, "a", encoding="utf-8") as f:
f.write(inputs_json)
if inputs_json:
with open(self._error_file, "a", encoding="utf-8") as f:
f.write(inputs_json)
raise

except Exception as error:
print(f"Error on iteration {sim_monitor.count}: {error}")
Expand Down Expand Up @@ -530,16 +546,15 @@ def __run_in_parallel(self, n_workers=None):

sim_monitor.print_final_status()

# Handle error from the main process
# pylint: disable=broad-except
except (Exception, KeyboardInterrupt) as error:
# Handle error from the main process. Re-raising unconditionally
# is what makes an interrupted run tell the caller it was cut
# short instead of reporting itself as a finished study.
except (Exception, KeyboardInterrupt):
# Bounded here too. An unbounded join undid the bound above.
_stop_the_workers_still_running(
processes, simulation_error_event, _SHUTDOWN_GRACE_SECONDS
)

if not isinstance(error, KeyboardInterrupt):
raise error
raise

def __validate_number_of_workers(self, n_workers):
if n_workers is None or n_workers > os.cpu_count():
Expand Down
Loading
Loading