Canvas-based modeller for ASCEND

From ASCEND
Jump to: navigation, search
This article is about planned development or proposed functionality. Comments welcome.
Screenshot of the prototype Gaphas-based canvas for ASCEND

Many other modelling environments provide a graphical flowsheet modeller, or 'canvas' onto which unit operations are arranged to create a process flow diagram (PFD). We would like to be able to do this in ASCEND too. The work on this was started by John Pye, continued in GSOC2009 by Arijit, and then as part of GSOC2010 and GSOC2011 by Grivan, and then as part of GSOC2012 by Saheb. Most recently, Mike has been working on in 2016. We work closely with Arjan Molenaar. The current goals are:

  • fix and test our model load/save functionality (Grivan)
  • some generic way for adding arcs in custom shaped icons (Saheb)
  • add support for basic custom-shaped blocks (Done - Saheb)
  • improve our parameter-setting dialog
  • test with some more detailed flowsheets
  • add support for annotations on flowsheets
  • add support for reporting variable values on the flowsheet
  • create a new empty flowsheet (done)
  • plop 'blocks' onto a canvas to represent various 'unit operations' such as tank, turbine, pump, column, resistor, capacity, actuator, motor, etc. (done)
  • wire up the 'ports' on these blocks using 'connectors' that would correspond to 'streams' with multiple variables and parameters within them, such as a steam flow, or single variables such as a voltage signal. (done)
  • view and edit parameters for the blocks in order to make adjustments to the system design.
  • view and edit constants and sets for the model (eg name of the fluid in a stream).
  • solve the model: export to solver, give error message feedback, create linkage between blocks and solver vars. (partially done)
  • see graphically the parts of the model which have not converged or which are out-of-bounds. (done)
  • view the values of variables within the blocks once it has solved (done)
  • save the flowsheet in such a way that when reloaded, it can again be solved afresh. (done)
Typical energy system diagram of the sort we would like to work with 'live' in this modeller.

Getting it running

Currently (29 March 2013) you can test our canvas-based GUI using the code from our saheb-canvas: branch. Tested with Ubuntu 12.04 32-bit.

cd ~
sudo apt-get install python-gtksourceview2 python-setuptools
# export http_proxy=...if required...
# git config --global $http_proxy #...if required...
git clone https://github.com/amolenaar/gaphas.git
cd gaphas
sudo python setup.py install
cd ..
svn co svn://ascend4.org/code/branches/saheb-canvas ascend-canvas
cd ~/ascend-canvas
scons install INSTALL_PREFIX=/usr/local
export LD_LIBRARY_PATH=~/ascend-canvas:/usr/local/lib
export ASCENDLIBRARY=~/ascend-canvas/models:~/ascend-canvas/solvers/qrslv
pygtk/canvas/canvas.py

During the above process make sure you have setuptools-git 0.4.2, because installing Gaphas requires version 0.4.2. Direclty using apt-get, you will get the newer version 0.6.2 that is stored in ubuntu 12.04 repository.

P.S.- Note that this does not include the recent 'libavoid' enhancements to Gaphas. You can try using Gaphas with libavoid for that, but don't expect immediate success.

Progress report

May - August 2012

Saheb - I will add it soon, till then you can refer to my page for current status of canvas based modeller. Saheb

Mar 2011

Arjan Molenaar has incorporated Michael Wybrow's libavoid into Gaphas. This improves the ability to write nice diagrams where lines do not cross over in front of blocks, but instead route around them automatically.

Previous

