Giter Club home page Giter Club logo

ironflow's Introduction

Ironflow

Binder License Codacy Badge Coverage Status Documentation Status

Anaconda Last Updated Platform Downloads

Ironflow combines ryven, ipycanvas and ipywidgets to provide a Jupyter-based visual scripting gui for running pyiron workflow graphs. This project is under active development, and in particular the set of nodes available for the workflow graphs is still limited. If there is a particular use-case you'd like to see, or if one of our nodes is not working as expected, please raise an issue!

In its current form, ironflow has some UI performance issues when verifying the ontological status of ports. For smaller graphs, e.g. those in the examples, things should still feel quite snappy -- so if you notice serious performance issues please raise an issue! -- but for larger graphs, e.g. many 10s of nodes, you may notice some delay when generating the "recommended" nodes and ports on selection of an ontologically-typed port, and on updating the otype status on making new connections. This is a known issue, but not top-priority to fix; If you are using ironflow regularly and bumping into this problem a lot, please let us know in the issue and we'll increase its priority.

Usage

The main gui can be imported directly from ironflow.

The gui takes a session title at instantiation, and will automatically try to load any saved session (a JSON file) with the same name present. To visualize the gui, call the draw method. E.g.:

from ironflow import GUI
gui = GUI('example')
gui.draw()

The main screen for ironflow is used to build/run/save/load graphical pyiron workflows. In addition to manipulating the gui with buttons in the toolbar (hover the cursor over buttons for more info), you can:

  • Look at a node's IO values by clicking on it (which selects it)
  • Deselect things by clicking on empty space
  • See a richer representation of the node by clicking its SHOW button
  • Connect the IO (input/output) of a node by clicking on its port and then clicking on another node's OI port
  • Move a node around by clicking and dragging it
  • Pan the entire camera around by clicking and dragging empty space
  • Add a new node of the selected type by double-clicking on empty space
  • Delete a node by double-clicking on it
  • Collapse or expand a node by clicking on the little triangle on its body (has no effect on functionality, just makes it take less space)
  • Reset an (unconnected) input port's value to its default by clicking the refresh button in the node controller window.
  • Change an input port to "batched" mode by clicking the corresponding button in the node controller window.

In the default data execution mode (we don't currently do anything with the exec mode, so don't worry about it), nodes will update their output whenever their input data changes. You'll see the node body change color when it's performing this update. Some nodes have input (or output) ports that are of the execution rather than data type. These can be triggered by a signal from another node's exec-type output port, or by manually clicking the button associated with that port right there in the node widget.

In addition to the workflows screen, ironflow also incorporates the browser from pyiron_gui, as well as a log tab that allows you to turn the underlying ryven logger on/off and choose whether stdout gets routed to ironflow or its original context.

Two notes on the logger:

  • The log re-routes pythons stdout to the log panel! This includes the output of print statements from any other cells in your notebook. To see these in their original context again, you'll need to go to the log panel and toggle off "Route stdout to ironflow", or initialize your gui with the kwarg log_to_display=False.
  • The underlying Ryvel model supports fairly detailed logging. Since handling IO is not the fastest operation in python, and the log is fairly dense, this is turned off by default. You will still get error messages etc. in the log, but to see info-level log messages you need to go to the log panel and toggle on "Use Ryven's InfoMsgs system" or initialize your gui with the kwarg enable_ryven_log=True. Note that this will induce a performance hit on most actions. Performance can be restored by turning the Ryven logging off again, and you may get a further (extremely marginal) improvement by clearing the existing log history with the "Clear" button in the log panel.

Differences to Ryven

Ironflow is built on top of ryvencore 0.3.1.1. There are a number of minor differences between ryven nodes and ironflow nodes discussed in the next section, but at a high level there are two significant differences:

Data typing

All node ports are typed, and connection perform type-checking to ensure validity prior to establishing a connection. By default, a special Untyped data type is used, which performs all validity checks by value, and thus does not allow pre-wiring of a graph without full data. Further, the validity of the current value for each IO port is indicated by the port color: green for valid, red for invalid.

You can read the full spec for the typing rules the ironflow.model.dtypes module, but at a high level each port has one or more classes whose instances are valid input. An output port can be connected to an input port as long as its valid classes are a strict subset of the input port's valid classes, and as long as the output port won't allow the the input port to be surprised by a None value.

This type checking is still under development and may be somewhat brittle. Our goal is to extend this system to be dynamically informed by an ontology on top of the graph: instead of statically insisting that input be of type float, we instead demand that the ontological type of the energy be surface energy dynamically because the output value of that port is used, e.g., to calculate a grain boundary interface energy.

Ontological typing

Nodes can also optionally carry an "ontological type" (otype). Leaning on the pyiron_ontology library for representing knowledge in computational workflows, otypes give a rich graph dependent representation of the data and facilitate guided workflow design. This is fully demonstrated in the bulk_modulus.ipynb and surface_energy.ipynb notebooks, but a quick demo is also provided in the video below.

