Developer Tutorial

DIALS has been designed to be extensible. If you’ve got a great idea for a new integration algorithm, then, with a bit of work, you should be able to get it running within the DIALS framework. The following tutorial applies for both developers looking to put their algorithms within DIALS itself as well as for those adding algorithms in their own installation.

Spot Finding

Algorithms for computing the possible spot pixels used in spot finding should implement the following interface. An example can be found under “dials/extensions/kabsch_spotfinder_threshold_ext.py”.

class SpotFinderThresholdIface(interface.Interface):

  def __init__(self, params, imageset):
    pass

  @interface.abstractmethod
  def compute_threshold(self, image, mask):
    pass

The algorithm is configured through phil parameters and the imageset. The extension should also provide a compute_threshold method which takes an image and a mask and should return a flex.bool array of the same dimensions where True pixels are possible spot pixels and False pixels are background.

Profile Modelling

Warning

The profile modelling framework is not very mature and may be changed. I’ll try and keep the documentation up-to-date.

Algorithms for computing the profile model should implement the following interface. An example can be found under “dials/extensions/gaussian_rs_profile_model_ext.py”.

class ProfileModelCreatorIface(interface.Interface):

  @interface.abstractmethod
  def create(cls, params, experiments, reflections=None):
    pass

The profile algorithm should have a “create” method that is given phil parameters, experiments and (optionally) reflections. In the absence of reflections, the profile model should be constructable from phil parameters. When reflections are present, the create method should be able to construct the model from the input reflections and experiments.

The algorithm should return an instance of the ProfileModelList class (found in dials.algorithms.profile_model.model_list). The profile model list should contain a list of objects which implement the interface given in the dials.algorithms.profile_model.interface module and shown in brief below.

class ProfileModelIface(object):

  @interface.abstractmethod
  def predict_reflections(self, experiment, **kwargs):
    pass

  @interface.abstractmethod
  def compute_bbox(self, experiment, reflections, **kwargs):
    pass

  @interface.abstractmethod
  def compute_partiality(self, experiment, reflections, **kwargs):
    pass

  @interface.abstractmethod
  def compute_mask(self, experiment, reflections, **kwargs):
    pass

  @interface.abstractmethod
  def dump(self):
    pass

The profile model should have methods for predicting the reflections, computing the bounding box of reflections for a number of experiments, computing the partiality of reflections and computing the foreground/background mask. Of these the bounding box and mask methods are crucial for integration to work; partiality is currently only used in reporting and can be a placeholder.

The extention should have the ability to dump the profile model to phil parameters so that it can be input via a profile.phil file to, for example, re-run integration with the same profile parameters.

Indexing

FIXME

Refinement

FIXME

Integration

Centroid algorithms

Algorithms for computing the reflection centroid should implement the following interface. An example can be found under “dials/extensions/simple_centroid_ext.py”.

class CentroidIface(interface.Interface):

  def __init__(self, params, experiments):
    pass

  @interface.abstractmethod
  def compute_centroid(self, reflections):
    pass

The algorithm is configured through phil parameters and the list of experiments. The extension should also provide a compute_centroid method which takes a list of reflections with extracted shoebox data. The “shoebox” column of the reflection table should contain a list of dials.model.Shoebox types. The algorithm should fill the “xyzobs.px” column of the reflection table with the observed centroid positions.

Background algorithms

Algorithms for computing the reflection background should implement the following interface. An example can be found under “dials/extensions/simple_background_ext.py”.

class BackgroundIface(interface.Interface):

  def __init__(self, params, experiments):
    pass

  @interface.abstractmethod
  def compute_background(self, reflections):
    pass

The algorithm is configured through phil parameters and the list of experiments. The extension should also provide a compute_background method which takes a list of reflections with extracted shoebox data. The “shoebox” column of the reflection table should contain a list of dials.model.Shoebox types. The algorithm should fill the shoebox.background values and return the reflection list.

Deploying algorithms

Within the DIALS project

The DIALS project has the following layout.

dials
|
|-- algorithms
|   |
|   |-- integration
|       |
|       |-- sum
|           |
|           |-- ...
|
|-- interfaces
|   |
|   |-- ...
|
|-- extensions
    |
    |-- summation_integration_ext.py
    |
    |-- ...

Each algorithm should have it’s implementation encapsulated within a package in the appropriate place. For example, summation integration is implemented within the “dials.algorithms.integration.sum” package. The extension class, which inherits from the appropriate interface, and configures and calls this algorithm should then be placed in the “dials/extensions” folder with an appropriate name. For example, the summation integration extension is placed in the module “dials.extensions.summation_integration_ext”. Modules within the dials.extension package will be automatically loaded when searching for algorithms and any class within these modules that inherits from an interface will be registered for use within the DIALS command line programs.

Within external projects

If you have a project containing algorithms written for use within DIALS that is built using the cctbx build system, it is easy to make DIALS aware of your new algorithms.

A typical project layout is shown below.

my_project
|
|-- algorithms
|   |
|   |-- integration
|       |
|       |-- my_algorithm
|           |
|           |-- ...
|
|-- extensions
    |
    |-- my_algorithm_intensity_ext.py

If your project has this directory structure, with an intensity algorithm implementation within the “my_algorithm” directory and the extension class (inheriting from the IntensityIface class) in the “extension” directory you can make DIALS aware of your algorithm by adding the following code to the libtbx_refresh.py scripy in the top level of your project. This will add the extensions directory in your project to the list of directories searched when loading available algorithms.

# libtbx_refresh.py

from __future__ import division

try:
  from dials.framework import env
  import libtbx.load_env
  from os.path import join
  path = libtbx.env.dist_path("my_project")
  env.cache.add(join(path, "extensions"))
except Exception:
  pass

Running “libtbx.refresh” or “make reconf” will update your build. You can check that your algorithm has been found properly by using the “dials.show_extensions” command-line program which should show a list of extensions implementing each interface with your algorithm listed with the other available algorithms.