Skip to content

Create External Jobs

RELION has a built-in job called "External" that can run any command as a RELION job. This means that if you define the proper wrapper for the command you want to use, you can easily extend RELION.

Simple examples are available in the GitHub repository.

Class Structure

All the subclasses of RelionExternalJob will be registered as external jobs. The subclasses must implement output_nodes() and run().

from himena_relion.external import RelionExternalJob

class MyJob(RelionExternalJob):
    def output_nodes(self):
        return [
            ("output-particles.star", "ParticleGroupMetadata.star"),
            ("output-density.mrc", "DensityMap.mrc"),
        ]

    def run(
        self,
        in_3dref,
        in_parts,
        some_param: float,
    ):
        # do something with the input files and parameters
  • output_nodes() should return a list of (output file name, node type) pairs. The output file name is relative to the job directory. RelionExternalJob will trust the output files to be generated by the run() method and will not check the implementation sanity. The node type should be one of the node types that RELION can interpret.
  • run() should implement the logic of the job. To make the job abortable, run() should be a generator function in which yield statement is inserted at the checkpoints. Following variables are useful for the implementation:
  • self.out_job_dir Path ... path to the job directory.
  • self.console rich.console.Console ... a console object for printing messages to the user. self.console.log("xyz") will print "xyz" to the run.out file.

Job Title

To make the job more informative, you can override the job_title() class method to provide a custom job title.

from himena_relion.external import RelionExternalJob

class MyJob(RelionExternalJob):
    ...

    @classmethod
    def job_title(cls):
        return "My Job"

Parameter types

To provide the user interface for the job parameters, himena-relion uses the type-to-widget mapping functionality built by magicgui, with restrictions on the types so that the job parameters can be safely serialized to and deserialized from a job.star file.

In short, following parameter types can be immediately used as the type annotations:

  • int
  • float
  • str
  • bool
  • Path (from pathlib)
  • T | None (where T is one of the above.)
  • list[T] (where T is one of the above.)
  • tuple[T1, T2, ...] (where T1, T2, ... are one of the above.)
  • Literal["option1", "option2", ...] (from typing)

To improve the user interface, you will usually need to use Annotated type from typing to provide extra information for the widgets.

Annotated[
    int,
    {
        "min": 0,  # specific to int and float types
        "max": 100,  # specific to int and float types
        "step": 1,  # specific to int and float types
        "label": "Parameter X",
        "tooltip": "This is an integer parameter.",
        "group": "Advanced Parameters",
    }
]

The reserved parameter names

Following parameter names are reserved for RELION and can be used in the run() method. These parameters do not need type annotations.

  • in_3dref ... Reference map
  • in_coords ... Picked particle coordinates
  • in_mask ... Mask
  • in_mics ... Micrographs
  • in_movies ... Movies
  • in_parts ... Particles
  • j ... Number of threads to use

Widget For Your Job

If defined, provide_widget() will be called when the job window is opened. This method must return a Qt widget

myjob.py
class MyJob(RelionExternalJob):
    ...

    def provide_widget(self, job_dir):
        return QMyJobWidget(job_dir)

job_dir is a JobDirectory object. This object is implemented with properties and methods that are useful for the manipulation of the job content.

  • job_dir.path ... absolute path to the job directory.
  • job_dir.relion_project_dir ... absolute path to the RELION project directory.
  • job_dir.make_relative_path(path) ... convert to the path relative to the RELION project (like "Class2D/job020/job.star").
  • job_dir.resolve_path(path) ... convert to the absolute path.

The Qt widget class to be returned can be any class that inherits from QWidget. To listen to the updates of the job content, you can implement the on_job_updated(job_dir, path) method in the widget class. This method will be called when the path file under the job_dir is updated.

from qtpy import QtWidgets as QtW

class QMyJobWidget(QtW.QWidget):
    def __init__(self, job_dir):
        super().__init__()
        # build your custom widget here

    def on_job_updated(self, job_dir, path):
        # this method will be called when the `path` file under the `job_dir` is
        # updated.
        ...

Run Your Job

himena can also install a .py file as a plugin. You can run the following command for testing and actually running your jobs.

himena relion --install myjob.py

If the installation succeeds, you should see the file is listed under the "Plugins" panel of the setting dialog (Ctrl+,)

and your job is discoverable in the command palette (Ctrl+Shift+P).

Distribute As A Plugin

Since your RELION plugin is just a Python module, you can distribute it as a pip-installable package. You need to specify the entry point in your pyproject.toml file as follows.

pyproject.toml
[project.entry-points."himena.plugin"]
"My RELION Plugin" = mymodule.mysubmodule.myjob

Please refer to the himena plugin system for more details.