From eb526bd0c81c3cf760e309f47963ca80a7a1e5ce Mon Sep 17 00:00:00 2001 From: "Ankur Sinha (Ankur Sinha Gmail)" Date: Tue, 20 Aug 2024 12:33:58 +0100 Subject: [PATCH] wip: simulation_manager Needs debugging of pylems: https://github.com/LEMS/pylems/issues/86 TODO: - allow choosing simulation engine - save simulation date - allow saving data over multiple simulations --- neuroml_widgets/simulation.py | 83 +++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/neuroml_widgets/simulation.py b/neuroml_widgets/simulation.py index cfe73aa..a459e23 100644 --- a/neuroml_widgets/simulation.py +++ b/neuroml_widgets/simulation.py @@ -7,9 +7,17 @@ """ import logging +import typing +from datetime import datetime +from pathlib import Path import ipywidgets +from IPython.display import display from pyneuroml.io import read_lems_file +from pyneuroml.runners import run_lems_with +from pyneuroml.utils.units import split_nml2_quantity + +from .debug import debug_view as sim_debug_view logger = logging.getLogger(__name__) logger.setLevel(logging.ERROR) @@ -17,6 +25,10 @@ logger.debug(f"Logger setup for {__name__}") +sim_widget_states: typing.Dict[str, typing.Tuple[str, str]] = {} +simdata = None + + def simulation_manager(simfile: str): """Simulation manager widget. @@ -27,6 +39,7 @@ def simulation_manager(simfile: str): :type simfile: str """ model = read_lems_file(simfile) + print(model.toxml()) simdict = {} for am in model.components: @@ -34,6 +47,70 @@ def simulation_manager(simfile: str): simdict = am.parameters logger.debug(f"Simdict is {simdict}") - # TODO: create widget and run button etc.print(simdict) - # TODO: is this the best way, or should I be reading the file as XML so - # that we don't lose the other bits? + print(f"Simdict is {simdict}") + + @sim_debug_view.capture(clear_output=True) + def update_object(change): + """Change handler""" + # get units from description + owner_widget = change["owner"] + owner_widget_description = owner_widget.description + init_magnitude = float(sim_widget_states[owner_widget_description][0]) + init_units = sim_widget_states[owner_widget_description][1] + + new_magnitude = float(change["new"]) + new_value = f"{new_magnitude} {init_units}" + # set new values + print(f"Setting new values: {owner_widget_description}: {new_value}") + simdict[owner_widget_description] = new_value + + # TODO: indicate change, does not currently work + try: + if str(new_magnitude) != str(init_magnitude): + owner_widget.style.background = "yellow" + except KeyError: + pass + + sim_widgets = [] + for at in ["step", "length", "seed"]: + value, units = split_nml2_quantity(simdict[at]) + widget = ipywidgets.FloatText( + value=value, + description=at, + disabled=False, + style={"description_width": "initial"}, + ) + widget.observe(update_object, names="value") + box_widget = ipywidgets.HBox([widget, ipywidgets.Label(units)]) + sim_widgets.append(box_widget) + + # track initial value for changing widget slide when + # it's changed, and to reset + if at not in sim_widget_states: + sim_widget_states[at] = (value, units) + + @sim_debug_view.capture(clear_output=True) + def save_and_simulate(button): + """Save and simulate callback""" + timestamp = datetime.now().strftime("%Y%m%d%H%M%S") + new_simfile = f"{timestamp}_{Path(simfile).name}" + print(model.export_to_dom().toprettyxml()) + model.export_to_file(new_simfile) + print(f"Saved {new_simfile}, simulating") + run_lems_with("jneuroml", new_simfile) + + # simulate button + sim_button = ipywidgets.Button( + description="Save and simulate", + disabled=False, + button_style="success", + tooltip="Save LEMS simulation file and simulate", + ) + sim_button.on_click(save_and_simulate) + sim_widgets.append(sim_button) + + accordion = ipywidgets.Accordion( + children=[ipywidgets.VBox(sim_widgets)], + titles=[f"Simulation: {simdict['target']}"], + ) + display(accordion)