# Advanced topics

This section covers several advanced topics related to OVITO’s scripting interface:

> - Saving and loading pipelines

> - Controlling how many processor cores OVITO uses

> - Packaging and installation of user extensions for OVITO

## Saving and loading pipelines

The `ovito.io.export_file()` function lets you save the computed output of a pipeline to disk. But how do you save the definition of the pipeline itself, including the modifiers and their parameters, to a file? OVITO can save the entire visualization scene to a `.ovito` state file using the `Scene.save()` method. Thus, in order to save a `Pipeline` you need to first make it part of the scene by calling its `add_to_scene()` method:

    import ovito
    from ovito.io import import_file
    from ovito.modifiers import CoordinationAnalysisModifier

    pipeline = import_file("input/simulation.dump")
    pipeline.modifiers.append(CoordinationAnalysisModifier(cutoff = 3.4))
    # ...
    pipeline.add_to_scene()
    ovito.scene.save("output/mypipeline.ovito")

The saved state can be restored by calling the `Scene.load()` method. This function loads all saved data pipelines and makes them available in the `Scene.pipelines` list:

    import ovito

    ovito.scene.load("output/mypipeline.ovito")
    pipeline = ovito.scene.pipelines[0]
    data = pipeline.compute()

Of course, it is also possible to open the .ovito state file with the graphical OVITO application or, conversely, to use the graphical application to create a data pipeline by hand and then save that pipeline to a .ovito file for future use.

You can either load the state file from a script at runtime using the `Scene.load()` method or preload the state file when running the script using the ovitos integrated interpreter. This is done by specifying the -o command line option:

    ovitos -o mypipeline.ovito script.py

The code in `script.py` will now be executed in an environment where the `Scene` was already populated with the state loaded from the .ovito scene file. Instead of setting up a new pipeline from scratch, the script can now work with the existing pipeline(s) that were restored from the state file:

    import ovito

    pipeline = ovito.scene.pipelines[0]

    # Replace the data file that serves as pipeline input with a different one:
    pipeline.source.load("input/second_file.dump")
    # Adjust modifier parameters of the existing pipeline:
    pipeline.modifiers[0].cutoff = 3.1

    data = pipeline.compute()

## Controlling how many processor cores OVITO uses

Many computation functions in OVITO have been parallelized in order to make use of all available processor cores. This includes, for example, computationally heavy analysis rountines such as `PolyhedralTemplateMatchingModifier`, `VoronoiAnalysisModifier`, `ClusterAnalysisModifier`, `ComputePropertyModifier`, and many more. The software-based rendering engines `TachyonRenderer` and `OSPRayRenderer` also harness multiple cores in parallel to speed up image generation.

By default, these algorithms will make use of all available cores of your CPU. This default number is determined by OVITO using the function QThread.idealThreadCount(), which can be queried as follows in a Python program:

    >>> from ovito.qt_compat import QtCore
    >>> print(QtCore.QThread.idealThreadCount())
    4

Sometimes it is desirable to restrict OVITO to a single CPU core only, for example, when running multiple instances of OVITO in parallel on the same node. This can be acocmplished in two ways. The graphical application ovito and the script interpreter ovitos both support the command line parameter --nthreads, which lets you override the number of CPU cores used by parallel algorithms:

    ovitos --nthreads 1 yourscript.py

Your second option is to set the `OVITO_THREAD_COUNT` environment variable prior to invoking or importing OVITO. This approach always works, including for Python scripts running in an external Python interpreter and importing the `ovito` module:

    export OVITO_THREAD_COUNT=1
    python3 yourscript.py

## Packaging and installation of user extensions for OVITO

OVITO’s Python interface allows you to implement custom modifiers, file readers, and viewport overlays that seamlessly extend the functionality of OVITO Pro and the standalone OVITO Python package. To make such extensions available to the user in the graphical user interface or in Python programs, they need to be properly installed and registered for the system to automatically find them at runtime.

The automatic discovery of user-defined extension classes is based on Python’s entry point specification. This standard mechanism helps to keep the code of your extension class cleanly separated from the main OVITO software package. That means both may be updated or changed independently, and the standard Python packaging mechanism facilitates the easy deployment of your extension to other OVITO users in case you want to share your work with the community.

Tip

