Python Scripting Cookbook

Table of Contents

1. Introduction

Using scripting with IronPython within UnityMol is a powerful approach to automate and simplify common workflows. Many things are very simple to achieve, when one knows the proper syntax or method to do them. This cookbook aims to collect useful patterns to facilitate such work.

2. Using recipes from UnityMolCommons.py

Since version beta-VR-1.1.3-208-gdb0618c98, UnityMol ships with a python library that provides a few useful functions. To use it in your python script, put this in the header:

import sys
sys.path.append(Application.streamingAssetsPath)
from UnityMolCommons import *

Alternatively, you can start UnityMol with the -c or -commons option, which will execute these commands for you by calling loadPythonCommonsModule().

Currently, the following functions are available:

2.1. q() to exit UnityMol

Not much to explain here. q() followed by enter will call Application.Quit() for you and is just much quicker to type.

2.2. info() for general information about build and execution

With this command you get a printout of quite some useful information. Simply do:

info()

Below, see an example output:

>>>
Below information about version and build

>>>  Version:beta-VR-1.1.3-208-gdb0618c98 
>>>  Built for architecture:Non-fat file: /Users/Shared/umol-dev-mb.app/Contents/MacOS/UnityMol is architecture: arm64 
>>>  Executable Info:/Users/Shared/umol-dev-mb.app/Contents/MacOS/UnityMol: Mach-O 64-bit executable arm64 
>>>  Platform: 
>>>OSXPlayer 
>>>  productName: UnityMol 
>>>  companyName: CNRS-IBPC 
>>>  unityVersion: 2021.3.45f1 
>>>  operatingSystem: Mac OS X 15.3.1 
>>>  deviceModel: Mac16,5 
>>>  Processor Type: Apple M4 Max 
>>>  OS Architecture: Mac OS X 15.3.1 
>>>  UnityMol version:beta-VR-1.1.3-208-gdb0618c98  

2.3. All() for using a selection with all atoms

You can use All() either without any argument, then it applies to the last() structure that was loaded, or you can specify a structure reference obtained from e.g. s=load(..) to then use as in All(s). An example can be to hide all hydrogens:

showHideHydrogensInSelection(All())

Do not mix up All() as defined here with all(), which is a Python builtin function.

2.4. setTrajFramerate(fr) to set trajectory playback speed

You can use setTrajFramerate(fr) to set the framerate fr for trajectory playback. Maximum value is 25.0. It can be used either with fr as a single argument, then it applies to the last() structure that was loaded - assuming there is trajectory data for it -, or you can specify a structure reference obtained from e.g. s=load(..) to then use as in setTrajFramerate(s,fr). A minimal example below:

s=load("my.pdb")
loadTraj(s.name, "my.xtc")
setTrajFramerate(25.0)

2.5. nameRes(sel) to retrieve residue names in a selection

Either provide a selection object as an argument, or no argument to create a temporary selection of all. Use as follows:

  fetch("1kx2")
  print(nameRes())
[Log] Selection of 1249 atoms, named 'tempSel' (all) 
>>>['ALA', 'ASP', 'LEU', 'GLN', 'GLU', 'ILE', 'TYR', 'ASN', 'LYS', 'CYS', 'THR', 'VAL', 'HIS', 'SER', 'MET', 'GLY', 'PRO', 'TRP', 'ARG', 'PHE', 'HEC']

2.6. Low level functions

2.6.1. run_command(cmd)

This command is used by info() to run a system command via bash shell. This is useful to access system information, for example about the machine one is running on:

i=run_command("uname -a")
print(i)
>>>Darwin neo.lan 24.3.0 Darwin Kernel Version 24.3.0: Thu Jan  2 20:24:22 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T6041 arm64  

3. Common patterns

3.1. Iterating

3.1.1. Over atoms in a selection

In the example below, we select all carbon alpha atoms and then print out their temperature B-factor values. No graphical representation is displayed.

s=fetch("1crn", showDefaultRep=False)
ca=select("name CA","ca")
for atom1 in ca.atoms:
      print(atom1.bfactor)