We see that there is a "recommended" tab for nodes. After selecting this menu, clicking on the CalcMurnaghan.engine port populates the tab with nodes that have valid output for this port. We can double-click to place the new node (Lammps) and repeat the process, e.g. for the Lammps.structure input. Here we see there are two possibilities -- BulkStructure and SlabStructure -- and place both. (Note, as mentioned at the head of the readme, there is some lag in ironflow right now; you can see this in the delay between the double-click and the placement of these larger nodes.) Not only do we get recommendations for nodes to place in the graph, but we also get specific recommendations of which ports make valid connections! Below we again select the Lammps.structure input port, and see that the output ports on both the structure nodes is highlighted. Similarly, if we click the Lammps.engine output port, we see that all the valid input ports on our graph get highlighted; in this case, CalcMurnaghan.input. Finally, we see the real power of otypes -- by connecting the two engine ports, the Lammps node now has access to the ontological requirements of the CalcMurnaghan node! In particular, CalcMurnaghan produces bulk moduli and thus only works for calculations on bulk structures. After these are connected, when we once again select the Lammps.structure input, only the BulkStructure node gets highlighted, and only BulkStructure appears in the recommended nodes window.

ironflow_ontology.mov

Of course, not all ports in ironflow are otyped, and indeed not all should be -- e.g. it doesn't make sense to ontologically-type the output of the Linspace node, as it is just providing numbers which may be useful in many contexts. However, for nodes which specifically produce and require physically-/ontologically-meaningful data, otyping is a powerful tool for understanding workflows and guiding their design.

Batching

Many ports can be "batched" by selecting them to open the node controller window and pressing the "batched" button. This changes the expected input for the port from a single value to a list of values. The node operation is then iterated over the entire list, and output values are correspondingly also turned to a list.

You can quickly see which ports are batched in the graph because their labels are converted to ALL_CAPS while unbatched ports are all_lower_case.

Any number of input ports can be batched on the same node as long as all batches are of the same length.

Batching impacts the type checking in a (hopefully) intuitive way: a batched output port of type float can be fed to a batched input port of type float but not to an unbatched input port of type float. Similarly, an unbatched port of type list[float] can be passed to an input port of type float only if that port is batched. Only single values and 1D lists are supported right now, although support for higher order matrices of data is planned.

Adding custom nodes

The tools needed for extending your graphs with new custom nodes can be imported as from ironflow import node_tools. New nodes can be registered either from a list of nodes, or from a python module or .py file. In the latter two cases, only those nodes that inherit from Node and have a class name ending in _Node will be registered (this allows you to have your own node class templates and avoid loading the template itself by simply using regular python CamelCase naming conventions and avoiding ending in _Node).

A new node should have a title and may optionally have input and/or output channels specified. If you want your node to actually do something, you'll also need to define an update_event method. E.g.:

from ironflow.node_tools import Node, NodeInputBP, NodeOutputBP, dtypes, input_widgets


class My_Node(Node):
    title = "MyUserNode"
    init_inputs = [
        NodeInputBP(dtype=dtypes.Integer(default=1), label="foo")
    ]
    init_outputs = [
        NodeOutputBP(label="bar")
    ]
    color = 'cyan'

    def update_event(self, inp=-1):
        self.set_output_val(0, self.input(0) + 42)


gui.register_node(My_Node)

