8000 Fixed bug with cumulative CPU time (#291) · modelon-community/PyFMI@5d28a4e · GitHub
[go: up one dir, main page]

Skip to content

Commit

Permalink
Fixed bug with cumulative CPU time (#291)
Browse files Browse the repository at this point in the history
* Fixed bug with cumulative CPU time

* Added name for result file name

* Fixed typo in variable name

* Added verification of get_variables_data
  • Loading branch information
modelonrobinandersson authored Feb 4, 2025
1 parent ed2ea58 commit 5d28a4e
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 28 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* Changed default log and file size limitations from 2 GiB (2*1024**3) to 2 GB (2e9).
Updated the corresponding error messages to correctly reference sizes in GB instead of GiB.
* Clarified that the `filter` option only applies to model variables.
* Fixed a bug introduced in PyFMI 2.15 where the diagnostics trajectory for CPU time was
returned as CPU time per step instead of cumulative CPU time.

--- PyFMI-2.16.2 ---
* Increased robustness of getting the `supports` attribute from ResultHandlers.
Expand Down
10 changes: 5 additions & 5 deletions src/common/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def is_negated(self, var):
class ResultHandler:
def __init__(self, model = None):
self.model = model
# Dictionary of support capabilities
# Dictionary of support capabilities
# should be UPDATED, not REPLACED, for stating supported capabilities
self.supports = {"dynamic_diagnostics": False,
"result_max_size": False}
Expand Down Expand Up @@ -1545,7 +1545,7 @@ def _get_variable_data_as_trajectory(self,
name == f'{DIAGNOSTICS_PREFIX}cpu_time'
):
return Trajectory(
time, self.get_variable_data(f'{DIAGNOSTICS_PREFIX}cpu_time_per_step').x[start_index:stop_index])
time, np.cumsum(self.get_variable_data(f'{DIAGNOSTICS_PREFIX}cpu_time_per_step').x[start_index:stop_index]))

factor, data_index, data_mat = self._map_index_to_data_properties(name)

Expand Down Expand Up @@ -2885,18 +2885,18 @@ def get_result(self):

def verify_result_size(file_name, first_point, current_size, previous_size, max_size, ncp, time):
free_space = get_available_disk_space(file_name)

if first_point:
point_size = current_size - previous_size
estimate = ncp*point_size + previous_size

msg = ""
if estimate > max_size:
msg = msg + "The result is estimated to exceed the allowed maximum size (limit: %g GB, estimate: %g GB). "%(max_size/GB_FACTOR, estimate/GB_FACTOR)

if estimate > free_space:
msg = msg + "The result is estimated to exceed the available disk space (available: %g GB, estimate: %g GB). "%(free_space/GB_FACTOR, estimate/GB_FACTOR)

if msg != "":
if ncp > NCP_LARGE:
msg = msg + "The number of result points is large (%d), consider reducing the number of points. "%ncp
Expand Down
73 changes: 50 additions & 23 deletions tests/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ def test_get_description(self):
res = ResultDymolaBinary('CoupledClutches_result.mat')

assert res.description[res.get_variable_index("J1.phi")] == "Absolute rotation angle of component"

def test_modified_result_file_data_diagnostics(self):
"""Verify that computed diagnostics can be retrieved from an updated result file"""
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)
Expand Down Expand Up @@ -660,7 +660,7 @@ def test_modified_result_file_data_diagnostics(self):
result_writer.simulation_end()

assert len(res.get_variable_data("@Diagnostics.state_errors.clutch2.w_rel").x) == 4, res.get_variable_data("@Diagnostics.state_errors.clutch2.w_rel").x

def test_modified_result_file_data_diagnostics_steps(self):
"""Verify that diagnostics can be retrieved from an updated result file"""
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)
Expand Down Expand Up @@ -711,7 +711,7 @@ def test_modified_result_file_data_diagnostics_steps(self):
result_writer.simulation_end()

assert len(res.get_variable_data("@Diagnostics.nbr_steps").x) == 4, res.get_variable_data("@Diagnostics.nbr_steps").x

def test_modified_result_file_data_2(self):
"""Verify that continuous trajectories are updated when retrieved from a result file"""
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)
Expand All @@ -733,7 +733,7 @@ def test_modified_result_file_data_2(self):
result_writer.simulation_end()

assert len(res.get_variable_data("J1.phi").x) == 2, res.get_variable_data("J1.phi").x

def test_modified_result_file_data_2_different(self):
"""Verify that (different) continuous trajectories are updated when retrieved from a result file"""
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)
Expand All @@ -755,7 +755,7 @@ def test_modified_result_file_data_2_different(self):
result_writer.simulation_end()

assert len(res.get_variable_data("J2.phi").x) == 2, res.get_variable_data("J2.phi").x

def test_modified_result_file_data_1(self):
"""Verify that (different) constants/parameters can be retrieved from an updated result file"""
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)
Expand All @@ -779,7 +779,7 @@ def test_modified_result_file_data_1(self):

#Assert that no exception is raised
res.get_variable_data("J2.J")

def test_modified_result_file_data_1_delayed(self):
"""Verify that constants/parameters can be retrieved from an updated result file"""
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)
Expand All @@ -799,7 +799,7 @@ def test_modified_result_file_data_1_delayed(self):

#Assert that no exception is raised
res.get_variable_data("J2.J")

