class TestTelescopeIngest(unittest.TestCase): def setUp(self) -> None: self.env = simpy.Environment() self.cluster = Cluster(env=self.env, spec=CLUSTER_CONFIG) self.buffer = Buffer(env=self.env, cluster=self.cluster, config=BUFFER_CONFIG) self.scheduler = Scheduler(env=self.env, buffer=self.buffer, cluster=self.cluster, algorithm=None) self.planner = Planner(self.env, 'heft', self.cluster) def testIngest(self): telescope = Telescope(env=self.env, config=OBSERVATION_CONFIG, planner=self.planner, scheduler=self.scheduler) self.assertEqual(0, telescope.telescope_use) self.env.process(telescope.run()) self.scheduler.init() self.env.process(self.scheduler.run()) self.env.run(until=1) self.assertEqual(36, telescope.telescope_use) self.assertEqual(5, len(self.cluster.available_resources)) # After 1 timestep, data in the HotBuffer should be 2 self.assertEqual(496, self.buffer.hot.current_capacity) self.env.run(until=10) self.assertEqual(460, self.buffer.hot.current_capacity) self.env.run(until=12) self.assertEqual(0, telescope.telescope_use) self.assertEqual(10, len(self.cluster.available_resources)) self.assertEqual(5, len(self.cluster.finished_tasks)) self.assertEqual(1, len(self.buffer.waiting_observation_list))
class Simulation(object): """ The Simulation class is a wrapper for all Actors; we start the simulation through the simulation class, which in turn invokes the initial Actors and monitoring, and provides the conditions for checking if the simulation has finished. Parameters ---------- env : simpy.Environment bject This is how the TOpSim simulation maintains state across the different actors, and interfaces with the simpy processes. telescope_config: str This is a path to the telescope config that follows the TOpSim config specification (JSON). This file will be parsed in the Telescope class constructure cluster_config: str Path to the HPC cluster config that forms the computing component of the SDP buffer_config: str Path to the buffer configuration planning_algorithm: Object instance of the planning algorithm class interface as defined in algorithms.examples/ scheduling_algorithm: object instance of the core.algorithm interface event_file: str Path to the output file that stores execution of simulation. visualisation: bool If visualisation is required, True; else, False Methods ------- Raises ------ """ def __init__( self, env, telescope_config, cluster_config, buffer_config, planning_algorithm, scheduling_algorithm, event_file, visualisation=False ): self.env = env # Event file setup self.event_file = event_file self.visualisation = visualisation if event_file is not None: self.monitor = Monitor(self) if visualisation: self.visualiser = Visualiser(self) # Process necessary config files # Initiaise Actor and Resource objects self.cluster = Cluster(env, cluster_config) self.buffer = Buffer(env, self.cluster, config=buffer_config) self.planner = Planner(env, planning_algorithm, cluster_config) self.scheduler = Scheduler( env, self.buffer, self.cluster, scheduling_algorithm ) self.telescope = Telescope( env=self.env, config=telescope_config, planner=self.planner, scheduler=self.scheduler ) def start(self, runtime=150): """ Run the simulation, either for the specified runtime, OR until the exit condition is reached: * There are no more observations to process, * There is nothing left in the Buffer * The Scheduler is not waiting to allocate machines to resources * There are not tasks still running on the cluster. Parameters ---------- runtime : int Nominiated runtime of the simulation. If the simulation length is known, pass that as the argument. If not, passing in a negative value (typically, just -1) will run the simulation until the exit condition is reached. Returns ------- """ if self.event_file is not None: self.env.process(self.monitor.run()) if self.visualisation: self.env.process(self.visualiser.run()) self.env.process(self.telescope.run()) self.env.process(self.cluster.run()) self.scheduler.init() self.env.process(self.scheduler.run()) # Calling env.run() invokes the processes passed in init_process() if runtime > 0: self.env.run(until=runtime) else: if not self.is_finished(): self.env.run() logger.info("Simulation Finished @ %s", self.env.now) def is_finished(self): status = ( len(self.telescope.observations) == 0 and self.buffer.observations_for_processing.empty() and len(self.scheduler.waiting_observations) == 0 and len(self.cluster.running_tasks) == 0 ) if status: # Using compound 'or' doesn't give us a True/False return True else: return False