Skip to content

Commit

Permalink
Fix simulation time precision
Browse files Browse the repository at this point in the history
  • Loading branch information
lccasagrande committed Aug 31, 2020
1 parent 329974c commit 529ec35
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 52 deletions.
2 changes: 1 addition & 1 deletion batsim_py/__version__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__all__ = ['__version__']

__version__ = "1.0.4"
__version__ = "1.0.5"
44 changes: 16 additions & 28 deletions batsim_py/simulator.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import atexit
from collections import defaultdict
import decimal
import math
from shutil import which
import signal
import subprocess
import sys
import tempfile
from typing import Dict
from typing import Callable, Union
from typing import Union
from typing import Sequence
from typing import List
from typing import DefaultDict
Expand Down Expand Up @@ -88,7 +89,6 @@ class SimulatorHandler:
Examples:
>>> handler = SimulatorHandler("tcp://localhost:28000")
"""
__TIME_PRECISION = 0.001

def __init__(self, tcp_address: Optional[str] = None) -> None:
if which('batsim') is None:
Expand Down Expand Up @@ -171,14 +171,9 @@ def is_running(self) -> bool:
return self.__simulator is not None

@property
def current_time(self) -> float:
""" The current simulation time. """
t = self.__current_time

# Truncate:
exp = decimal.Decimal(str(self.__TIME_PRECISION))
t = float(decimal.Decimal(t).quantize(exp, decimal.ROUND_DOWN))
return t
def current_time(self) -> int:
""" The current simulation time (seconds). """
return math.floor(self.__current_time)

@property
def is_submitter_finished(self) -> bool:
Expand Down Expand Up @@ -286,11 +281,11 @@ def close(self) -> None:
self.__callbacks.clear()
self.__dispatch_event(SimulatorEvent.SIMULATION_ENDS, self)

def proceed_time(self, time: float = 0) -> None:
def proceed_time(self, time: int = 0) -> None:
""" Proceed the simulation process to the next event or time.
Args:
time: The time to proceed (min is 0.001 sec). Defaults to 0. It's
time: The time to proceed (min is 1 sec). Defaults to 0. It's
possible to proceed directly to the next event or to a specific
time. If the time is unset (equals 0), the simulation will proceed
to the next event. Otherwise, if a time 't' is provided, the
Expand All @@ -312,10 +307,6 @@ def unflag(_):
if not self.is_running:
raise RuntimeError("The simulation is not running.")

if time < 0:
raise ValueError('Expected `time` argument to be a number '
f'greater than zero, got {time}.')

if not time:
# Go to the next event.
self.__wait_callback = False
Expand All @@ -328,10 +319,8 @@ def unflag(_):
self.set_callback(time + self.current_time, unflag)

self.__goto_next_batsim_event()
self.__start_runnable_jobs()
while self.is_running and self.__wait_callback:
self.__goto_next_batsim_event()
self.__start_runnable_jobs()

@overload
def subscribe(self, event: JobEvent, call: JobListener) -> None: ...
Expand All @@ -357,13 +346,13 @@ def subscribe(self, event: Event, call: Listener) -> None:
assert callable(call)
self.__subscriptions[event].append(call)

