How to Add a Kernel to moacean_parcels.kernels
In summary, the steps you need to follow to add a kernel to this package are:
Put your kernel function and related particle class code in a module in the
MoaceanParcels/moacean_parcels/kernels/
directory. Please see Kernel Modules for details.Register your kernel function and particle class in the
MoaceanParcels/moacean_parcels/kernels/__init__.py
file. Please see Kernel Function and Particle Class Registration for details.Add your kernel function and particle class to the automatic documentation generator in the
MoaceanParcels/docs/kernel_functions.rst
file. Please see Kernel Function and Particle Class Auto-Documentation for details.Add a notebook that explain the design of your kernel function and particle class to the
MoaceanParcels/docs/kernels/kernel_example_notebooks/particle_behaviour_kernels/
directory andindex.html
file therein, or theMoaceanParcels/docs/kernels/kernel_example_notebooks/recovery_kernels/
directory andindex.html
file. Please see Kernel Example Notebooks for details.
The details of what to do and how to do it for each of those steps are provided in the sections below:
Kernel Modules
A kernel module is a .py file that contains code of your kernel function, and an associated particle class (if applicable).
Conventions
Use one module per kernel.
MoaceanParcels/moacean_parcels/kernels/DeleteParticle.py
is an example of a kernel module.The name of the kernel module is the same as the name of the kernel function it contains.
OceanParcels style is to write kernel function names and particle class names in camel-case (capitalized words jammed together with no spaces). Examples:
AdvectionRK4()
AdvectionDiffusionM1()
DeleteParticle()
ScipyParticle
JITParticle
VectorParticle
Note that this is different to the usual Python convention of using snake-case (lower case words separated by underscores) for function and module names and camel-case for class names. However, one of the principle of the Python style guide is the consistency within a project is important. So, we adopt the OceanParcels style in this package.
Copyright Notice
Please include this copyright notice in a comment block at the top of your module:
# Copyright 2021 – present, UBC EOAS MOAD Group and The University of British Columbia
#
# 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
#
# https://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.
# SPDX-License-Identifier: Apache-2.0
Kernel Function Signature
Kernel function definition statements must be like:
def KernelName(particle, fieldset, time):
That is, kernel function must accept exactly three arguments. The conventional names of those arguments are article, fieldset, time.
As noted above, the OceanParcels style is to write kernel function names in camel-case (capitalized words jammed together with no spaces).
Particle Class Sub-classing
Particle classes must sub-class either parcels.ScipyParticle
:
class VectorParticle(ScipyParticle):
or parcels.JITParticle
:
class VectorParticle(JITParticle):
Particle classes that sub-class parcels.JITParticle
generally provide
faster execution.
Those that sub-class parcels.ScipyParticle
are easier to debug so they are generally
faster to develop.
parcels.JITParticle
sub-classes have access to a limited set of Python library modules
while parcels.ScipyParticle
sub-classes are less limited.
A good development strategy may be to start with a parcels.ScipyParticle
sub-class
and then change it to a parcels.JITParticle
once it is debugged and tested.
Please see the OceanParcels JIT Particles and Scipy particles tutorial for more details.
Variables defined within particle classes provide the way to pass information other than fieldset and time to a kernel function operating on a particular particle.
As noted above, Python and OceanParcels style is to write particle class names in camel-case (capitalized words jammed together with no spaces).
Kernel Function Docstrings
Docstrings are triple-quoted comment blocks that follow immediately after function def statements. The docstring in your kernel function provides the documentation that is rendered in the MOAD Kernel Functions and Particle Classes section of these docs (see Kernel Function and Particle Class Auto-Documentation for details of how that happens).
Your docstring should have for parts:
A description of what the kernel does
A code example of how to use the kernel
A reference to where the example notebook for your kernel is stored
Descriptions and type annotations for the three arguments that the kernel function accepts
Here is an example of a complete kernel function docstring:
"""Delete a particle that has been lost during execution
of the simulation and print its id number as well as information
about where and when it was lost.
This kernel is intended for use as an error recovery kernel,
most likely for the
:py:exc:`parcels.tools.statuscodes.OutOfBoundsError` or
:py:exc:`parcels.tools.statuscodes.ThroughSurfaceError`
error conditions.
Example usage:
.. code-block:: python
from moacean_parcels.kernels import DeleteParticle
# ...
pset.execute(
kernels,
# ...
recovery={ErrorCode.ErrorOutOfBounds: DeleteParticle},
# ...
)
For a more detailed usage example,
please see the example notebook for this kernel in the
:ref:`RecoveryKernelExampleNotebooks` section.
:param particle: Particle that has gone out of bounds.
:type particle: :py:class:`parcels.particle.JITParticle` or
:py:class:`parcels.particle.ScipyInteractionParticle`
:param fieldset: Hydrodynamic fields that is moving the particle.
:type fieldset: :py:class:`parcels.fieldset.FieldSet`
:param time: Current time of the particle.
:type time: :py:attr:`numpy.float64`
"""
Important
Indentation is important in docstrings, the same way it is in Python code.
Breaking that down into the four parts:
The description of what the kernel does is:
"""Delete a particle that has been lost during execution of the simulation and print its id number as well as information about where and when it was lost. This kernel is intended for use as an error recovery kernel, most likely for the :py:exc:`parcels.tools.statuscodes.OutOfBoundsError` or :py:exc:`parcels.tools.statuscodes.ThroughSurfaceError` error conditions.
This is mostly free text, though recovery kernels should probably make reference to the exceptions that they provide recovery for as shown here.
The code example of how to use the kernel is mostly a Sphinx code-block directive containing the example code. Ellipses in the code block must be marked as comments so that the block is valid Python. In the example above, the code example part is:
Example usage: .. code-block:: python from moacean_parcels.kernels import DeleteParticle # ... pset.execute( kernels, # ... recovery={ErrorCode.ErrorOutOfBounds: DeleteParticle}, # ... )
The reference to where the example notebook for your kernel is stored can be pretty much verbatim:
For a more detailed usage example, please see the example notebook for this kernel in the :ref:`RecoveryKernelExampleNotebooks` section.
For particle behaviour kernels, change the reference label to ParticleBehaviourKernelExampleNotebooks.
You can copy and paste the following for the descriptions and type annotations of the three arguments that the kernel function accepts:
:param particle: Particle that has gone out of bounds. :type particle: :py:class:`parcels.particle.JITParticle` or :py:class:`parcels.particle.ScipyInteractionParticle` :param fieldset: Hydrodynamic fields that is moving the particle. :type fieldset: :py:class:`parcels.fieldset.FieldSet` :param time: Current time of the particle. :type time: :py:attr:`numpy.float64` """
Particle Class Documentation
Coming soon…
Kernel Function and Particle Class Registration
One of the design goals of this package is to enable kernel functions and particle classes to be imported from it using clean, intuitive import statement like:
from moacean_parcels.kernels import DeleteParticle
To make that possible with the naming convention we have adopted for kernel modules and the
functions they contain,
it is necessary to “register” kernel functions and particle classes in the
MoaceanParcels/moacean_parcels/kernels/__init__.py
file.
The moacean_parcels.kernels.DeleteParticle()
function is registered with the line:
from .DeleteParticle import DeleteParticle
That line is using Python relative import syntax to import the function called
DeleteParticle()
from the module called
moacean_parcels.kernels.DeleteParticle
in the
MoaceanParcels/moacean_parcels/kernels/
directory.
It has the effect of putting the DeleteParticle()
function into
the moacean_parcels.kernels
namespace so that import statement like:
from moacean_parcels.kernels import DeleteParticle
just work.
If you have defined a particle class in your kernel module,
it also needs to have a registration line in the
MoaceanParcels/moacean_parcels/kernels/__init__.py
file.
Kernel Function and Particle Class Auto-Documentation
We use the Sphinx autodoc extension pull the documentation for kernel functions and particle classes from the code docstrings.
Provided that you have followed the instruction in the Kernel Modules section about writing
your docstrings,
adding the documentation of your code to the MOAD Kernel Functions and Particle Classes section is a simple matter
of adding a title and an autofunction directive to the appropriate section of the
MoaceanParcels/docs/kernels/kernel_functions.rst
file.
For example:
:py:func:`DeleteParticle`
-------------------------
.. autofunction:: moacean_parcels.kernels.DeleteParticle
For a particle class:
use :py:class: in the title
use the autoclass directive
Please ensure the the underline below your title is at least as long as the title. It can be longer, but Sphinx will complain if it is shorter.
If you check the documentation, either by building it locally, or after it has been rendered on readthedocs, and find that your kernel or particle class documentation is missing or incomplete, the likely cause is a reStructuredText syntax error in your docstring. Check the docstrings of other kernel functions or particle classes or reach out for help on the #oceanparcels or #moad-python-notes Slack channels.
Kernel Example Notebooks
We use the nbsphinx extension for Sphinx to enable Jupyter notebooks to be included as pages in this documentation.
It is highly recommended that you create a notebook that explains the purpose and features of your kernels and particle classes, and provides an example of their use. To add your notebook to this documentation:
Store your notebook in one of the sub-directories of
MoaceanParcels/docs/kernels/kernel_example_notebooks/
:MoaceanParcels/docs/kernels/kernel_example_notebooks/particle_behaviour_kernels/
is for particle behaviour kernels and their associated particle classesMoaceanParcels/docs/kernels/kernel_example_notebooks/recovery_kernels/
is for error recovery kernels
To make it easy for people to find the example notebook associated with a given kernel module we use the convention of making the name of the notebook file the same as that of the module with -example appended. For example, the example notebook for the
MoaceanParcels/moacean_parcels/kernels/DeleteParticle.py
recovery kernel isMoaceanParcels/docs/kernels/kernel_example_notebooks/recovery_kernels/DeleteParticle-example.ipynb
.Add the name of your notebook to the toctree section of the
index.rst
file in the directory where you stored it. Be sure to include the .ipynb extension to signal to Sphinx that it should use nbsphinx to parse the notebook instead of trying to read it as reStructuredText. Example:.. toctree:: :caption: Contents: DeleteParticle-example.ipynb
The title in the first cell of your notebook will be used as the section title in docs table of contents.