3.1.2. Dynamic double atoms loop

Loop over all carbon alpha atoms within 4.5 angstrom distance using a dynamic selection in a double loop, then annotate these distances. Not very useful per se, but illustrates the feature.

s=fetch("1crn", showDefaultRep=False)
showSelection("all_1crn", "trace")
ca=select("name CA","ca")
for atom1 in ca.atoms:
    tmp = select("name CA and within 4.5 atomid "+str(atom1.index+1),createSelection=False)
    for atom2 in tmp.atoms:
        if atom1 != atom2:
            annotateDistance(s.name,atom1.index+1,s.name,atom2.index+1)

3.1.3. Over all residues in all chains

In a double loop, we go over all chains in a model, and for each chain we iterate over all residues. We print summary information on number of residues per chain and number of atoms per residue.

s=fetch("1crn", showDefaultRep=False)
for chain in s.currentModel.GetChains():
    print("Chain with "+str(chain.residues.Count)+" residues encountered.")
    for residue in chain.residues:
        print("Residue with "+str(residue.atoms.Count)+" atoms encountered.")

4. Remote-controlling UnityMol

4.1. Using the built-in ZMQ server

4.1.1. General information

UnityMol can be remote controlled through a ZeroMQ (ZMQ) network connection. ZeroMQ is a good choice if you need fast, asynchronous communication between your Python applications and are willing to worry about the details of messaging protocols.

More information:

To manage the internal UnityMol server, you can activate it via

activateExternalCommands()

and shut it down via

disableExternalCommands()

4.1.2. Writing a python script to control UnityMol via ZMQ

Below is a simple and minimalistic skeleton to remote-control the ZMQ server we started in UnityMol from an external python script:

import zmq
socket = zmq.Context.instance().socket(zmq.REQ)
socket.connect("tcp://localhost:5555")
socket.send_string("fetch('1kx2')")

By default the server runs on port 5555 on the local machine localhost accessible via the tcp network protocol. We open a socket for communication, then we send the command to fetch the PDB-id 1kx2 from the PDB, which will be displayed with default settings. In this way we can feed UnityMol any commands it typically reads from a command python script.

4.1.3. Design a custom menu to drive UnityMol

In this github repository of a recent paper you will find the menu-spike1.py script that shows a more advanced usage of the above features, designing a custom menu to control a UnityMol instance. Below a screenshot illustrating the resulting menu.

Full Image

4.1.4. Get command results from UnityMol to use in your script

A minimal example below to obtain a list of the currently defined selections in UnityMol. I the latest UnityMol version the ZMQ exchange now returns a json payload that contains three fields, success, a boolean about success or failure of the command execution, result, the actual result of the command as string, and stdout, the eventual stdout or log output as another string. Here we only use the result string:

import zmq
import json

socket = zmq.Context.instance().socket(zmq.REQ)
socket.connect("tcp://localhost:5555")

socket.send_string("getSelectionListString()")
result = json.loads(socket.recv().decode())["result"]
print(*[s.strip() for s in result.strip("[]").split(",") if s.strip()], sep="\n")

First, we establish a connection, then we get the result of the command getSelectionListString(), parse it to remove [,=]= and , characters, separating each selection name, and print these actual names for each selection.

4.1.5. More advanced usage

A nice example is the implementation of a minimal python console to drive UnityMol outside the program. This was implemented in python with the ZMQ protocol. You can obtain the file by cloning the ScriptCollection git repository, or directly access the python-console.py script online. You will need the unitymol_zmq.py helper script to handle zmq connections, it is easily re-usable. With this script it is very easy to establish a connection and send commands. Here is boiler-plate code:

import unitymol_zmq
if unitymol_zmq.unitymol is None:
    unitymol_zmq.unitymol = unitymol_zmq.UnityMolZMQ()
    unitymol_zmq.unitymol.connect()
print(unitymol_zmq.unitymol.send_command_clean("ls()"))

Author: Marc Baaden

Created: 2025-12-03 Mer 19:03

Validate