def set_callback(self, at: float, call: Callable[[float], None]) -> None:
def set_callback(self, at: int, call: Callable[[float], None]) -> None:
""" Setup a callback.
The simulation will call the function at the defined time.
Args:
at: The time which the function must be called (min is 0.001 sec).
at: The time which the function must be called (min is 1 sec).
call: A function that receives the current simulation time as
an argument.
Expand All @@ -376,10 +365,6 @@ def set_callback(self, at: float, call: Callable[[float], None]) -> None:
if not self.is_running:
raise RuntimeError("The simulation is not running.")

# Truncate:
exp = decimal.Decimal(str(self.__TIME_PRECISION))
at = float(decimal.Decimal(at).quantize(exp, decimal.ROUND_DOWN))

if at <= self.current_time:
raise ValueError('Expected `at` argument to be a number '
'greater than the current simulation time'
Expand Down Expand Up @@ -637,6 +622,8 @@ def __goto_next_batsim_event(self) -> None:
self.__handle_batsim_events()
if self.__simulation_time and self.current_time >= self.__simulation_time:
self.close()
else:
self.__start_runnable_jobs()

def __close_simulator(self) -> None:
""" Close the simulator process. """
Expand All @@ -647,6 +634,7 @@ def __close_simulator(self) -> None:

def __set_batsim_call_me_later(self, at: float) -> None:
""" Setup a call me later request. """
at += 0.09 # Last batsim priority
request = CallMeLaterBatsimRequest(self.current_time, at)
if not any(isinstance(r, CallMeLaterBatsimRequest) and r.at == request.at for r in self.__batsim_requests):
self.__batsim_requests.append(request)
Expand Down Expand Up @@ -732,10 +720,10 @@ def __on_batsim_host_state_changed(self, event: ResourcePowerStateChangedBatsimE

def __on_batsim_requested_call(self, _) -> None:
""" Handle batsim answer to call me back request. """
if self.current_time in self.__callbacks:
for callback in self.__callbacks[self.current_time]:
callback(self.current_time)
del self.__callbacks[self.current_time]
for t in list(self.__callbacks.keys()):
if t <= self.__current_time: # batsim time
for call in self.__callbacks.pop(t):
call(self.current_time) # local time

def __on_batsim_job_submitted(self, event: JobSubmittedBatsimEvent) -> None:
""" Handle batsim job submitted event. """
Expand Down
26 changes: 3 additions & 23 deletions tests/test_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def test_current_time_must_truncate(self, mocker):
msg = BatsimMessage(10.00199, [NotifyBatsimEvent(10.00199, e['data'])])
mocker.patch.object(protocol.NetworkHandler, 'recv', return_value=msg)
s.proceed_time()
assert s.current_time == 10.001
assert s.current_time == 10

def test_batsim_not_found_must_raise(self, mocker):
mocker.patch("batsim_py.simulator.which", return_value=None)
Expand Down Expand Up @@ -171,7 +171,7 @@ def test_start_with_simulation_time_must_setup_call_request(self, mocker):
s = SimulatorHandler()
s.start("p", "w", simulation_time=100)
batsim_py.simulator.CallMeLaterBatsimRequest.assert_called_once_with( # type: ignore
0, 100)
0, 100+0.09)

def test_start_must_dispatch_event(self):
def foo(h: SimulatorHandler): self.__called = True
Expand Down Expand Up @@ -384,26 +384,6 @@ def foo(p): pass

assert "running" in str(excinfo.value)

def test_callback_must_truncate_time(self, mocker):
def x(a): return

mocker.patch("batsim_py.simulator.CallMeLaterBatsimRequest")

s = SimulatorHandler()
s.start("p", "w")
s.set_callback(0.001999, x)
simulator.CallMeLaterBatsimRequest.assert_called_once_with( # type: ignore
0, 0.001)

def test_callback_must_truncate_time_and_raise(self, mocker):
def x(a): return
s = SimulatorHandler()
s.start("p", "w")
with pytest.raises(ValueError) as excinfo:
s.set_callback(0.000999, x)

assert "at" in str(excinfo.value)

def test_callback_invalid_time_must_raise(self, mocker):
def foo(p): pass
s = SimulatorHandler()
Expand All @@ -426,7 +406,7 @@ def foo(p): pass
s.start("p", "w")
s.set_callback(50, foo)
simulator.CallMeLaterBatsimRequest.assert_called_once_with( # type: ignore
0, 50)
0, 50+0.09)

def test_queue(self, mocker):
s = SimulatorHandler()
Expand Down

0 comments on commit 529ec35

Please sign in to comment.