Skip to content

Scripting

John Holt edited this page Jun 8, 2017 · 76 revisions

Scripting

Scripting in IBEX is done using genie_python. The genie_python reference manual gives a full account of what functions are available in genie_python. This page is intended to give a broad guide to scripting for the beginner and novice user.

There is also some specific scripting advice for the Muon Front End

If you are new to Python, the Mantid team has created an excellent Introduction to Python on the Mantid web-site.

Running genie_python commands

When running genie_python from an interactive console such as from the GUI or after running C:\Instrument\Apps\Python\genie_python.bat, the genie module will be aliased to g. Genie commands can then be accessed by using the prefix g.[COMMAND_NAME]. For example:

g.start()
g.cset("BLOCK_1",1)
g.abort()

This is particularly useful from the GUI which will auto-complete commands and provide tool tips describing each function and its arguments.

Note that in many cases, arguments will be optional. For instance, begin can be used as g.begin() despite supporting all of the arguments period, meas_id, meas_type, meas_subid, sample_id, delayed, quiet, paused, and verbose.

Running in simulation mode

Genie_python can be used in simulation mode. Simulation mode will allow you to run scripts but without changing items on your instrument. It does this by putting dummy commands in for some genie command, it also creates dummy blocks on the fly so that block values can be read and written to. NB This means that simulation can not be this will not validate your block names within the script. To run in simulation mode use the genie_python_simulate.bat found in C:\Instrument\Apps\Python.

Common genie_python commands

Many genie_python commands share the same name with their Open Genie equivalent so it will often be very straightforward to find the function you're looking for. Still, here is a list of the most commonly used genie_python commands. This is not a complete list. For full information, consult the genie_python reference manual.

Starting and stopping a run

Command Description Example
begin Starts a new run g.begin()
end Ends the current run g.end()
abort Aborts the current run g.abort()
pause Pauses the current run g.pause()
resume Resumes the current run after it has been paused g.resume()

Updating blocks and PVs

Command Description Example
cget Gets the useful values associated with a block g.cget("NEW_BLOCK")
cset Sets the setpoint and runcontrol settings for blocks g.cset("NEW_BLOCK",1)
get_pv Gets the value for the specified PV g.get_pv("IN:INSTNAME:IOC_01:STAT")
set_pv Sets the value for the specified PV g.set_pv("IN:INSTNAME:IOC_01:STAT",1)

Run control

Command Description Example
get_uamps Gets the current number of micro-amp hours g.get_uamps()
get_frames Gets the current number of good frames g.get_frames()
get_runstate Gets the current status of the instrument as a string g.get_runstate()
get_mevents Gets the total counts for all the detectors g.get_mevents()
get_totalcounts Gets the total counts for the current run g.get_totalcounts()
waitfor Interrupts execution until certain conditions are met g.waitfor("NEW_BLOCK",value=1)
waitfor_block Interrupts execution until block reaches specific value g.waitfor_block("NEW_BLOCK",1)
waitfor_time Interrupts execution for a specified amount of time g.waitfor_time(1)
waitfor_frames Interrupts execution to wait for number of total good frames to reach parameter value g.waitfor_frames(1000)
waitfor_uamps Interrupts execution to wait for a specific total charge g.waitfor_uamps(9.9)
waitfor_runstate Waits for a particular instrument run state g.waitfor_runstate("paused")
waitfor_move Waits for all motion or specific motion to complete g.waitfor_move("NEW_BLOCK")

Converting Open Genie to genie_python

If you know a little bit of Python already, converting Open Genie scripts to genie_python is very often straightforward. Even if you're new to Python, once you know a few simple pieces of syntax, you'll be able to convert many scripts without issue.

Note: If you are new to Python, consult the Introduction to Python on the Mantid web-site.

List of functions

We've included some common genie_python commands on this page, but for a full list refer to the genie_python reference manual.

Indentation

One thing where there is no direct comparison between Python and Open Genie is with indentation. In Python, different code blocks are identified by their indentation level. In many programming languages, code blocks are encased in curly braces ({...}), but Python uses indentation. For example:

