diff --git a/force_bdss/api.py b/force_bdss/api.py index 828570458e60ba2d9b117fc32f9279b2ec28696d..fa2fddd8cf2847e007b6e89f4c410f7011edc862 100644 --- a/force_bdss/api.py +++ b/force_bdss/api.py @@ -21,7 +21,7 @@ from .mco.i_mco_factory import IMCOFactory # noqa from .mco.parameters.base_mco_parameter_factory import BaseMCOParameterFactory # noqa from .mco.parameters.base_mco_parameter import BaseMCOParameter # noqa -from .mco.events import * # noqa +from .core_driver_events import * # noqa from .notification_listeners.i_notification_listener_factory import INotificationListenerFactory # noqa from .notification_listeners.base_notification_listener import BaseNotificationListener # noqa diff --git a/force_bdss/core_driver_events.py b/force_bdss/core_driver_events.py new file mode 100644 index 0000000000000000000000000000000000000000..d7b10c0026abc89c296769d93219923e40a140c8 --- /dev/null +++ b/force_bdss/core_driver_events.py @@ -0,0 +1,23 @@ +from traits.api import HasStrictTraits, Tuple + + +class BaseDriverEvent(HasStrictTraits): + """ Base event for the MCO driver.""" + + +class MCOStartEvent(BaseDriverEvent): + """ The MCO driver should emit this event when the evaluation starts.""" + input_names = Tuple() + output_names = Tuple() + + +class MCOFinishEvent(BaseDriverEvent): + """ The MCO driver should emit this event when the evaluation ends.""" + + +class MCOProgressEvent(BaseDriverEvent): + """ The MCO driver should emit this event for every new evaluation that has + been completed. It carries data about the evaluation, specifically the + input data (MCO parameter values) and the resulting output (KPIs).""" + input = Tuple() + output = Tuple() diff --git a/force_bdss/core_mco_driver.py b/force_bdss/core_mco_driver.py index e2678daee1837df4cf8595ee4618008a988c47d3..fb4171427dadcf58e98b7342fef214b6fd375df4 100644 --- a/force_bdss/core_mco_driver.py +++ b/force_bdss/core_mco_driver.py @@ -14,6 +14,7 @@ from .io.workflow_reader import ( InvalidVersionException, InvalidFileException ) +from .core_driver_events import MCOStartEvent, MCOFinishEvent, MCOProgressEvent log = logging.getLogger(__name__) CORE_MCO_DRIVER_ID = plugin_id("core", "CoreMCODriver") @@ -50,8 +51,27 @@ class CoreMCODriver(BaseCoreDriver): mco_factory = mco_model.factory return mco_factory.create_optimizer() - @on_trait_change("mco:event") - def _handle_mco_event(self, event): + @on_trait_change("mco:started") + def _deliver_start_event(self): + output_names = [] + for kpi in self.workflow.kpi_calculators: + output_names.extend(kpi.output_slot_names) + + self._deliver_event(MCOStartEvent( + input_names=tuple(p.name for p in self.workflow.mco.parameters), + output_names=tuple(output_names) + )) + + @on_trait_change("mco:finished") + def _deliver_finished_event(self): + self._deliver_event(MCOFinishEvent()) + + @on_trait_change("mco:new_data") + def _deliver_mco_progress_event(self, data): + self._deliver_event(MCOProgressEvent(**data)) + + def _deliver_event(self, event): + """ Delivers an event to the listeners """ for listener in self.listeners[:]: try: listener.deliver(event) diff --git a/force_bdss/core_plugins/dummy/dummy_dakota/dakota_optimizer.py b/force_bdss/core_plugins/dummy/dummy_dakota/dakota_optimizer.py index 9f9de5d87192fa818b1bd4e9ae49a510b3489827..96d5fe70a617c0401f72df5e62555c0d602cd7f6 100644 --- a/force_bdss/core_plugins/dummy/dummy_dakota/dakota_optimizer.py +++ b/force_bdss/core_plugins/dummy/dummy_dakota/dakota_optimizer.py @@ -4,8 +4,6 @@ import itertools import collections from force_bdss.api import BaseMCO -from force_bdss.mco.events import MCOStartEvent, MCOFinishEvent, \ - MCOProgressEvent def rotated_range(start, stop, starting_value): @@ -18,8 +16,6 @@ def rotated_range(start, stop, starting_value): class DummyDakotaOptimizer(BaseMCO): def run(self, model): - self.notify_event(MCOStartEvent()) - parameters = model.parameters values = [] @@ -34,6 +30,7 @@ class DummyDakotaOptimizer(BaseMCO): application = self.factory.plugin.application + self.started = True for value in value_iterator: ps = subprocess.Popen( [sys.argv[0], @@ -45,9 +42,9 @@ class DummyDakotaOptimizer(BaseMCO): out = ps.communicate( " ".join([str(v) for v in value]).encode("utf-8")) out_data = out[0].decode("utf-8").split() - self.notify_event(MCOProgressEvent( - input=tuple(value), - output=tuple(out_data), - )) + self.new_data = { + 'input': tuple(value), + 'output': tuple(out_data) + } - self.notify_event(MCOFinishEvent()) + self.finished = True diff --git a/force_bdss/mco/base_mco.py b/force_bdss/mco/base_mco.py index ae9d89b72092c962ebc7f6415d3fa8166613711e..91918c6bf2fd262eed4eecab8d5c42c213c5c5bc 100644 --- a/force_bdss/mco/base_mco.py +++ b/force_bdss/mco/base_mco.py @@ -1,8 +1,7 @@ import abc -from traits.api import ABCHasStrictTraits, Instance, Event +from traits.api import ABCHasStrictTraits, Instance, Event, Dict, Str, Tuple -from force_bdss.mco.events import BaseMCOEvent from .i_mco_factory import IMCOFactory @@ -14,8 +13,14 @@ class BaseMCO(ABCHasStrictTraits): #: A reference to the factory factory = Instance(IMCOFactory) - #: Triggered when an event occurs. - event = Event(BaseMCOEvent) + #: Triggered when the evaluation started. + started = Event() + + #: Triggered when the evaluation finished + finished = Event() + + # Event triggered when the mco wants to send new data to listeners + new_data = Event(Dict(Str(), Tuple())) def __init__(self, factory, *args, **kwargs): """Initializes the MCO. @@ -39,18 +44,3 @@ class BaseMCO(ABCHasStrictTraits): An instance of the model information, as created from create_model() """ - - def notify_event(self, event): - """Method based interface to deliver an event, instead of - assignment to traits. - - Sends the event, synchronously. When the routine returns, - listeners have been fully informed (they might, however, handle - the event asynchronously at their convenience) - - Parameters - ---------- - event: BaseMCOEvent - The event to deliver. - """ - self.event = event diff --git a/force_bdss/mco/events.py b/force_bdss/mco/events.py deleted file mode 100644 index 216e1effa30d7b1e81e0a8f82312310bb3fae583..0000000000000000000000000000000000000000 --- a/force_bdss/mco/events.py +++ /dev/null @@ -1,21 +0,0 @@ -from traits.api import HasStrictTraits, Tuple - - -class BaseMCOEvent(HasStrictTraits): - """Base event for the MCO""" - - -class MCOStartEvent(BaseMCOEvent): - """MCO should emit this event when the evaluation starts.""" - - -class MCOFinishEvent(BaseMCOEvent): - """MCO should emit this event when the evaluation ends.""" - - -class MCOProgressEvent(BaseMCOEvent): - """MCO should emit this event for every new evaluation that has been - completed. It carries data about the evaluation, specifically the - input data (MCO parameter values) and the resulting output (KPIs).""" - input = Tuple() - output = Tuple() diff --git a/force_bdss/tests/test_core_mco_driver.py b/force_bdss/tests/test_core_mco_driver.py index 688f7944604416e69ec586053d44d63005e7f807..3a4b33f2d9f15205bf9ebb8debafe85ec088c534 100644 --- a/force_bdss/tests/test_core_mco_driver.py +++ b/force_bdss/tests/test_core_mco_driver.py @@ -1,7 +1,8 @@ import unittest from testfixtures import LogCapture -from force_bdss.mco.events import MCOStartEvent +from force_bdss.core_driver_events import ( + MCOStartEvent, MCOFinishEvent, MCOProgressEvent) from force_bdss.notification_listeners.base_notification_listener import \ BaseNotificationListener from force_bdss.tests import fixtures @@ -47,16 +48,37 @@ class TestCoreMCODriver(unittest.TestCase): ) self.assertEqual(len(driver.listeners), 1) - def test_event_handling(self): + def test_start_event_handling(self): driver = CoreMCODriver( application=self.mock_application, ) listener = driver.listeners[0] mock_deliver = mock.Mock() listener.__dict__["deliver"] = mock_deliver - event = MCOStartEvent() - driver.mco.event = event - self.assertTrue(mock_deliver.call_args[0][0], event) + driver.mco.started = True + self.assertIsInstance(mock_deliver.call_args[0][0], MCOStartEvent) + + def test_finished_event_handling(self): + driver = CoreMCODriver( + application=self.mock_application, + ) + listener = driver.listeners[0] + mock_deliver = mock.Mock() + listener.__dict__["deliver"] = mock_deliver + driver.mco.finished = True + self.assertIsInstance(mock_deliver.call_args[0][0], MCOFinishEvent) + + def test_progress_event_handling(self): + driver = CoreMCODriver( + application=self.mock_application, + ) + listener = driver.listeners[0] + mock_deliver = mock.Mock() + listener.__dict__["deliver"] = mock_deliver + driver.mco.new_data = {'input': (1, 2), 'output': (3, 4)} + self.assertIsInstance(mock_deliver.call_args[0][0], MCOProgressEvent) + self.assertEqual(mock_deliver.call_args[0][0].input, (1, 2)) + self.assertEqual(mock_deliver.call_args[0][0].output, (3, 4)) def test_listener_init_exception(self): driver = CoreMCODriver( @@ -90,7 +112,7 @@ class TestCoreMCODriver(unittest.TestCase): listener.__dict__["deliver"] = mock_deliver mock_deliver.side_effect = Exception() with LogCapture() as capture: - driver.mco.event = MCOStartEvent() + driver.mco.started = True self.assertTrue(mock_deliver.called) capture.check(