home
download
class models
standard gui
custom gui
models
user guide
problems
 

Catacomb scripting with JPython

Catacomb does not need its own scripting language because JPython provides all the usual scripting facilities and a great deal more. JPython is the Java version of Python, a versatile, high-level, widely used and well supported object oriented language (and free).

Getting Started

If you have java installed, installing JPython is straightforward following the instructions on their Web site. A typical UNIX installation will put the jpython startup script in /usr/local/JPython-1.1/ so it is convenient to add this directory to your PATH environment variable.

To give jpython access to catacomb, the file ccmb.jar must be in the CLASSPATH variable when you start jpython. Supposing for example the file is in /home/rcc/, then using bash you would need:
      CLASSPATH="/home/rcc/ccmb.jar"; export CLASSPATH
using tcsh it would be
      setenv CLASSPATH /home/rcc/ccmb.jar
Then, when you execute jpython it should print a message like:
packageManager: processing modified jar, "/home/rcc/ccmb.jar"
This will only appear the first time after you update the jar file.

Running JPython

There are three complementary ways to run JPython with catacomb. _STARUL

  • Catacomb can start a JPython interpreter to give interactive access to the JPython command line

  • A JPython script can instantiate catacomb

  • Catacomb can load JPython modules and run them as though they were methods of catacomb objects

    Interactive examples

    To start the JPython interpreter, edit the CcmbPython object. First hold hte right mouse button on the system object and select show children. Then click python. This brings up a small window. From the CcmbPython menu select activate terminal. You can now type commands to the JPython interpreter just as if you were running JPython alone, except that you have access to Catacomb components too. First of all, to gain access to the Catacomb system

    Sys = catacomb.Sys

    We can now call methods (functions) in the catacomb object:

    col = Sys.getObjectByName("extcolor")

    Tells catacomb to find the object called "extcolor" and put it in the variable col. Then for example,

    Sys.editObject(col)

    brings up the color editor. To instantiate a new framework for one of the models you can call the newObject method with the name of the framework as under the objects menu. For example,

    Sys.newObject("Numerics")

    makes a new numerics object for a simple differential equations.

    Before manipulating components of the model, you must find them within catacomb, as you would with the tree display in the main window. The easiest way to do this from a script is with getObjectByName as in

    calc = Sys.getObjectByName("diffeq")

    As before, to get a visual editor for this object:

    Sys.editObject(calc)

    Now, you have a display of all the variables in the differential equation calculation object. Python can tell you what they are:

    calc.B

    just prints the value of parameter B in the object called calc. Python can also change them:

    calc.B=0.5

    To rerun the calculation change xmax with the slider a little. Now, the value B=0.5 is being used in the calculation (it gives rather bigger wiggles in the solution), but catacomb hasn't noticed that it has changed. Often this isn't a problem if you are running everything from python, but when you want the graphical side to keep up to data, you should call the function sync() on an object that has had its variables changed some way other than by catacomb itself.

    calc.sync()

    Now move the mouse over the B slider: when the mouse arrives the slider checks that it has the latest value, finds that it doesn't and catches up. (it would be easy to make catacomb do the sync() itself and update all the sliders immediately, but this is a deliberate economy to avoid to much unnecessary rattling around of every tiny change).

    The last key command for using the user interface with jpython is flagDataChange. This tells catacomb that the data has changed and that it should recompute anything necessary.

    calc.B=0.1

    calc.flagDataChange()

    The graph should update to show the results of the calculation with the new parameter.

    Starting Catacomb from JPython

    By starting JPython first you can run Catacomb in batch mode without having to use the user interface at all. Ensure that the jar file or the catacomb directory containing the classs files is in the CLASSPATH. Then

    import catacomb

    gives access to object in the Catacomb package.

    Sys = catacomb.Sys

    makes a shortcut to the Sys object which contains the most convenient methods for accessing Catacomb components. However, you still have to start Catacomb itself:

    Sys.init()

    And you must specify an interactor object for it to communicate with the user:

    Sys.interactor = catacomb.GuiInteractor()

    You can then use all the comands demonstrated in the previous section in just the same way. Normally, python code is packaged in modules. The following example shows a short python module demo1.py. To run it, restart jpython and type:

    from demo1 import *

    numerics()

    The first command imports the demo1 module and instantiates the ccmb variable. The second runs the method numerics from that module. In addition to the functions above, it uses the sleep method from the class java.lang.Thread to put in some delays, and flagViewChange to tell the display that the data hasn't changed but the preferred view has, so it redraws the same data with the new axes. Something else to note about this module is that while Python statements looks lot like Java or c, there are no curly brackets. The block structure is derived exclusively from the indentation, so the blank spaces at the beginning of lines are not just a matter of taste: they define where blocks begin and end. The script won't run properly if the indentation is wrong.

    
    import catacomb
    from java.lang import Thread
    
    csys = catacomb.Sys
    csys.init()
    csys.setInteractor(catacomb.GuiInteractor())
    
    def numerics():
        csys.newObject("Numerics")
        diffeq = csys.getObjectByName("diffeq")
        csys.editObject(diffeq)
        diffeq.B
        for i in range (10): 	
           diffeq.B = 1. / (1 + i)
           diffeq.sync()
           diffeq.flagDataChange()		 
           Thread.sleep(200)
    
        res = csys.getObjectByName("diffeq-results")
        res.x_max
        for i in range (100): 	
              res.x_max = abs (0.2 * (i-50))
              res.flagViewChange ()
              Thread.sleep(10)
    
    
    

    Batch mode scripting

    The previous example showed how to use Python to control Catacomb with the calculations still handled by Catacomb's own event propagation and updating machinery. This is designed for use with the user interface and uses different threads to run models, update displays and respond to user actions. When running from a script it is often better to stick to a strictly sequential structure so you know exactly what parameters have been used in each calculation. The command

    Sys.setBatchMode()

    switches catacomb into batch mode, stopping all its own rerunning and updating. To switch it back on use setInteractiveMode() or, equivalently setBatchMode(1) to switch it on and setBatchMode(0) to switch it off.

    If you read the catacomb source to find methods to call from python, you will see that setBatchMode is defined with a boolean argument. When you call methods from python you don't have to worry too much about variable types: python will try to coerce them into what is needed, so, for example, here 0 means false, and 1 means true.

    The next example demo2.py uses batch mode without the graphical interface. It also needs the model iafdemo.ccm. To run it, restart jpython and type:

    from demo2 import *

    iafdemo()

    The module loads the file, extracts references to various objects, then repeatedly changes a parameter, reruns the calculation, and extracts some values from the results. The nBetween(v, a, b) function counts the number of spikes in the vector v between times a and b. The batchRun() command simply runs the calculation defined in the iafCalc object in the current thread. The script carries on when the calculation returns.

    
    import catacomb
    from string import ljust
    
    csys = catacomb.Sys
    csys.init()
    csys.setInteractor(catacomb.GuiInteractor())
    
    
    def iafdemo():
        csys.setBatchMode()	
        csys.loadFile("iafdemo.ccm")
        	
        calc = csys.getObjectByName("iafcalc")
        spikes = csys.getObjectByName("spikes")
        wf = csys.getObjectByName("wavefo_0")
    
        f = open('batchresults.txt','w')	
    
        for i in range(50):
           yin = -20 + 1.5 * i
           wf.movePoint(2, 0, yin) 	 
    
           calc.batchRun()	
         
           tspk = spikes.getSpikes()
           n23 = nBetween(tspk23, 30, 60)
           n24 = nBetween(tspk24, 30, 60)
        
           print 'stim, n23, n24:   ' + `yin` + ' ' + `n23` + ' ' + `n24`
           f.write(ljust(`yin`, 12))
           f.write(ljust(`n23`, 12))
           f.write(ljust(`n24`, 12))
           f.write('\n')
        f.close()
    
    def nBetween (a, t1, t2):
        n = 0
        for x in a:
            if x > t1 and x < t2:
                 n = n + 1
        return n
    
    
    

    Running Python modules from Catacomb objects

    The third way to run JPython scripts is by wrapping them in a CcmbPyModule object. To do this, just use the load module option from the CcmbPthon object. Each module is given a new CcmbPyModule object which has a data display exclusively for that module. Moreover, the CcmbPyModule object, being a normal Catacomb object can be instructed to listen for changes in the curremt model and rerun the Python code whenever anything is modified. The file demo3.py includes a simple python module that can be loaded this way. It instantiates the Numerics object when loaded, and declares two methods, method1 and method2 which can be called from the user interface. They do is copy one line of the normal output of the calculation to the modules own display, and register themselves as listener to the original results. Thereafter, whenever the results are updated, the module reruns and updates its own display.

    Conclusion

    The commands used here for setting variables and running calculations are sufficient for many scripting applications. The names of public variables within catacomb components can be found through the user interface or with the "show fields" option on the model browser window. However, where objects contain array fields, these are not accessible by default from the standard user interface: there are special components to control their values, as for example in the ProfileEditor where the Profile is defined internally as two double[][] arrays and two int[][] arrays. You can manipulate these arrays directly from JPython but to do so requires an understanding of how they define the Profile. It is easier and more reliable to use public methods in the containing class to set its properties, just as the user interface does when you move points around. All public methods are documented in the javadoc documentation which can be found at _EXTLINK(www.compneuro.org/catacomb, www.compneuro.org).