print "No indent"
def my_func():
    print "1 indent. Inside the function"
    if True:
        print "2 indents. Inside the if clause"
        print "2 indents. Still inside the if clause"
    print "1 indent. In the function but not the if clause"
print "No indent. Outside the function"

Typically an indent is 3 or 4 spaces. Tabs can be used too, but Python won't recognise that a tab is the same as 4 spaces so can lead to problems. It's often best to avoid them. This might be a new concept if you've only ever written Open Genie, but trust us in that it opens up a lot of ways to make scripts more powerful, easier to read and more reliable.

Side-by-side comparison

Open Genie syntax genie_python syntax Comments
PROCEDURE my_func def my_func(): This is the standard way to define a function in Python
begin g.begin() Many functions in genie_python have the same name as in Open Genie. In these cases, prepend g. to idicate the method belongs to genie_python and append () to tell Python to execute the method with no arguments
my_func_2 100 my_func_2(100) To execute a function, give its name and then the argument in brackets. This is a user-defined function, not a genie_python function, so it has no g. in front.
my_func_3 100 200 my_func_2(100,200) As above, except that multiple arguments are separated by a comma
waitfor seconds=20 g.waitfor(seconds=20) Some methods can take named arguments. In these cases you simply give named arguments in the form [name]=[value]. Note that you can mix named and unnamed arguments, but unnamed arguments always appear at the start of the argument list. This section gives a more detailed description
cset/nocontrol MY_BLOCK=value g.cset('MY_BLOCK',value,runcontrol=False) Some Open Genie commands take optional arguments. The same principle applies as in the above example.
# This is a comment # This is a comment Comments are the same in both languages

Creating and running instrument scripts

Different instruments may have specific scripts that are required for multiple users. To make accessing these easier, genie_python loads instrument scripts at startup.

Creating scripts

For the most part, this is the same as creating-user-scripts.

Script directory

Instrument scripts should be placed in C:\Instrument\Settings\config\[MACHINE_NAME]\Python\inst.

Script structure

Everything in the instrument scripts will be executed when genie_python is started. This includes - Making functions available to be called later - Setting variables - Running methods. Typically, most code in instrument scripts will be contained within functions (procedures in Open Genie language), but it's important to be aware that anything that isn't will be included too. For example, if an instrument script is loaded:

a = 1
def print_a():
    print str(a)

then the user can call print_a, but they can also use the variable a, and change its value, which may not be desirable. If instead the script was:

def print_a():
    a = 1
    print str(a)

then the user could only access print_a() and the value could not be changed.

To reload the inst module, for instance if you had updated the script or added a new script, you should issue the command reload(inst).

Running

Please note: When you are running scripts, the script is being executed on the instrument control PC. This means that the script itself has to be stored on the instrument control PC. It also means that any path names in your scripts refer to locations on the instrument control PC (unless you are referring to network drives).

Once the script is loaded, anything from the script will be available using the inst package reference. For instance if your script contains the function my_function you can call:

inst.my_function()

Whilst using the scripting perspective in the Ibex GUI, users will also benefit from the same auto-complete feature as they do with genie_python commands.

Creating and running user scripts

Creating scripts

  1. First, we need to create a script file. By default, user scripts should be placed in C:\scripts. Navigate to your desired directory and create the script file with extension .py.
  2. Write some genie_python!
  3. Save the file

We have glossed over step 2 because Python is a very powerful scripting language. Combined with Open Genie, the potential scope of a script is enormous, and well beyond the scope of this guide. For example though, here is a simple script that executes a calibration run.

# Change the title
calibration_run_title = "Calibration run 1, 29th September"
g.change(title=calibration_run_title)

# Begin the run
print "Beginning calibration run : " + calibration_run_title
g.begin()

# Wait for 100 uamps
g.waitfor(uamps=100)

# End the run
g.end()
print "Calibration run finished successfully"

Running

Please note: When you are running scripts, the script is being executed on the instrument control PC. This means that the script itself has to be stored on the instrument control PC. It also means that any path names in your scripts refer to locations on the instrument control PC (unless you are referring to network drives).

Once you've created your script, it's time to run it. There are a number of ways of launching a Python script.

