Execution of Dashboard widgets #2
Replies: 3 comments 3 replies
|
First of all, fantastic work on the widgets. The WidgetManager and WidgetBase organization looks very clean and thought out. This far exceeds my expectations. At this stage "a quick and dirty hack to get things running and refine later" would have been sufficient, but it looks you are building things right away with the big picture in mind. Regarding pickling and parallelization, I am a little confused. |
|
Great work and start on the Dashboard backend. It seems you have given it enough thought and the org looks good to me.
I like this
For this master loop, is there an event manager or similar class that logs pause/resume etc. from the user and passes it to the loop? Looking forward to how this setup communicates with the UI of the dashboard ; and how you tackle related challenges, good luck! |
|
All the relevant details from this discussion are now part of the documentation: |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Hi @orbeckst, @jeremyleung521, @amruthesht, @HeydenLabASU,
This is a discussion regarding the core execution of the dashboard widgets on the server side. #1 showed wireframes of how the widgets are added, updated, displayed and deleted from the dashboard UI.
The details below focus purely on the execution of widgets on the server side and doesn't include a lot of details about the actual interface with UI.
WidgetManagerThe lifecycle of all the widgets is managed by a
WidgetManager(singleton) on the server side. This is responsible for:Add Widgetor when an existing widget is duplicated)WidgetBaseThere is a
WidgetBase, which is the base class for all widgets. This class implements__init_subclass__method to register all widget classes (all of which derive fromWidgetBase) automatically with theWidgetManager. It is this class list that is used by theWidgetManagerto provide the list of available widgets to the UI (from thenameanddescriptionclass atrributes of the corresponding widget classes), create instances using these classes and maintain the widget instances list (each widget instance is identified by auuid), use this list to run and manage these instances.Trajectory Iteration Loop
There is a core loop that gets created along with the universe creation (i.e., when connected to the live simulation) as a separate thread or an
asynciotask. It uses events to handle Pause / Resume / Connect / Disconnect from the UI.trajectory.next()is used to iterate the trajectory in this loop. The buffer ofTimestep's, which is used for batching is updated in this loop (see details in next section). Therun_widgets()method of theWidgetManageris also run in this loop (see details in later section).TimestepBuffer for Batch AnalysisThe
Timesteps buffer (global) is maintained as acollections.dequewhere themaxlenis what is configured in the Settings page. A copy of the the current timestep is made before it is added to this buffer. We need to make this buffer available to the various analysis widgets in a seamless manner so that it can be accessed in a natural way - for example:u.trajectory[-1]should change the timestep to the last but one frame with respect to the current frame, etc. Currently, streamed trajectories cannot be directly indexed this way and throw aStreamed trajectories must be an indexed using a sliceexception. One way I was able to achieve this while exploring was by creating a simple wrapper (within the dashboard code) for thetrajectoryattribute and implementing the__getitem__to set the timestep from this buffer. (A__getattr__to pass through other attributes and a__dir__to keep code completions working were also needed) Without this, passing the buffer to individual widget code and having them access it in a usable way would be very tedious.Show example code
Here is an example of how the
BufferedTrajectorywrapper could look like. This internally handles the circular buffer, provides direct negative indexing, updates buffer automatically with.next()iteration as shown below:Here is how it is setup:
WidgetManager.run_widgets()The
run_widgetsmethod in theWidgetManageris the one responsible for executing all the widgets and passing over the output to the UI. It runs through all the widget instances, checks which of them are runnable at a given moment (based on whether the widget execution mode isper-frameorbatch), checks whether the widget needs to be run in serial / parallel (through ais_parallelizableattribute) and in the later case, get a delayed task (joblib'sdelayed) that it adds to the list of parallel tasks. A separate list of instances which need to be run in serial is also created. All the parallel tasks are run usingjoblib.Parallelin a separate thread (which is joined later) before the serial instances are run. Then_jobswill be based on a configurable value in the Settings page. Note that these attributes for the execution mode and serial / parallel can also be made as inputs in the widget and changed at runtime as long as the underlying code supports them.Paralellization with Streamed Trajectories
More details regarding the parallelization case: Streamed trajectories cannot be pickled today. We get a
IMDReader does not support picklingexception when we try to do this. Removing this exception here, reveals that the core problem arises becuase of the network socket inimdclientwith Python'scannot pickle 'socket' objectexception. Pickling support is required if we need to use the universe as is within the parallelized task. Without this, the analysis code will have to be refactored quite a bit to remove all dependencies on the universe and make them independently executable. The best way I was able to get streamed trajectories picklable was by adding a custom__getstate__method inIMDReaderto remove the_imdclientattribute from the state (on the unpickling side, in__setstate__, set the_imdclienttoNoneand added a warning). I was then able to create parallelized tasks easily with much more natural and simpler code based on our existing examples (as the analysis code don't / shouldn't need to iterate the streamed trajectory).As stated at the beginning, the discussion above is about the core execution of the widgets on the server side and doesn't include a lot of details about communication with the UI etc, which are for a later time.
Could you please let me know if you have any thoughts, suggestions or feedback on the above. Thanks
All reactions