Note: Recent changes (svn trunk r2819) have restored the functioning of the Canvas GUI. You must be sure to use latest Gaphas (git clone http://github.com/amolenaar/gaphas.git && cd gaphas && sudo python setup.py install ).

We have got a basically-functional 'canvas' widget now based on Gaphas, with 'blocks', 'ports' and connector lines (see figure). A 'palette' is created based on markup of ASCEND models in the model library with NOTES as specified below. Blocks can be created by clicking in the palette then clicking in the canvas. And connector lines are created by draging from one 'port' to another. Blocks can be saved and restored, using Python Pickle functionality.

Canvas models can now be exported to the solver and ASCEND can attempt to solve them. However, there is currently no means for setting the parameters of Blocks, so there is no simple way of using a canvas model to solve specific problems. There is also not yet any way of viewing the resulting solution!

We 'mark up' MODELs as being blocks suitable for canvas-based modelling, using the NOTES framework. We can mark blocks, and also mark variables within them as being inputs or outputs, as follows:

MODEL pump_simple;
    ADD NOTES
        'block' SELF {Simple model of a pump}

    END NOTES;
    inlet "in:" IS_A stream;
    outlet "out:" IS_A stream;

    inlet.mdot, outlet.mdot ARE_THE_SAME;
    dp IS_A delta_pressure;

    outlet.p = inlet.p + dp;
END pump_simple;

Metadata currently implemented is

  • name for the block (via NOTE of lang 'block', also is used to tell ASCEND that the object is indeed a block)
  • what variables are the 'inputs' (via inline NOTES starting with 'in:')
  • what variables are the 'outputs' (via inline NOTES starting with 'out:')
  • what variables are parameters for a particular block (via inline NOTES starting with 'param:')
  • icon used to display the block in the block palette (via NOTE with lang 'icon')

Two possible approaches to canvas-based models

There were two choices of ways that canvas-based 'blocks' could be declared using the ASCEND modelling language: parametric models or non-parametric models, which are shown below:

(* parametric model *)
MODEL pipesegment(
    inlet "in:" WILL_BE stream;

    output "out:" WILL_BE stream;
    dp WILL_BE delta_pressure;

);
    inlet.h = outlet.h;
    inlet.p + dp = outlet.p;

END pipesegment;

and

(* non-parametric model *)
MODEL pipesegment;

    inlet "in:" IS_A stream;
    output "out:" IS_A stream;

    dp "param:" IS_A delta_pressure;
    inlet.h = outlet.h;

    inlet.p + dp = outlet.p;
END pipesegment;

In the first case, the pipesegment model does not contain all the necessary variables, and it is not possible to create the pipesegment object without first creating the necessary parameter models 'inlet' and 'outlet'. On the other hand, this type of model is clearer in some ways because it effectively makes a distinction between 'private' (internal) variables and 'interface' variables (the ones that define its relationship with other models).

Problem with 'ARE_THE_SAME' in parametric models

In order to make models smaller, sometimes it's desirable to use ARE_THE_SAME to wire up variables that will always be the same, for example the mass flowrate into a unit and the mass flow rate out of the unit, in the case where there is no mass holdup. There are cases where it is mathematically preferable to use '=' instead of merging two variables, but it's nice to have the flexibility to choose between these.

The problem with using parametric models, though, is that it's not possible to merge parameter variables inside a parametric model, because this is not implemented or supported in the ASCEND compiler.

Problem with 'equality loops'

When wiring up loops of variables, such as constant flow rate through a loop, one cannot use '=' relations, because the system ends up overspecified. We would need a special solver to eliminate the unnecessary constraint, eg when three items are wired up in a constant-flow-rate loop:

mdot1 = mdot2;
mdot2 = mdot3;
mdot3 = mdot1; (* this equation is a redundant *)

On the other hand, using ARE_THE_SAME eliminates this problem, and rather than an overconstrained system, we just end up with one free variable:

mdot1, mdot2 ARE_THE_SAME;
mdot2, mdot3 ARE_THE_SAME;

mdot3, mdot1 ARE_THE_SAME; (* ASCEND ignores this one, because the variables are already the same *)

Although it is possible to design a solver such that the redundant equations are ignored, it was considered (by John) to perhaps be 'safer' modelling practise to require the user to specify a 'square' system without the solver having to ignore particular equations. So the ARE_THE_SAME system of wiring up models is currently preferred, but perhaps in future we will want to revisit this.

Parameters for blocks

In creating a canvas-based model, it is certainly not enough just to 'wire up' blocks are then expect the resulting model to do something useful. Most blocks will need to have some parameters set. For example, a pump may need to have its flow-rate or output pressure specified; a boiler will need to have its fuel supply rate and efficiency specified. Some blocks will require many parameters. It is also possible that blocks may need to be able to be used in multiple 'modes', for example a pump may have its efficiency and speed specified, or perhaps its efficiency and volumetric flow rate.


Currently, the parameters that a block provides can be marked up with NOTES as shown

MODEL pump;
    eta "param:isentropic efficiency" IS_A fraction;

    f "param:rotational speed" IS_A inverse_time;
    (* ... *)
END pump;

In these cases, the canvas GUI can then specify (FIX and set) these variables in the resulting auto-generated MODEL. We still need to provide a GUI to allow the user to set and adjust these variables. It remains to determine what should happen in the case of multiple parameter 'modes' for a model.

Pickling to load/save canvasses

We are using the Python 'pickle' module to save and load canvas data. The models are loaded and saved divorced from and corresponding ASCEND instance data, in other words, they are a recipe for how to build up the ASCEND model, rather than a saved copy of the model itself. When the canvas is loaded, each block is 'reconnected' to the appropriate ASCEND objects, such as TypeDescription objects for the MODEL which each Block represents.

When we get further along, the pickles will also need to include the values of user-set parameters, and perhaps optionally the current solved value of the model, so that different canvases can be saved with different scenarios recorded therein.

What do do about connections?

When connecting two blocks together, up to now we have simply been providing ARE_THE_SAME statements to merge the two connector port sub-models. This approach is not fully general, and still leaves the possibility of equality loops in the model, for the case where, within the blocks', "=" statements are used to express equality between, for example, input and output flowrates in a block.


To allow dynamic models to be represented on the canvas, we can't expect to use ARE_THE_SAME statements within blocks, because that would eliminate the possibility of modelling mass holdups. So, we need to think about using another kind of model generation that fixes this problem relating to the degrees of freedom in models generated from the canvas GUI. We're still discussing a solution.

Zoom and Pan

The canvas currently supports some mouse-friendly zoom and pan manipulations:

  • Zoom using ctrl+wheel
  • Pan using middle-button drag
  • Zoom using ctrl+middle-button drag (up and down) (this option is provided for easier use on laptops with trackpads)
  • Pan up and down using mouse wheel.

We still need to implement support for zoom-to-fit in the GUI.

Next steps

It remains to implement:

  • what do do where there is are multiple 'parameter modes' for the model.
  • what to do about 'constants' in the model, eg when specifying a fluid, what fluid is to be used? when specifying a data reader block, what is the input file?
  • customisable appearance of the block (perhaps via a NOTE of lang 'graphic', which might point to an SVG file, for example?)
  • an alternative way of specifying inputs and outputs that doesn't use inline NOTES (eg a NOTE lang named 'in', perhaps? Would allow inputs to be declared elsewhere from the model itself.
  • type-checking to ensure that only compatible ports are connected (this is not straightforward, could be done by exporting to the compiler and checking for error messages?)

Loading, saving and solving canvas-based models

The above contains the minimal data required for the Library to be parsed and a 'palette' of available model 'blocks' presented to the user. The next question is: how should the resulting model be loaded, saved, and solved?

For the canvas-based model the essential data structures would contain

  • list of modules used in the canvas
  • list of blocks
    • type of each block
    • size and position of the block on the canvas
    • name of the block
    • block styling: colour, style?
    • type of each block
    • size and position of the block on the canvas
    • name of the block
    • block styling: colour, style?
  • type of each block
  • size and position of the block on the canvas
  • name of the block
  • block styling: colour, style?
  • list of connections
    • starting block and port (as blockname.portname)
    • ending block and port
    • line routing (a series of x,y points)
    • line styling: colour, width, etc?
    • starting block and port (as blockname.portname)
    • ending block and port
    • line routing (a series of x,y points)
    • line styling: colour, width, etc?
  • starting block and port (as blockname.portname)
  • ending block and port
  • line routing (a series of x,y points)
  • line styling: colour, width, etc?
  • list of other canvas objects
    • text labels, headings etc
    • possible 'output cells' that report current values of variables in the model
    • possible 'method buttons' that run methods on the model.
    • possible 'input cells' that allow variable values to be specified
    • text labels, headings etc
    • possible 'output cells' that report current values of variables in the model
    • possible 'method buttons' that run methods on the model.
    • possible 'input cells' that allow variable values to be specified
  • text labels, headings etc
  • possible 'output cells' that report current values of variables in the model
  • possible 'method buttons' that run methods on the model.
  • possible 'input cells' that allow variable values to be specified
  • list of parameter values
    • block to which the parameter blocks (maybe recursive).
    • value of the parameter.
    • units in which parameter has been specified.
    • block to which the parameter blocks (maybe recursive).
    • value of the parameter.
    • units in which parameter has been specified.
  • block to which the parameter blocks (maybe recursive).
  • value of the parameter.
  • units in which parameter has been specified.

With the above information it would be possible to save and reload a canvas-based model without loss of information, but note that the model would still have to be solved; it would not be saving the full state. It would have to be assumed that the model library upon which the canvas was based had not changed; certain debugging would be required to ensure that such a situation didn't result in a corrupted canvas-model. The above data might possibly be stored in a new file format '.a4g' specific to canvas-based modelling. (We could see the '.a4g' format as a sub-type of the '.a4c' format (in which case the above metadata might be embedded in NOTES, or similar?)

To solve a model with the above information, the approach would depend on whether the parametric or non-parametric modelling style is chosen.

For the parametric style, we would need to create definitions of the different streams before the blocks could be declared. This could lead to some ambiguity in the way that different streams are initialised, possible.

For the non-parametric style, we would only need to declare the blocks using IS_A, then declare the connections using ARE_THE_SAME. We would want to have some sort of hierarchical system of METHODS to ensure that the necessary variables were FIXed and initialised for each block in the model (similar to the proposed 'default_self' recursion that is currently a bit broken).

There need to be some protocols about how the model would be intialised, in any case.

It should be possible to re-solve the model after the user changes a value of a parameter. This would mean that canvas-gui would have exported the model to the solver, and would then know how its blocks corresponded to variables in the model, and would call var_fixed or similar routines via the 'libascend' API.

It appears that the right approach would be for the canvas-based GUI to generate a string or file-based representation of the model it has in member, using the above hierarchy of objects (modules, blocks, connectors, but not parameter values), then to instruct ASCEND to load that model, then to FIX and set the values of the parameters, then attempt to 'solve' the model. This might be done in cooperation with the existing PyGTK GUI, or might be done directly with calls to the libascend engine. the canvas-based GUI would have no awareness of the variables it contained other than that provided by the connector end-point names and the parameter names. Using those names it would be able to get/set values in the simulation.

It would be possible to avoid the need for the canvas GUI to be able to FIX or FREE variables in the model. When a 'source' block was required, it could be set up as a new 'block' and the necessary un-FIXed variables left as parameters to the model. Changing the source block to another type would allow other parameters to be fixed. When greater flexibility was required, the user could switch to the non-canvas GUI.

Design considerations

Do connections have properties and variables that need to be accessible, or is it always OK to access those variables/values via the connected blocks at either end?

What to do with the standard METHODS like Specify, Default self, Values?

How can we store the current state of the model? Should we try to do that?

How do we keep track of the fixed/free variables? Do we need to?

What about clashes between the Values methods in different submodels setting values on their 'connection' members. Is this the corner-case that makes the parametric approach more desirable? Or does that not actually solve the problem?

How can the canvas modeller determine when a model is 'complete'?

Is it OK to leave dangling lines?

Is it OK to leave unconnected 'ports' on the sub-models?

Is it better to use '=' relationships instead of ARE_THE_SAME? It seems that in some cases this leads to models that solve more reliably.

Line routing is tricky and something that is not often done well. Could use some cool calligraphic code to make this work better

Could it be possible to highlight parts of the flowsheet corresponding to solved and un-solved portions of the model?

Software

The canvas-based model of fREEDA, an electrical circuit simulator. This canvas is based on Qt.
Screenshot from Gaphor a UML tool. This canvas is pure Python on top of Cairo.

Programs that implement these types of GUIs include

  • Gaphor, written in pure python, uses Gaphas on top of Cairo.
  • fREEDA (screenshot) (FOSS, Windows-based, uses a Qt canvas widget)
  • gEDA's schematic editor, gschem is a GPLed GTK+ application that appears to use its own bespoke canvas implementation (screenshots). In a very quick and superficial test-drive, it appeared to be a little clumsy to use -- hard to identify when a connection has successfully been made, and fairly dated bitmap-style graphics.
  • kicad is another free electronic circuit schematic editor.
  • XCircuit is a Tcl/Tk-based circuit editor.
  • TRNSYS, Simulink, Visio, Dia, OOo Draw
  • Kepler (nice multi-domain canvas-based simulation, in Java)
  • COCO simulator (windows, closed-source freeward)
  • Conduit (uses goocanvas)
  • Dia (seems to use GnomeCanvas?)
  • GStreamer Editor (uses GnomeCanvas)
  • DWSIM (sequential-modular simulator implemented in VB.net)
  • Dunnart (testbed for libavoid)
  • Inkscape (apparently includes support for libavoid, for automatic connector routing)
  • JMCAD (java-based tool, seems to focus on nonlinear control)
  • FlowDesigner (used Qt, written in C++)
  • GNU Radio Companion ([http://www.joshknows.com/grc#screenshots screenshots here) Allows connection of signal processing pipelines for software radio projects. Has dependencies on wxGTK and also QT, not sure how its GUI works.

Software libraries that could be used for this functionality (most are discuss here):

  • Gaphas (uses only Cairo, via pure Python)
  • GooCanvas (uses Cairo inside)
  • GnomeCanvas (not sure if it's deprecated yet or not, uses libart inside)
  • libccc/CriaCanvas (uses Cairo inside)
  • Cairo (directly)
  • SoGTK (use the OpenInventor 3D scenegraph for 2D work here?)
  • Papyrus
  • libavoid (automatic connector-line routing including obstacle avoidance)

... it needs to be something that works within GTK and perhaps also PyGTK. ... we want to choose something that will be around for a while. The only canvas widgets from the above that is in Ubuntu and linked against are GooCanvas and GnomeCanvas, with GnomeCanvas being much more used than GooCanvas. libccc is available in Ubuntu but noone is linking against it yet (as of Hardy release)