To aid you in the development of new extensions, we provide GitHub repository templates for each OVITO extension type, which include step-by-step instructions, an initial directory structure, and a `pyproject.toml` skeleton that simplifies packaging and distribution of your extension to other users:

- Modifier template (GitHub)

- File reader template (GitHub)

- Pipeline source template (GitHub)

- Viewport overlay template (GitHub)

Furthermore, the OVITO team hosts a public repository where you can publish your extensions and share them with other users:

> OVITO Extensions Directory: A collection of Python extensions submitted by the community, maintained by their respective authors, and curated by the OVITO developers.

In the following, we’ll consider a custom modifier class written for OVITO Pro with the name MyModifier, which will serve as an example for an extension to be registered in the OVITO system. We start with a simple code project layout having the following directory structure:

    <root-dir>/
    ├── pyproject.toml
    └── src/
        └── MyModifier/
            └── __init__.py

The `MyModifier` class is defined in `src/MyModifier/__init__.py`:

    from ovito.data import DataCollection
    from ovito.pipeline import ModifierInterface

    class MyModifier(ModifierInterface):
       def modify(self, data: DataCollection, frame: int, **kwargs):
          ...

If you are developing a complex extension, you can also split your code into multiple `.py` files or nested packages if needed.

The `pyproject.toml` package metadata file is needed for the installation of MyModifier and any dependencies it requires (e.g. the SciPy package) using pip:

    [build-system]
    requires = ["setuptools", "wheel"]
    build-backend = "setuptools.build_meta"

    [project]
    name = "MyModifier"
    version = "1.0"
    description = "A custom Python modifier for OVITO."
    keywords = ["ovito", "python-file-reader"]
    authors = [{name = "Author Name", email = "author@organization.org"}]
    maintainers = [{name = "Maintainer", email = "maintainer@organization.org"}]
    license = {text = "MIT License"}
    readme = "README.md"
    requires-python = ">=3.7"
    dependencies = [
       "ovito >= 3.9.1"
    ]

    [project.urls]
    repository = "https://github.com/author/MyModifier"

    [tool.setuptools.packages.find]
    where = ["src"]

Thanks to this `pyproject.toml` file, as we will see later, the code of `MyModifier` can be installed in any Python interpreter (including the OVITO Pro integrated interpreter) using the pip standard utility – just like regular Python packages. After the installation, we could call `import MyModifier` from a Python program to load and use our module.

However, we still have to tell OVITO Pro that the `MyModifier` package contains the definition of an OVITO `ModifierInterface` class (or some other kind of OVITO extension) that should be automatically loaded at runtime and made available in the graphical user interface of OVITO Pro. This requires the definition of an entry point in `pyproject.toml` (see the setuptools website for more details).

To register our custom modifier class as an entry point in the standard group `OVITO.Modifier`, we add an entry-points section to `pyproject.toml`:

    [project.entry-points.'OVITO.Modifier']
    "My Modifier" = "MyModifier:MyModifier"

The second line first specifies the human-readable name of our extension to be displayed in the OVITO Pro GUI. The part after the `=` specifies the module name and the Python class that implements the OVITO extension. In our case, both the module and the class are named MyModifier.

OVITO Pro automatically loads all installed packages that did register entry points in the `OVITO.Modifier` group and displays them to the user as part of the list of available modifiers. For other extension types similar entry point groups exist:

Python extension class | Entry point group | Template
--- | --- | ---
`ModifierInterface` | `[project.entry-points.'OVITO.Modifier']` | pyproject.toml
`PipelineSourceInterface` | `[project.entry-points.'OVITO.PipelineSource']` | pyproject.toml
`FileReaderInterface` | `[project.entry-points.'OVITO.FileReader']` | pyproject.toml
`ViewportOverlayInterface` | `[project.entry-points.'OVITO.ViewportOverlay']` | pyproject.toml

After preparing the `pyproject.toml` file, you can now install your extension package locally into OVITO Pro by invoking the pip install command via ovitos:

    ovitos -m pip install --user --editable <root-dir>

where `<root-dir>` is the directory containing the `pyproject.toml` file. The `--editable` option ensures that you can still make code changes without the need to reinstall the package. `-m` tells ovitos to invoke the pip module and run its install command.

If you decide to upload the code of your OVITO extension to a public Git repository, other users can install it directly from your online repository as explained in Installing Python-based extensions for OVITO with pip.