Avogadro Remote Procedure Call (RPC)#
Avogadro 2 listens on a local socket and accepts JSON-RPC 2.0 messages, so another program on the same machine can drive it: open files, load molecules straight from memory, change the display, render surfaces, animate vibrations, and save images. MoleQueue and XtalOpt use this to push results into Avogadro for visualization, and it is a convenient way to script figure generation from Python.
The easiest way to send messages is the connect class in the
avogadro package on PyPI. Everything it
does can also be done by writing the JSON packets yourself, which is described
further down.
Quick start with Python#
pip install avogadro
Start Avogadro, then from any Python script or notebook:
from avogadro.connect import connect
with connect() as avo:
avo.open_file("/path/to/caffeine.cml")
# render the van der Waals surface
avo.command("renderVanDerWaals")
avo.save_graphic("/path/to/caffeine.png")
Creating a connect object raises ConnectionError if Avogadro is not
running. Each call blocks until Avogadro answers, and raises
avogadro.connect.RPCError if Avogadro reports a problem:
from avogadro.connect import connect, RPCError
with connect() as avo:
try:
avo.command("renderMO", orbital="homo", isovalue=0.02)
except RPCError as error:
print(error.code, error.message)
Methods on connect#
Method |
Description |
|---|---|
|
Open a file from disk, inferring the format from the extension |
|
Load molecular data from a string |
|
Write the active molecule, inferring the format from the extension |
|
Save a bitmap image of the current view |
|
Run any registered command, e.g. |
|
Send any JSON-RPC method; the general escape hatch |
|
Return |
|
Ask Avogadro to quit (only honored with |
|
Close the connection (also done by the |
command() and send() reach every method Avogadro understands, including
commands added by plugins, so the list below is the real vocabulary rather than
the handful of named convenience methods.
Molecules can be sent without touching the disk, which is useful when the data was generated in the same script:
xyz = """3
O 0.000 0.000 0.119
H 0.000 0.763 -0.477
H 0.000 -0.763 -0.477
"""
with connect() as avo:
avo.load_molecule(xyz, "xyz")
The connection#
Avogadro creates a single local server named avogadro when it starts:
Linux, macOS, BSD — a Unix domain socket at
$TMPDIR/avogadro(Python’stempfile.gettempdir()resolves to the same place).Windows — a named pipe at
\\.\pipe\avogadro.
Only one instance can own the name. When a second copy of Avogadro starts it
sends internalPing to the existing server; if it gets a reply the new
instance leaves the server alone, and if it does not it takes the name over.
This means messages always go to the instance that started first and is still
alive.
Each packet on the wire is a 4-byte big-endian unsigned length followed by that many bytes of UTF-8 JSON. The same framing is used in both directions, so a reply must be read by taking the 4-byte header first and then exactly that many bytes.
Protocol#
Requests follow JSON-RPC 2.0:
{
"jsonrpc": "2.0",
"id": 1,
"method": "openFile",
"params": { "fileName": "rutile.POSCAR" }
}
id is any unique number for that request; the reply carries the same id.
params is an object whose keys are the options for that method — Avogadro
converts it to a QVariantMap before handing it to the plugin, so numbers,
strings, booleans, and lists all pass through.
A successful reply looks like:
{ "jsonrpc": "2.0", "id": 1, "result": true }
and a failure like:
{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32601, "message": "Method not found" }
}
Error code -32601 means the method name is not registered — the original
request is echoed back in error.data.request. Code -1 is used for a
method that exists but could not be carried out, for example a file that
could not be read, or any request that arrives with no Avogadro window open.
Note
params is a single JSON object. Older versions of this page showed a nested
form with braces around each key, which is not valid JSON and is not what
Avogadro parses.
Built-in methods#
These are handled by the application itself.
Method |
Parameters |
Description |
|---|---|---|
|
|
Read a file from disk and make it the active molecule. The format is inferred from the extension (e.g. |
|
|
Read molecular data from a string in any format Avogadro can read, and make it the active molecule. |
|
|
Write the active molecule. The format is inferred from the extension, and the write is queued asynchronously, so |
|
|
Save a bitmap image of the active view at its current on-screen size. |
|
|
Switch the camera projection. Either |
|
|
Choose which display types are active — see below. |
|
none |
Answers |
|
none |
Quit Avogadro. Refused unless Avogadro was started with |
Display types#
setRenderTypes takes either a list of names:
{ "method": "setRenderTypes", "params": { "types": ["BallStick", "VanDerWaals"] } }
or a set of names mapped to booleans, which also lets you turn types off:
{ "method": "setRenderTypes",
"params": { "BallStick": true, "Wireframe": false } }
A name matches either a plugin’s identifier or its translated display name. Prefer the identifier — it does not change with the interface language:
BallStick, Cartoons, CloseContacts, CrystalScene, Dipole, Force,
Label, Licorice, NonCovalent, OverlayAxes, QTAIMScenePlugin,
SurfaceRender, SymmetryScene, VanDerWaals, Wireframe
Plugin commands#
Tools and extensions register their own commands at startup, and any of them can be sent as a JSON-RPC method name. Commands that belong to a tool temporarily make that tool active, run, and then restore the tool you were using.
Unless noted otherwise, a command needs an open molecule and returns an error if there is none.
Atoms and bonds#
Command |
Parameters |
Description |
|---|---|---|
|
none |
Perceive bonds between all atoms, or within the selection if there is one. |
|
none |
Remove bonds from all atoms, or from the selection. |
|
none |
Perceive bond orders. |
|
|
Move the molecule so that this atom sits at the origin. Atoms are numbered from zero. |
|
|
Rotate the molecule so this atom lies along an axis. |
Camera and view#
Command |
Parameters |
Description |
|---|---|---|
|
|
Rotate the camera about the screen x, y, and z axes, through the current focal point. |
|
|
Zoom in or out. Positive values move toward the molecule. |
|
|
Pan the camera by a distance given in screen pixels. |
|
none |
Align the view to the x, y, and z axes, centered on the molecule and backed off far enough to frame it. |
Crystals#
Command |
Parameters |
Description |
|---|---|---|
|
none |
Wrap all atoms back into the unit cell. |
|
none |
Rotate the unit cell into the standard orientation. |
|
none |
Add the symmetry-equivalent atoms implied by the space group. |
|
none |
Fill the cell including atoms related by translation. |
Surfaces and orbitals#
Command |
Parameters |
Description |
|---|---|---|
|
|
van der Waals surface. |
|
|
Solvent-accessible surface. |
|
|
Solvent-excluded surface. |
|
|
Molecular orbital, for a file that includes a basis set. |
|
|
Total electron density. |
|
|
Spin density. |
Shared options:
orbital— either a number, counting from one so that1is the lowest orbital, or a string relative to the frontier orbitals:"homo","lumo","homo-1","lumo+2", and so on.isovalue— the isosurface value, default0.03.resolution— the cube spacing in Å. If it is left out, Avogadro picks a value from the size of the molecule, between 0.05 and 0.5 Å.spin— set to"beta"for the beta orbitals or density of an open-shell calculation; alpha is used otherwise.
Surface generation runs in the background. The reply comes back as soon as the calculation has been started, not when the surface appears.
with connect() as avo:
avo.open_file("acetone.out")
avo.command("renderMO", orbital="lumo", isovalue=0.02, resolution=0.1)
Vibrations#
Command |
Parameters |
Description |
|---|---|---|
|
none |
Open the vibrational modes dialog. |
|
|
Select a mode, numbered from zero. |
|
|
Set the animation amplitude on the same 0–99 scale as the dialog slider, which starts at 20. |
|
none |
Start animating the selected mode. |
|
none |
Stop the animation. |
|
|
Append coordinate sets displaced along one or more modes. |
Adding commands from a plugin#
Any ExtensionPlugin or ToolPlugin can add to this vocabulary. Register the
names in registerCommands() and act on them in handleCommand():
void MyExtension::registerCommands()
{
emit registerCommand("renderMovie", tr("Render a movie of the current view."));
}
bool MyExtension::handleCommand(const QString& command,
const QVariantMap& options)
{
if (command == "renderMovie") {
renderMovie(options.value("fileName").toString());
return true;
}
return false; // becomes a "Method not found" error
}
The application collects these at startup, so a command is available as soon as
the plugin loads. Return false for anything you do not handle — including a
command of yours that was sent with options it cannot use — so that the caller
gets an error rather than a silent success. If the work continues after
handleCommand() returns, emit commandFinished() when it is done.
Writing the packets directly#
If you would rather not depend on the Python package, the protocol is small enough to implement anywhere. In Python it is about a dozen lines:
import json
import socket
import struct
import tempfile
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(tempfile.gettempdir() + "/avogadro")
request = json.dumps({
"jsonrpc": "2.0",
"id": 1,
"method": "openFile",
"params": {"fileName": "rutile.POSCAR"},
}).encode("utf-8")
sock.sendall(struct.pack(">I", len(request)) + request)
size = struct.unpack(">I", sock.recv(4))[0]
print(json.loads(sock.recv(size).decode("utf-8")))
On Windows, open \\.\pipe\avogadro as an unbuffered binary file instead of
creating a socket; the framing and the JSON are identical.