From Ibex

  1. Launch the Ibex GUI
  2. Navigate to the scripting perspective
  3. Run the command g.load_script("C:\path\to\script\my_script.py") where the path and script name are updated appropriately
    • Note that if you omit the absolute path to the file (i.e. C:\path\to\script) then genie_python will look in the current script directory. By default this is C:\scripts but can be viewed and set with the commands g.get_script_dir() and g.set_script_dir() respectively.
  4. When the script is loaded, any procedures in the script will be run automatically. If the script contains any function, you will now be able to call them from within the scripting window.

From a genie_python terminal

  1. Launch a genie_python terminal from C:\Instrument\Apps\Python\ by running genie_python.bat
  2. Follow the above starting at step 3.

Tips from the developers

Even with correct syntax, a working script can become bug-prone and difficult for users to update. Here we document some tips to help keep your scripts working as expected and easy to modify in the future.

Argument ordering

As this is Python, genie_python conforms to the standard pattern of calling Python functions. The arguments to the function are contained within brackets and the variables passed in as a comma-separated list. Ordering is important but can be overridden by using named variables, for instance the following are all correct and equivalent:

g.change_beamline_pars("PAR1",1)
g.change_beamline_pars(name="PAR1",value=1)
g.change_beamline_pars(value=1,name="PAR1")
g.change_beamline_pars("PAR1",value=1)

In the last example, named and unnamed variables are mixed. Unnamed variables must precede named variables. The following examples are not valid

g.change_beamline_pars(name="PAR1",1) # Named variable before unnamed
g.change_beamline_pars(1,"PAR1") # Cannot change order of unnamed variables

Using named variables can be very useful in avoiding mistakes. For instance, getting the order of high and low limits the wrong way round. For instance this example:

g.change_monitor(1,10,0)

is wrong and wouldn't work. Instead, we could have written:

g.change_monitor(1,high=10,low=0)

which would have worked and makes it clear for whoever comes to edit the code in future (hint: that person might be you!).

Making Errors Standout in Python Console

Python console will display most output in black this is from the standard out but will display error information in red. Error information should be written to standard error using sys.stderr.write("errorn").

Some examples

Sequentially sets a position (e.g. sample changer) and waits for a fixed number of uamps

from genie_python import genie as g

def loop_over_samples(block_name, position_names, charge_to_wait_for):
    """
    Begins a run. Loops over a set of sample positions. Ends the run

    :param block_name: The name of the block containing the sample position
    :param position_names: A collection of the positions to loop over
    :param uamps_to_wait_for: The number of uamps to wait for between samples
    """
    g.begin()
    total_charge_to_wait_for = 0
    for position_name in position_names:
        print "Moving sample changer to position: {0}".format(position_name)
        g.cset(block_name, position_name)
        total_charge_to_wait_for += charge_to_wait_for
        g.waitfor_uamps(total_charge_to_wait_for)
    g.end()

Reads the value of a block and moves it through a series of intervals to a new values taking a fixed time

from genie_python import genie as g

def ramp(time, block_name, steps=100, final_value=0.0):
    """
    Moves a block between an initial and target value over a sequence of steps, waiting a fixed time at each step.

    :param time: The overall time to take to ramp. Time will be split evenly between steps
    :param nsteps: The number of steps to take during the ramp
    :param block_name: The name of the block whose value to set
    :param final_value: The value we want to ramp to
    """

    initial_value = g.cget(block_name)['value']
    if initial_value is None:
        print "Unable to determine temperature from block {0}. No action will be taken".format(block_name)
        return

    def values_match(v1, v2):
        tolerance = 0.001
        small = 1.0e-20
        return 2*abs(v1-v2)/(abs(v1)+abs(v2)+small) < tolerance

    step_duration = time/steps
    for i in range(1, steps+1):
        step_value = initial_value + (final_value - initial_value)/steps*i
        g.cset(block_name, step_value)
        print "Ramping to: {0} Target value: {1}".format(step_value, final_value)
        g.waitfor_time(seconds=step_duration)
        new_block_value = g.cget(block_name)['value']
        if not values_match(new_block_value, step_value):
            print "WARNING: The current value for block {0}, {1}, does not match the target value of {2}".format(block_name, new_block_value, step_value)

    print "Ramp complete"
Clone this wiki locally