Developers’ Guide#

CIL is an Object Orientated software. It has evolved during the years and it currently does not fully adheres to the following conventions. New additions must comply with the following.

Conventions on new CIL objects#

For each class there are essential, and non-essential parameters. The non-essential can be further be divided in often configured and advanced parameters:

  • essential

  • non-essential

    • often-configured

    • advanced

The definition of what are the essential, often-configured and advanced parameters depends on the actual class.

Creator#

To create an instance of a class, the creator of a class should require the essential and often-configured parameters as named parameters.

It should not accept positional arguments *args or key-worded arguments **kwargs so that the user can clearly understand what parameters are necessary to create the instance.

Setter methods and properties#

Use of property is favoured instead of class members to store parameters so that the parameters can be protected.

The class should provide setter methods to change all the parameters at any time. Setter methods to set multiple parameters at the same time is also accepted. Setter methods should be named set_<parameter>. The use of set_ helps IDEs and user to find what they should change in an instance of a class.

Other methods#

Methods that are not meant to be used by the user should have a _ (underscore) at the beginning of the name. All methods should follow the convention of small caps underscore separated words.

Documentation#

Docstrings#

The Core Imaging Library (CIL) follows the NumpyDoc style with the PyData Sphinx HTML theme. When contributing your code please refer to this link for docstring formatting and this rendered example.

Example from cil#

The following provides an example of the docstring format used within cil, and the rendered documentation generated from it.

Source#
FBP.run method from cil.io.recon.FBP#
    def run(self, out=None, verbose=1):
        """
        Runs the configured FBP recon and returns the reconstruction

        Parameters
        ----------
        out : ImageData, optional
           Fills the referenced ImageData with the reconstructed volume and suppresses the return

        verbose : int, default=1
           Controls the verbosity of the reconstructor. 0: No output is logged, 1: Full configuration is logged

        Returns
        -------
        ImageData
            The reconstructed volume. Suppressed if `out` is passed
        """
        if verbose:
            print(self)

        if self.slices_per_chunk:
            if AcquisitionType.DIM2 & self.acquisition_geometry.dimension:
                raise ValueError("Only 3D datasets can be processed in chunks with `set_split_processing`")
            elif self.acquisition_geometry.system_description == 'advanced':
                raise ValueError("Only simple and offset geometries can be processed in chunks with `set_split_processing`")
            elif self.acquisition_geometry.get_ImageGeometry() != self.image_geometry:
                raise ValueError("Only default image geometries can be processed in chunks `set_split_processing`")

            if out is None:
                ret = self.image_geometry.allocate()
            else:
                ret = out

            if self.filter_inplace:
                self._pre_filtering(self.input)

            tot_slices = self.acquisition_geometry.pixel_num_v
            remainder = tot_slices % self.slices_per_chunk
            num_chunks = int(np.ceil(self.image_geometry.shape[0] / self._slices_per_chunk))

            if verbose:
                pbar = tqdm(total=num_chunks)

            #process dataset by requested chunk size
            self._setup_PO_for_chunks(self.slices_per_chunk)
            for i in range(0, tot_slices-remainder, self.slices_per_chunk):

                if 'bottom' in self.acquisition_geometry.config.panel.origin:
                    start = i
                    end = i + self.slices_per_chunk
                else:
                    start = tot_slices -i - self.slices_per_chunk
                    end = tot_slices - i

                ret.array[start:end,:,:] = self._process_chunk(i, self.slices_per_chunk)

                if verbose:
                    pbar.update(1)

            #process excess rows
            if remainder:
                self._setup_PO_for_chunks(remainder)

                if 'bottom' in self.acquisition_geometry.config.panel.origin:
                    start = tot_slices-remainder
                    end = tot_slices
                else:
                    start = 0
                    end = remainder

                ret.array[start:end,:,:] = self._process_chunk(i, remainder)

                if verbose:
                    pbar.update(1)

            if verbose:
                pbar.close()

            if out is None:
                return ret

        else:

            if self.filter_inplace is False:
                proj_filtered = self.input.copy()
            else:
                proj_filtered = self.input

            self._pre_filtering(proj_filtered)

            operator = self._PO_class(self.image_geometry,self.acquisition_geometry)

            if out is None:
                return operator.adjoint(proj_filtered)
            else:
                operator.adjoint(proj_filtered, out = out)
Rendered#
FBP.run(out=None, verbose=1)[source]#

Runs the configured FBP recon and returns the reconstruction

Parameters:
  • out (ImageData, optional) – Fills the referenced ImageData with the reconstructed volume and suppresses the return

  • verbose (int, default=1) – Controls the verbosity of the reconstructor. 0: No output is logged, 1: Full configuration is logged

Returns:

The reconstructed volume. Suppressed if out is passed

Return type:

ImageData

Building documentation locally#

The easiest way to test documentation changes is to open a pull request and download the rendered documentation from the CI.

Alternatively, to build the docs locally, you will need a working cil installation. For development of the documentation embedded within the source code itself (e.g. docstrings), you should build cil from source.

The following steps can be used to create an environment that is suitable for building cil and its documentation, and to start a HTTP server to view the documentation.

  1. Clone cil repo

  2. Update conda with conda update -n base -c defaults conda

  3. Follow the instructions here to create a conda environment and build cil from source

  4. Go to docs folder

  5. Install packages from docs_environment.yml

  6. [Install Ruby version 3.2](https://www.ruby-lang.org/en/documentation/installation/#installers)

  7. Install the web dependencies with make web-deps

  8. Build the documentation with make dirhtml web

  9. Start an HTTP server with make serve to access the docs via localhost:8000.

Example:

git clone --recurse-submodule git@github.com:TomographicImaging/CIL
cd CIL
sh scripts/create_local_env_for_cil_development_tests.sh -n NUMPY_VERSION -p PYTHON_VERSION -e ENVIRONMENT_NAME
conda activate ENVIRONMENT_NAME
cmake -S . -B ./build -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX}
cmake --build ./build --target install
cd docs
conda update -n base -c defaults conda
conda env update -f docs_environment.yml # with the name field set to ENVIRONMENT_NAME
make web-deps  # install dependencies (requires ruby 3.2)
make dirhtml web serve

Contributions guidelines#

Make sure that each contributed file contains the following text enclosed in the appropriate comment syntax for the file format. Please replace [yyyy] and [name of copyright owner] with your own identifying information. Optionally you may add author name and email.

Copyright [yyyy] United Kingdom Research and Innovation
Copyright [yyyy] The University of Manchester
Copyright [yyyy] [name of copyright owner]
Author(s): [Author name, Author email (optional)]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.