def test_modified_result_file_time(self):
"""Verify that 'time' can be retrieved from an updated result file"""
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)
Expand Down Expand Up @@ -1945,7 +1945,7 @@ def test_mixes_get_variable_s_data(self):

vars = ["time"]
start_index, stop_index = 0, 5

partial_1, _ = rdb.get_variables_data(vars, start_index, stop_index)
full_traj = rdb.get_variable_data(vars[0])
partial_2, _ = rdb.get_variables_data(vars, start_index, stop_index)
Expand All @@ -1954,14 +1954,41 @@ def test_mixes_get_variable_s_data(self):
assert len(full_traj.x) == (ncp + 1)
assert len(partial_2[vars[0]].x) == (stop_index - start_index)

def test_cpu_time(self):
""" Verify the cumulative CPU time trajectory is never decreasing. """
fmu = Dummy_FMUModelME2(
[],
os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "bouncingBall.fmu"), _connect_dll=False
)
opts = fmu.simulate_options()
opts['dynamic_diagnostics'] = True
opts["result_file_name"] = "TestCPUTime.mat"
res = fmu.simulate(options = opts)

rdb = ResultDymolaBinary(opts["result_file_name"])
cpu_time = rdb.get_variable_data(f"{DIAGNOSTICS_PREFIX}cpu_time").x
cpu_time_2, _ = rdb.get_variables_data([f"{DIAGNOSTICS_PREFIX}cpu_time"])
cpu_time_2 = cpu_time_2[f"{DIAGNOSTICS_PREFIX}cpu_time"].x
first_value = -1 # initialize to any negative value since the first cpu_time value is 0.0

# Test that the data is never decreasing (since we return it using numpy cumulative sum)
for value in cpu_time:
assert value >= first_value
first_value = value

first_value = -1
for value in cpu_time_2:
assert value >= first_value
first_value = value

@pytest.mark.assimulo
class TestFileSizeLimit:
def _setup(self, result_type, result_file_name="", fmi_type="me"):
if fmi_type == "me":
model = Dummy_FMUModelME2([], os.path.join(file_path, "files", "FMUs", "XML", "ME2.0", "CoupledClutches.fmu"), _connect_dll=False)
else:
model = Dummy_FMUModelCS2([], os.path.join(file_path, "files", "FMUs", "XML", "CS2.0", "CoupledClutches.fmu"), _connect_dll=False)

opts = model.simulate_options()
opts["result_handling"] = result_type
opts["result_file_name"] = result_file_name
Expand Down Expand Up @@ -2025,7 +2052,7 @@ def _test_result_size_verification(self, result_type, result_file_name="", dynam

assert file_size > max_size*0.9 and file_size < max_size*1.1, \
"The file size is not within 10% of the given max size"

def _test_result_size_early_abort(self, result_type, result_file_name=""):
"""
Verifies that the ResultSizeError is triggered and also verifies that the cause of the error being
Expand Down Expand Up @@ -2067,7 +2094,7 @@ def _test_result_size_early_abort(self, result_type, result_file_name=""):

assert file_size < max_size*0.1, \
"The file size is not small, no early abort"

# TODO: Pytest parametrization
"""
Binary
Expand All @@ -2077,19 +2104,19 @@ def test_binary_file_size_verification_diagnostics(self):
Make sure that the diagnostics variables are also taken into account.
"""
self._test_result_size_verification("binary", dynamic_diagnostics=True)

def test_binary_file_size_verification(self):
self._test_result_size_verification("binary")

def test_binary_file_size_early_abort(self):
self._test_result_size_early_abort("binary")

def test_small_size_binary_file(self):
self._test_result_exception("binary")

def test_small_size_binary_file_cs(self):
self._test_result_exception("binary", fmi_type="cs")

def test_small_size_binary_file_stream(self):
self._test_result_exception("binary", BytesIO())

Expand All @@ -2104,13 +2131,13 @@ def test_large_size_binary_file_stream(self):
"""
def test_text_file_size_verification(self):
self._test_result_size_verification("file")

def test_text_file_size_early_abort(self):
self._test_result_size_early_abort("file")

def test_small_size_text_file(self):
self._test_result_exception("file")

def test_small_size_text_file_stream(self):
self._test_result_exception("file", StringIO())

Expand All @@ -2125,13 +2152,13 @@ def test_large_size_text_file_stream(self):
"""
def test_csv_file_size_verification(self):
self._test_result_size_verification("csv")

def test_csv_file_size_early_abort(self):
self._test_result_size_early_abort("csv")

def test_small_size_csv_file(self):
self._test_result_exception("csv")

def test_small_size_csv_file_stream(self):
self._test_result_exception("csv", StringIO())

Expand All @@ -2146,10 +2173,10 @@ def test_large_size_csv_file_stream(self):
"""
def test_small_size_memory(self):
self._test_result_exception("memory")

def test_memory_size_early_abort(self):
self._test_result_size_early_abort("memory")

def test_small_size_memory_stream(self):
self._test_result_exception("memory", StringIO())

Expand All @@ -2171,7 +2198,7 @@ class TestCustomResultHandlerMissingSupport:
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)
Expand All @@ -2187,7 +2214,7 @@ def test_limit_result_size(self, caplog):
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)
Expand Down

0 comments on commit 5d28a4e

Please sign in to comment.
0