Skip to content

Commit

Permalink
Merge pull request #31 from ihmeuw/feature/initialize_simulation_from…
Browse files Browse the repository at this point in the history
…_model_spec

Made the simulation easily accessible before the setup lifecycle phase
  • Loading branch information
collijk committed Jun 18, 2018
2 parents d047248 + 2eb66ac commit b2f1f15
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 9 deletions.
2 changes: 2 additions & 0 deletions vivarium/interface/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .interactive import (build_simulation_configuration, setup_simulation, setup_simulation_from_model_specification,
initialize_simulation, initialize_simulation_from_model_specification)
42 changes: 33 additions & 9 deletions vivarium/interface/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@
from vivarium.framework.plugins import PluginManager
from vivarium.framework.engine import SimulationContext

from .utilities import run_from_ipython, log_progress
from .utilities import run_from_ipython, log_progress, raise_if_not_setup


class InteractiveContext(SimulationContext):

def __init__(self, configuration, components, plugin_manager=None):
super().__init__(configuration, components, plugin_manager)
self._initial_population = None
self._setup = False

def setup(self):
super().setup()
self._start_time = self.clock.time
self.initialize_simulants()
self._setup = True

def initialize_simulants(self):
super().initialize_simulants()
Expand All @@ -26,20 +29,25 @@ def reset(self):
self.population._population = self._initial_population
self.clock._time = self._start_time

@raise_if_not_setup(system_type='run')
def run(self, with_logging=True):
return self.run_until(self.clock.stop_time, with_logging=with_logging)

@raise_if_not_setup(system_type='run')
def run_for(self, duration, with_logging=True):
return self.run_until(self.clock.time + duration, with_logging=with_logging)

@raise_if_not_setup(system_type='run')
def run_until(self, end_time, with_logging=True):
if not isinstance(end_time, type(self.clock.time)):
raise ValueError(f"Provided time must be an instance of {type(self.clock.time)}")

iterations = int(ceil((end_time - self.clock.time)/self.clock.step_size))
self.take_steps(number_of_steps=iterations, with_logging=with_logging)
assert self.clock.time - self.clock.step_size < end_time <= self.clock.time
return iterations

@raise_if_not_setup(system_type='run')
def step(self, step_size=None): # TODO: consider renaming to take_step for similarity with sim.take_steps
old_step_size = self.clock.step_size
if step_size is not None:
Expand All @@ -49,6 +57,7 @@ def step(self, step_size=None): # TODO: consider renaming to take_step for simi
super().step()
self.clock._step_size = old_step_size

@raise_if_not_setup(system_type='run')
def take_steps(self, number_of_steps=1, step_size=None, with_logging=True):
if not isinstance(number_of_steps, int):
raise ValueError('Number of steps must be an integer.')
Expand All @@ -60,53 +69,64 @@ def take_steps(self, number_of_steps=1, step_size=None, with_logging=True):
for _ in range(number_of_steps):
self.step(step_size)

@raise_if_not_setup(system_type='value')
def list_values(self):
return list(self.values.keys())

@raise_if_not_setup(system_type='value')
def get_values(self):
return self.values.items()

@raise_if_not_setup(system_type='value')
def get_value(self, value_pipeline_name):
return self.values.get_value(value_pipeline_name)

@raise_if_not_setup(system_type='event')
def list_events(self):
return self.events.list_events()

@raise_if_not_setup(system_type='event')
def get_listeners(self, event_name):
if event_name not in self.events:
raise ValueError(f'No event {event_name} in system.')
return self.events.get_listeners(event_name)

@raise_if_not_setup(system_type='event')
def get_emitter(self, event_name):
if event_name not in self.events:
raise ValueError(f'No event {event_name} in system.')
return self.events.get_emitter(event_name)

@raise_if_not_setup(system_type='component')
def get_components(self):
return [component for component in
self.component_manager._components + self.component_manager._managers + self.component_manager._globals]
return [component for component in self.component_manager._components + self.component_manager._managers]

@raise_if_not_setup(system_type='component')
def reload_component(self, component):
raise NotImplementedError()

@raise_if_not_setup(system_type='component')
def replace_component(self, old_component, new_component):
self.component_manager._components.remove(old_component)
new_component.setup(self.builder)
self.component_manager.add_components([new_component])


def setup_simulation(components, input_config=None):
def initialize_simulation(components, input_config=None):
config = build_simulation_configuration()
config.update(input_config)

simulation = InteractiveContext(config, components)
return InteractiveContext(config, components)


def setup_simulation(components, input_config=None):
simulation = initialize_simulation(components, input_config)
simulation.setup()
simulation.initialize_simulants()

return simulation


def setup_simulation_from_model_specification(model_specification_file):
def initialize_simulation_from_model_specification(model_specification_file):
model_specification = build_model_specification(model_specification_file)

plugin_config = model_specification.plugins
Expand All @@ -117,7 +137,11 @@ def setup_simulation_from_model_specification(model_specification_file):
component_config_parser = plugin_manager.get_plugin('component_configuration_parser')
components = component_config_parser.get_components(component_config)

simulation = InteractiveContext(simulation_config, components, plugin_manager)
return InteractiveContext(simulation_config, components, plugin_manager)


def setup_simulation_from_model_specification(model_specification_file):
simulation = initialize_simulation_from_model_specification(model_specification_file)
simulation.setup()
simulation.initialize_simulants()

return simulation
29 changes: 29 additions & 0 deletions vivarium/interface/utilities.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import functools

from vivarium import VivariumError
from vivarium.framework.configuration import validate_model_specification_file


Expand Down Expand Up @@ -71,3 +74,29 @@ def log_progress(sequence, every=None, size=None, name='Items'):
name=name,
index=str(index or '?')
)


class InteractiveError(VivariumError):
"""Error raised when the Interactive context is in an inconsistent state."""
pass


def raise_if_not_setup(system_type):
type_error_map = {
'run': 'Simulation must be setup before it can be run',
'value': 'Value pipeline configuration is not complete until the simulation is setup.',
'event': 'Event configuration is not complete until the simulation is setup.',
'component': 'Component configuration is not complete until the simulation is setup.'
}
err_msg = type_error_map[system_type]

def method_wrapper(context_method):

@functools.wraps(context_method)
def wrapped_method(*args, **kwargs):
instance = args[0]
if not instance._setup:
raise InteractiveError(err_msg)
context_method(*args, **kwargs)

return method_wrapper

0 comments on commit b2f1f15

Please sign in to comment.