Ironflow nodes differ from standard ryven (version 0.3.1.1) nodes in five ways:

  • There is a new helper method output analogous to the existing input method that lets you more easily access output values, i.e. just a quality-of-life difference.
  • Input/output ports and the port values are directly accessible as attributes if those ports were labeled, e.g. node.inputs.ports.foo or node.outputs.values.bar.
  • They have a representation dictionary, which is used by the IPython gui front-end to give a richer look at nodes. By default, this includes all the outputs and the source code for the node, but you can append to or overwrite these values by specifying an extra_representations dictionary on your custom nodes.
  • They have two new events: before_update and after_update, to which you can connect (e.g. node.after_update.connect) or disconnect (...disconnect) methods to fire before and/or after updates occur -- such methods must take the node instance itself as the first argument, and the canonical input integer (specifying which input value it is that's updating) as the second argument. (You can see an example of this in our base Node class, where we use it to force an update of the representation attribute after each node update.)
  • It is strongly advised to specify a dtype for each of your nodes from among node_tools.dtypes.
  • Ports have an additional otype field to facilitate ontologically-informed port and node suggestions.

Otherwise, they are just standard ryven nodes, and all the ryven documentation applies.

Special nodes

We also have a number of special parent node classes available based of the meta-parent BatchingNode. Instead of specifying the update_event, children of BatchingNode specify other functions so that the update can be automatically batched over.

The simples of these is DataNode, for which children specify the node_function method, which must take arguments based on the labels of input ports and returns a dictionary with keys based on the labels of output ports. Nodes of this type attempt to update themselves on placement, and will automatically update or clear (set to None their output ports based on whether or not all of their input ports report valid input values.

The others are TakesJob and MakesJob, children of which must specify _modify_job or _generate_job methods, respectively. These nodes are designed to interact with pyiron's GenericJob objects in a functional way. They also support batching, and will automatically populate run and remove buttons on the node widget, and lock the input after their owned job(s) are run.

Structure

The code is broken into three main submodules:

  • model, which provides and interface to and extensions of the ryven back-end
  • gui, which has all the code for driving the back-end from the IPython visual interface
  • nodes, which stores all the nodes that get included by default when you instantiate the gui/model

The node_tools submodule is just a wrapper to expose other parts of the code base in one easy-to-import-from spot.

The model itself, HasSession, is just a driver for a single ryven Session, with some helpful tools like the ability to easily register new nodes.

The gui inherits from and drives the model, and is broken down into three screens: workflows (which allow you to manipulate the model), browser (which wraps the project browser from pyiron_gui), and a log. Inside the workflows screen, visual elements of the gui are broken down into subcomponents like the toolbar, a panel with a visual representation of the graph, a place to show the node representations, etc. We avoid listing them all here because what's included and how it's laid out is still in flux. The key conceptual bit is that these various sub-components do not rely directly on each other's internal implementation, they go through the workflow screen as an intermediary where necessary.

ironflow's People

Contributors

dependabot[bot] avatar jan-janssen avatar jnmpi avatar liamhuber avatar pyiron-runner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ironflow's Issues

Make input/output accessible by label

From our discussion in #102

Programming the nodes with constructs like self.inputs[1] is inconvenient and error prone. It would be nicer to allow constructs with the actual input name, i.e. self.input.alat of self.input['alat']. Being able to provide actual names makes the code much easier to read, avoids errors when introducing new inputs/outputs since it does not require reindexing.

I'm 100% on board with this. Right now the inputs and outputs are just lists, but I should be able to override this with a list-like class that has a modified __getattr__ so it first checks if any of its elements has a label corresponding to the requested key.

`node_tools` in README is now `custom_nodes`?

In the example in README it's written from ironflow.node_tools import Node, NodeInputBP, NodeOutputBP, dtypes, input_widgets, but I guess it must be from iron_flow.custom_nodes now.

Touch support

Originally requested in #8, which is otherwise mostly resolved and I want to close soon...

allow also touch devices (e.g. IPad), i.e., next to mouse events also the equivalent touch events should be supported

This is a good idea and supported by the ipywidgets framework, so hopefully it will be pretty straightforward. I consider it low priority though, and since I don't have convenient access to a touch device running pyiron and jupyter I will delay implementing it.

:bug: port location not registering correctly

It really feels like you need to click to the right of center in order to actually select ports. I suspect there's something off in their _is_at_xy method, but I'm not going to be able to check in this session.

Node editor

@liamhuber, I really like the latest changes and developments in the look and feel of ironflow. It is fun working with it. Also, the possibility to start my binder sessions also for subbranches is super useful and highly appreciated. A feature I am missing quite a bit to productively working with ironflow is a node editor, i.e. to quickly create and store user specific nodes. It would be great if we could add to the gui a basic node editor that allows to program and add nodes without having to leave ironflow.

ipycanvas 0.13.1 breaks mybinder

If I don't pin ipycanvas to 0.12.0, when I launch on MyBinder I get a javascript error whenever I try to display an ipycanvas.Canvas object. Stack trace at the end.

I haven't updated ipycanvas on my local machine yet, so I'll try that first and see if it sheds light. Next a bit more digging. Finally a MWE for the ipycanvas/mybinder folks that just makes a canvas and fails.

Error:

[Open Browser Console for more detailed log - Double click to close this message]
Failed to load model class 'CanvasModel' from module 'ipycanvas'
@https://hub.gke2.mybinder.org/user/pyiron-ironflow-svzivgxp/lab/extensions/@jupyter-widgets/jupyterlab-manager/static/134.bcbea9feb6e7c4da7530.js?v=bcbea9feb6e7c4da7530:1:74865
asyncFunctionResume@[native code]
@https://hub.gke2.mybinder.org/user/pyiron-ironflow-svzivgxp/lab/extensions/@jupyter-widgets/jupyterlab-manager/static/150.3e1e5adfd821b9b96340.js?v=3e1e5adfd821b9b96340:1:10738
asyncFunctionResume@[native code]
@https://hub.gke2.mybinder.org/user/pyiron-ironflow-svzivgxp/lab/extensions/@jupyter-widgets/jupyterlab-manager/static/150.3e1e5adfd821b9b96340.js?v=3e1e5adfd821b9b96340:1:7531
asyncFunctionResume@[native code]
@https://hub.gke2.mybinder.org/user/pyiron-ironflow-svzivgxp/lab/extensions/@jupyter-widgets/jupyterlab-manager/static/150.3e1e5adfd821b9b96340.js?v=3e1e5adfd821b9b96340:1:5148
asyncFunctionResume@[native code]
handle_comm_open@https://hub.gke2.mybinder.org/user/pyiron-ironflow-svzivgxp/lab/extensions/@jupyter-widgets/jupyterlab-manager/static/150.3e1e5adfd821b9b96340.js?v=3e1e5adfd821b9b96340:1:3903
@https://hub.gke2.mybinder.org/user/pyiron-ironflow-svzivgxp/lab/extensions/@jupyter-widgets/jupyterlab-manager/static/134.bcbea9feb6e7c4da7530.js?v=bcbea9feb6e7c4da7530:1:73409
asyncFunctionResume@[native code]
@https://hub.gke2.mybinder.org/user/pyiron-ironflow-svzivgxp/static/lab/jlab_core.1b1fbf0503029f1fe923.js?v=1b1fbf0503029f1fe923:2:981381
asyncFunctionResume@[native code]
@[native code]
promiseReactionJobWithoutPromise@[native code]

Node controller doesn't show `None` well

Since #131, input can only be modified in the node controller if (a) it's not connected to receive from the graph, and (b) there is an existing widget that handles that data type. In particular, for float data, transitioning from ipywidgets.Text to ipywidgets.TextFloat means that any initial None values no longer register in the widget, the widget default (0 in this case) is shown instead.

As far as I can tell this is just a fundamental limitation with the widgets, which don't have any sort of allow_none flag. I had hoped to at least add the default as a tooltip so when you moused over you could at least read the default, but tooltips aren't even implemented for TextFloat.

So for now let's just live with it.

Data from database

In a Zoom meeting, Joerg brought up the idea of allowing input values to be pulled directly from existing database data, instead of being provided at runtime by the user or pulled from other node results. This will require ontological ideas to already permeate the pyiron database and be exploited in ironflow, but then lets us join pyiron's workflow power with its data sharing/storing power.

Desired behaviour:

  • A new FromDatabase node, which gets an output value selected from stored database data
  • The node has a GUI filtering system, which allows you to place conditions on the selected database object (e.g. structure contains aluminum, representation uses DFT, whatever...)
  • When this node's output is connected as input to another node, exploit ontological filtering to only show database objects which have output of the correct character (e.g. if you need a BulkModulus-type input, the FromDatabase node pre-filters so that you're only looking at Murnaghan jobs (or whatever else winds up holding bulk moduli)

This requires both the ontolgization of pyiron as well as implementation of #21 to be fully realized, but a much dumber version of the FromDatabase node that takes some sort of database path and output value and simply tries to grab it from HDF is an intermediate implementation that should already be possible to implement...

More control in the "show" panel

Let's put more data in the "show" panel (e.g. the job_table() for the project node, the hdf5 storage for jobs, etc) and control it by pushing buttons in that panel (e.g. to show and hide the structure printout when looking at a plot3d).

It would also be nice to limit the vertical size and just rely on a scroll wheel.

Source from Joerg's comment in #54 :

  • The show button is really great. For nodes like BulkStructure the output gets however a bit lengthy since below the 3d-plot also the structure is printed. An idea may be to split the graphical representation (3dplot) and the text output horizontally rather than vertically. Also, the print window may be restricted in vertical size and using a (vertical) scrollbar. It may be also convenient to have something like a checkbox to show/hide a certain input. I like e.g. the solution in the plot3d node where <I can choose whether I want to show the printout. Having such a box in the corresponding output window may also be a good option.

Feature request: New node type with display data

Make a new node class, similar to DualNodeBase that holds data to be displayed in GUI.out_plot. Similar to DualNodeBase this has some internally stored value, a representation, that can be sent to the gui's output display -- e.g. a matplotlib plot, an nglview figure, or even just text. This should be sent whenever you click a show button on the node, and as long as this node was the last one sent to the output display, the display should get updated whenever the node's input gets updated.

We already mostly have this as of #30, but we can pare down some of the stuff from DualNodeBase -- e.g. plotting can just be a leaf, we never need output from it -- and the exec input can be replaced with a show button.

Then this functionality can be inherited in many more pyiron nodes, and hopefully we can depend on pyiron_gui and just grab representations from there.

Add log flush

When re-instantiating the GUI I discovered that the log doesn't flush. In principle this is fine, but it would be very helpful to have an option to flush it on instantiation and a button to flush it from the GUI log screen.

Issues

When playing with the present version (ironflow/blink) I realized a few minor issues:

  • linspace: clicking on show does not show the vector
  • matplotlib appears to be not working (I connected linspace to matplotlib but was unable to get a plot)
  • result node does not show an output (I connected it to linspace but it didn't show the vector)
  • clicking on linspace show does not work (should show the vector)

Ontological connections

Currently, Ryven allows node connections to be made willy-nilly. It would be very nice to have strict type checking when connecting the I/O of two nodes, which could be done as a thin layer on top of Ryven. Following a Zoom call with Joerg, it would be ideal to leverage ontological developments inside pyiron to control these checks. Since getting pyiron to be fully ontological is a very big task, in the short term let's develop an ontological layer on top of the very narrow slice of pyiron for which we have ironflow nodes.

Desired behaviour:

  • Node connections between ports are impossible if the types do not match
  • Selecting an I/O port highlights all the available O/I ports in the graph which are valid connections
  • Selecting an I/O port activates some manner of "suggested node" menu, which shows nodes containing appropriate O/I ports

Catching up to the ontological work that's already been going on, and the architecting the ontology check so it's as loosely coupled to Ryven and the pyiron nodes as possible will all take some thinking time, so this feature won't be immediately available.

Ironflow access rights differ?

@jan-janssen @JNmpi, I am no longer allowed to push to the ironflow repo, and unlike the other pyiron repos I don't have access to the "settings" tab here. My guess is that the access rights differ, maybe because of how you spun up the repo originally, Joerg? I think it's only coming up now because I seem to have recently transitioned from "owner" to "member" of the organization overall.

Looking at our other repos, I expect the "pyiron" team needs to be added under the access settings:

Screen Shot 2022-11-07 at 10 06 20

Log screen should pin itself to the bottom of the scroll area

The log screen introduced in #123 can get quite long, and it would be great to have it automatically start at the bottom of the scrollable area (and pin itself there when it's at the bottom and more content gets added). Unfortunately this behaviour is not currently supported by ipywidgets. Since this is a relatively minor QoL thing, I'll probably just wait and hope that it gets supported upstream so that the fix is just one or two extra layout lines or whatever.

Colour-code output by readiness

Many nodes require all their input to be properly set before they produce output; some (e.g. Lammps) actually need to be explicitly run. It would be nice if data output ports were colour coded (e.g. red/green) based on whether they held real output or just an un-processed None value.

Performance needs to be improved

Selecting nodes with lots of ports takes a long time, I believe the holdup is generating the node controller interface. This needs to be made more performant.

Macros

Be able to collapse multiple nodes into a macro node. After being collapsed only IO that is not sourced from/channeled to another node inside the macro should be exposed, e.g. with labels like subnode_name_1.input_name_4; subnode_name_3.input_name_0. It would also be great to then be able to generate the underlying node code from the macro, so that it can be saved to a module file, imported, registered, and used in other graphs later.

The main ryven author, Leon Thomm, has considered and rejected macros, at least for now. Personally, I think they're important/cool enough that we should push on them anyhow and one of ryven's comeptitors, PyGraph, does include support for 'subgraphs' -- i.e. macros, so I believe it is a soluble problem. I appreciate in particular, however, Leon Thomm's open question about how one should handle data vs exec flows.

Allow users to force a node update and see the output

Either in a new cell in the notebook, or a special window in the gui.

In principle adding a "(force) update" button is super easy, but I will need to research pushing content to new cells.

As an addition or alternative, it would be nice to give easier access to a selected object (be it port or node), which should be straightforward to add to the gui class. Then you can simply gui.selected_object.node.update(inp=3), gui.selected_object??, etc, in some other cell.

Remove pin on ipywidgets

tldr; there is no reason we need the pin, and whenever nglview is compatible with more recent versions of ipywidgets we can remove it. Note: Version 8 of ipywidgets contains the upgrade to FontAwsome 5!

As far as I can tell, the root cause of #74 was not ipycanvas, but ipywidgets+nglview.

The errors like Failed to load model class 'CanvasModel' from module 'ipycanvas' in the Jupyter output and [Error] Plugin 'ipycanvas:plugin' failed to activate. in the JavaScript console may be caused by an issue with incompatible @jupyter-widgets/base versions and token conflicts. I discovered this by searching the follow-up JavaScript console error: [Error] Error: No provider for: jupyter.extensions.jupyterWidgetRegistry.. This understanding is supported by one of the JavaScript console warnings I get ([Warning] Unsatisfied version 6.0.1 of shared singleton module @jupyter-widgets/base (required ^2.0.0 || ^3.0.0 || ^4.0.0) (remoteEntry.249e02ea9427f8cca2ad.js, line 1)), even though I don't fully grok the issue.

The catch is, I seem to have been wrong about the conflict coming from ipycanvas+ipywidgets -- I think the canvas business was just a red herring that cropped up because multiple versions of @jupyter-widgets/base were getting loaded. The real issue seems to by nglview+ipywidgets, and in a cruel twist of fate the hand holding the dagger is pyiron's very own @pmrv ๐Ÿ˜‚ He dipped his toes into nglview and frozen their ipywidgets dependency at v7!

The catch is that when both nglview and ipywidgets are explicitly included in the environment.yml for mybinder, mybinder seems to ignore this freeze and simply installs the latest version of ipywidgets. The catch is that I haven't been able to systematically reproduce the JavaScript error in a MWE, although I can reproduce another error and keeping ipywidgets down is letting ironflow's binder application work so far.

One day it would be nice to remove this pin (a) because there is no real reason for it inside ironflow, and (b) ipywidgets v8 contains the upgrade to FontAwesome5, which would let us resolve a bunch of TODOs for button icons.

I wanted some pretty detailed documentation of this:

  • For future me, because I will try hard to suppress this memory. Fighting dependency problems is miserable
  • So I don't forget to remove the pin and free up FontAwesome5 eventually
  • Because I had a really hard time finding google results to solve this, and the behaviour from mybinder (ignoring nglview's pin) surprised me, so I hope these terms will get picked up by google and help anyone else caught in the same trap
    • Failed to load model class 'CLASS' from module 'MODULE'
    • [Error] Error: No provider for: jupyter.extensions.jupyterWidgetRegistry.
    • [Warning] Unsatisfied version X.Y.Z of shared singleton module @jupyter-widgets/base (required ^X2.Y2.Z2 || ^X3.Y3.Z3)

Removing structure input breaks node control view

When you ask for a .data() from a NodePort that has no connections, it attempts to serialize its .get_val(). This chain gets triggered by our control interface for nodes. Most of the time it's no problem, but when the value of the port is an Atoms instance, we run into trouble -- ASE Atoms objects (and thus ours) are not pickle-able, and base64.b64encode(pickle.dumps(object)).decode('ascii') is exactly the serialization that's being attempted here.

Practically speaking, this means that when you hook a structure up as input to the Lammps node (or any other node that takes structure as input, e.g. plot3d), the node controller interface is just a big error message until you reconnect the data.

ryvencore 0.4 handles data and serialization differently. To keep everything on conda-forge we still depend on 0.3. I just made a try/except workaround that solves the surface-level problem, but I'm going to avoid trying to actually get the serialization to work until we rely on the new data paradigm.

Give sensory feedback on exec buttons

Right now, since they immediately "un-press" themselves, there is no feedback for the user that the button got pressed. I think I can wrangle something with the on_mouse_up hook in ipycanvas.

Source from Joerg's comment in #54:

  • I was unable to get a Lammps job running. Clicking on the run button gives no feedback, and the show command returns only none. It would be helpful to provide a feedback that the click on the button actually did something (e.g. a click sound or a short color change of the button).

Abstraction between representations and calculations/evolutions

From our conversation in #102

The following point is realted to pyiron, but required to get nice solutions for ironflow:
It would be good to have for some pyiron constructs an alternative formulation. An example is

    job.calc_md(temperature=300)  # previous
    job.calc = calculator.md(temperature=300)   # alternative

Such a formulation would allow us to easily create generic modules, e.g. for MD that can be used as input for VASP, LAMMPS, etc.
The same applies to job.server etc.

Yep. I will probably hack-and-slash something to this effect after I get the book-keeping (CI, pypi release, conda-forge release) all sorted out. My current vision is that things like the Lammps node can only ever run a static calculation (or no running of these nodes??) and that they otherwise need to be passed as input to MD, minimimization, NEB, VC-SGC, etc. calculators.

This is thus a little bit different from what you suggest here, something more like:

md = pr.atomistics.calculator.MD('my_md_run')
md.temperature = 300
md.n_steps = 1000

lammps = pr.atomistics.representation.Lammps('lammps_reference')
lammps.structure = pr.atomistics.structure.bulk('Fe').repeat(4)
md.representation = lammps

md.run()

In a perfect world I imagine that the calculator would then see if the representation has native support for this type of calculation, e.g. lammps has calc_md so we would basically just be using the calculator as a wrapper interface for that -- but then if the representation doesn't allow it that the calculator either falls back on a python-coded version of the algorithm, or throws an error (probably when the representation is assigned, so we fail as early as possible).

This is definitely best implemented right in the heart of pyiron, but I think it will be productive to play around with the idea here in ironflow where I'm just making it look like that's what the underlying model is. That will give us some freedom to experiment with different architectures/interfaces without breaking anything anyone else cares about.

Have nodes flash another colour when updating

Probably just in the main body, probably just by adding a _call_before_update list and populating it with a colour change, and adding the reverting colour change to the existing _call_after_update already on atomistic_nodes.NodeBase.

:bug: Invisible canvas widgets can still get selected

To reproduce:

  • place two nodes
  • connect them
  • hide the IO of at least one
  • click on the remaining visible connected port, and where you know the invisible port to be located
  • the connection (wrongly) gets terminated.

Desired behaviour: invisible canvas widgets should be really non-existent.

Node error stops it from getting deselected?

It seems like a problem in the node can interrupt the deselection process.

Steps to recreate:

  • Make two Val nodes and populate them with 1 and 'a'
  • Make a For node, and give the Val nodes as input
  • Select and move the For node around -- now it can't be deselected (or at least the highlighting doesn't change)

When the 'a' node is 0 instead, this doesn't come up, which makes me suspicious that the node complaining is somehow triggering the GUI issue.

EDIT: I'm on branch more_text_input when this is happening

Loading does not load port values

As noted by Joerg. E.g. in the canonical example notebook if you change the species from "Fe" to "Al", save, and reload the session, you'll see it reverts to "Fe".

AFAIK ryven itself handles saving and loading this data fine, so I probably broke the behaviour when I patched ryvencore to use the defaults provided in the node definition.

I'm currently locked out of a bunch of github behaviours, but this is label:bug assignees:liamhuber.

Exec connections get drawn to the wrong place

As stated, the position for the connection is wonky. I'm 99% sure this is just to do with how I specify the x and y coordinates of the triangle compared to those of the circular data ports. Should be an easy fix, maybe by specifying a new connection_location attribute on these buttons.

Source from Joerg's comment in #54:

  • Connecting an exec with a button aligns the node with the edge of the button, not with the center of the circle.

Turn lammps potential input into a dropdown menu

Which auto-populates from the list_potentials() method.

Source from Joerg's comment in #54:

  • It would be helpful to have as input in the Lammps job for the potential a combo-box that shows all the available potentials

Branding

This month I'd like to get the repo set up as a pip package and subsequently available on conda-forge. Before this we should briefly pause to make sure the naming/branding is what we want, as this gets difficult to change downstream. I'm content with ironflow, but earlier @jan-janssen had expressed some mild surprise it was not pyiron_ironflow. @JNmpi any reconsideration since creating the project?

Add a "reset" button to input

That allows (unconnected!) inputs to be reset to their default value in the node controller box.

While you're there, adjust various widths in the UI elements.

Converting ironflow scripts into python/pyiron code and vice versa

I super nice feature would be to convert the graphical workflow into python/pyiron code and vice versa. This topic came also up a couple of times in our discussions. While I definititely support this idea and the many opportunities it opens I also see a couple of fundamental challenges. To start the discussion a few thoughts. To be more specific let us consider the workflow to construct a simple Lammps jobs:

pr = Project('test')

for i in np.arange(1:5):
        job = pr.create.job.Lammps(f'lammps_{i}')
        job.structure = pr.create.structure.bulk('Al', bulk=True).repeat(5)

job.run()

Ideally, the pyiron object "job" would contain the information that is given by the above workflow. This is however only partly true. While it "knows" about the used indices [1,2,3,4] and the atomic structure it does not know anything how these parameters have been generated. The relevant information, i.e. the for loop and the pr.create.structure command are known to the Jupyter notebook but not to the object that is stored by pyiron. While the job object has all the information to reconstruct the project and the data it has no access to the metadata. This is unfortunate, since the metadata reveals much more directly what the creator of the workflow had in mind. Even more important, the metadata provides the full information in a much more compressed form, i.e., rather than only getting a vector of integers one gets the construction scheme that needs only two input parameters (in the abov example min=1 and max=5). The effect is even more dramatic for the structure, where three parameters (string, boolean, integer) are sufficient.

How could we expose the metadata to the pyiron job object? For the structure it would be straightforward: The return object would contain not only the positions, cell etc. but also the generating recipe. In fact, this would be straightforward to implement and would strongly enhance pyiron.

What about native python or numpy objects? A possible solution would be to make them accessible as pyiron objects (very similar to the approach in ironflow where we redefine constructs like a for loop, integer arrays etc.). However, I am not sure how this could be converted in easy to read and intuitive code. A very rough first concept is shown below:

pr = Project('test')

int_vec = pr.numpy.arange(min=1, max=5, steps=1)
for i in int_vec:
        job.counter = i
        job = pr.create.job.Lammps(f'lammps_{i.value}', i)
        job.structure = pr.create.structure.bulk('Al', bulk=True).repeat(i.value)

job.run()

In the above example i is not only an integer but a pyiron object, that contains also its generator function. I am sure that this is not the best solution, but to start the discussion it may be helpful to start with a concrete example.

Small feature requests

A few ideas about features to enhance pyironflow and improve user friendliness:

  • use different colors for the exec and data connectors (both for input and output) #13
  • make dot on exec input clickable (i.e., rather than connecting it with a click button one can directly activate the node/workflow by clicking on the exec input)
    • This is largely done in #33, for all things like Plot3d and similar. For actual jobs that run, you still need the click button, but I think job management needs more thought anyhow. Ok, I thought more and I want to "uncomplete" this request. Push-button execution should be reasonable for "exec"y ports as well as "display"y ports. Will do it later.
    • Completed in #49. We can have multiple exec input ports, so for now I'm just using one for each function, e.g. one to run a job and one to delete it.
  • a click on the node shows its content (gui) but makes it also moveable. It would be good to have to separate events, e.g. clicking enter to show the nodes gui when the mouse pointer is over it Mostly in #15, which changes the feel quite a bit overall. Keyboard is totally ignored now, so no using "enter" with a hovering mouse.
  • sort node list alphabetically #11
  • make nodes resizable (by using mouse pointer or based on content) Free-form resizing is pretty tough, but nodes now automatically size their height based on how many IO ports they have, and are collapse-/expandable to compress/show this IO over in #41
  • put static output (e.g. matplotlib, pandas tables, etc. ) directly into node Currently declined, see comment below
  • allow also touch devices (e.g. IPad), i.e., next to mouse events also the equivalent touch events should be supported This shouldn't be too hard, but for practical reasons it will take a while to get to, so I made a separate issue for it: #47
  • use Tab widget from ipywidgets to manage multiple scripts (could be similar in look and feel to browser tabs) #26; I'm not totally happy with all the button icons, but I'm having trouble getting the ones I want to render...these are good enough for now though. The "+" (i.e. new script) tab is wider than it should be, but this is a known ipywidgets issue and doesn't seem to be fixable from the python side.
  • include node editor (allows users to interactively develop and test new nodes within ironflow, without forcing them to switch environment and use an external code editor) #14, at least to the extent that all the tools are now available to write new node classes wherever the gui is being called from (e.g. the jupyter notebook) and then gui.register_user_node(MyUserNodeClass) makes them immediately available in the gui.

Performance plus gui input issue

Thanks @liamhuber for the many improvements. It is particularly nice to have now nodes for the different calculators available. A few thoughts/issues below:

  • Responsiveness:
    - Adding and working with nodes having many input ports feels (as you mentioned already) very slow. This makes it very hard and sometimes impossible to work, since one is never sure whether the doubleclick etc. worked or not.
    - I had also the impression that commands like remove job takes much longer than when running pyiron directly
    - Solving and improving this issue has for me highest priority. The present waiting times are too long for productive applications or to play around and test ironflow.
    - A few ideas:
    - I fully agree with using the features provided by ryven as suggest by you in the respective issue.
    - It may be generally a good idea to show not all possible inputs in the graphic node representation but only the mandatory ones. The less important ones could be then set in the node input gui. If one needs a port to connect them to other nodes one should have an option to make them visible in the node.

  • GUI input issues:
    - When working with the calc_md node I could not set the initial_temperature variable to None. I guess since the gui ipywidget element is an integer input it accepts only integer numbers, not None. The same applies e.g. for the pressure input. For these cases we may combine an integer input gui with a boolean flag (for None or integer).

Indentation not consistent

>>> class My_Node(Node):
>>> title = "MyUserNode"
>>> init_inputs = [
>>> NodeInputBP(dtype=dtypes.Integer(default=1), label="foo")
>>> ]
>>> init_outputs = [
>>> NodeOutputBP(label="bar")
>>> ]
>>> color = 'cyan'
>>>
>>> def update_event(self, inp=-1):
>>> self.set_output_val(0, self.input(0) + 42)

There's no indentation after the class definition in the doc string?

Update outputs on update() failure

When the node update() method fails, this gets passed to the log ok, but the output values simply don't get updated. From a functional paradigm we want input and output to stay linked, so these should be updated (e.g. to None or some sort of custom NotData class).

This is done in #131 for the BulkStructure node where it works fine. Generalizing it might be a bit tricky though, as it's possible for nodes to have multiple outputs only some of which may get invalidated. Maybe the parent node class should implement an invalidation routine that by default sets all the output to None if any of the input is invalid (a reasonable base case) and the user can override this?

I bet this breaks readthedocs

This commit. If I understand, readthedocs actually needs that env stuff, even though with pyiron_github I finangled it so sphinx doesn't (by merging envs with the main env file).

Wait in hope, and fix it when it breaks. This is just a reminder why it's going wrong if (when) it does.

The GUI implementation is getting messy

It's due for another refactor; try to lean more heavily on ryvencore.Event to tidy things up. Also look into cleaning the cyclic attribute assignment while you're there.

Give the user more data about what's happening under the hood

There's a number of places where we need to expose more data to the user. I've got a few gut reactions below, but it will need some digesting and trying things out to find really nice solutions overall.

From our discussion over in #54

  • For situations like above it would be helpful to have debugging tools, e.g., nodes that show the error output, the jobs, hdf5 etc.

Hmm, I guess this is actually several issues:

For the case you mentioned above where remove doesn't do anything, I actually explicitly have except AttributeError: pass that triggers when the node thinks there's no job to remove. Cases like this could easily be replaced by things along the lines of except AttributeError: self.gui._print("No job found to remove").

For browsing jobs, hdf5, etc, I actually would really like to get this working in the "show" window. For instance, right now the project node shows just the project name, but that's only because I was getting funny errors when I tried to directly put the job_table() there -- this is still my goal. With your idea of putting buttons right into that window to manipulate output, we should be able to get even more power from that window without having to introduce new nodes.

Finally, Ryven intentionally suppresses errors that arise when nodes update. If I understand, the intent is to keep the Ryven instance running smoothly even when a node borks -- a very good idea! But you're right that I should find where Ryven is routing these errors to and re-route them to our log, or even perhaps to a GUI display.

Feature requests and bugs based on a practical workflow example

Thanks @liamhuber. Many of the new features are really great. I particularly like the show button and the color flash when a cell is active. I worked a bit with the latest version and tried to build up a simple workflow to build a pyiron table, use this data to make a plot (including using the information from the column names to set the axis labels) and filter relevant projects. It was nice to see that the new capability to add new nodes within the notebook is quit powerful. I have added the example in the example_table notebook and json file in the repository.

When working with pyironflow a missed a few features and observed a few bugs listed below:

  • deleting a script does not work (clicking on the icon button nothing happens)
  • it would be nice if pyironflow would remember the last saved json-file and script and open it directly
  • when loading the json file the input is initialised to the default value, not the one in the saved script (this severely limits the user experience since the same buttons have to be clicked again and again making debugging hard)
  • it would be nice to get the command to open and run a node in a separate cell of the jupyter notebook. I did it manually via gui.script.nodes[index_of_node] but this is rather cumbersome since the node index has to be obtained by trial and error. For example, the command (or a button) could be placed in the box where the input of the node is controlled.
  • since scrolling appears to be not working in the output window it would be nice to open the output content in another notebook cell
  • certain node feature will be commonly requested, e.g. input as combobox with the possibility to add/delete them (e.g. for the pyironTable to add new columns)
  • I was expecting that gui.load(json_file) would work but got an error message

Failures in lates one_mode branch

I really like the new double click feature. It makes the gui much more intuitive. However, I realized two bugs:

  • When selecting and deselecting a node I cannot select it again. I first have to select another node. This is rather unintuitiv.

  • When selecting the ForEach node and double clicking on the canvas nothing happens. I guess the color definition is missing. At least this is the error I get when I try to load my example workflow.

Allow labelling nodes

The default label can still be the node class, but allow users to re-label them in the graph.

  • Nodes get a new label field, and don't forget to serialize it
  • The gui tries to read this, but defaults to title if it's None
  • Add a gui element for users to actually change the value

Actually add docs

Readthedocs is just an empty page ATM. Needs some sort of landing page and an API reference at a minimum.

Ryven log output sometimes shows up in widgets

After #123, when using the Ryven logger, but not routing stdout to ironflow's log widget, the Ryven log sometimes pops up where you don't want it. E.g. on selecting a node, some nodes print --> INFO: getting value of node input right in the node controller box. Not catestrophic, but annoying.

What solved the Docs issue?

After migrating a bunch of infrastructure stuff from pyiron_module_template, I was still getting the following error from the Docs workflow:

TypeError in source/notebooks/version.ipynb:
'coroutine' object is not subscriptable
/usr/share/miniconda/envs/test/lib/python3.9/site-packages/sphinx/cmd/build.py:281: RuntimeWarning: coroutine 'ZMQSocketChannel.get_msg' was never awaited
  return 2
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Error: Process completed with exit code 1.

Modifying the .ci_support/environment-docs.yml and removing docs/environment.yml in this commit -- analogous to what we find in, e.g. pyiron_atomistics -- fixed this, but I would like to better understand why. What dependency was causing the bug? Should we update the module template somehow?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.