Skip to content

Developer Documentation

TOFarmer edited this page Mar 31, 2020 · 13 revisions

Developer Documentation

Please read this documentation before modifying the MDMC code base. Any pull requests that are not consistent with this documentation will not be merged.

Coding Standards

Follow standards described in PEP 8, except where it differs from standards already adopted in surrounding code.

Line length

The line length in MDMC (including documentation) is 80 characters.

snake_case

snake_case is used for all variables names, except when the variable contains an abbreviation. So a variable related to a molecular dynamics (MD) engine is MD_engine, rather than md_engine. The same applies for physical symbols, e.g. Q for momentum transfer and E for energy should both be upper case.

Mathematical Operands

With regards mathematical operands, favour a space separating both sides of an operand:

E = h_bar * 1e18 * np.pi * np.arange(n) / (n * dt)

instead of:

E = h_bar*1e18*np.pi*np.arange(n)/(n*dt)

If an expression runs over two lines, prefer brackets to backslash, and align expressions in the same manner as for mathematical formula (i.e. new line should start with operand):

E = (h_bar * 1e18 * np.pi
     * np.arange(n) / (n * dt))

instead of:

E = h_bar * 1e18 * np.pi\
    * np.arange(n) / (n * dt)

or:

E = (h_bar * 1e18 * np.pi *
     np.arange(n) / (n * dt))

Method ordering

The ordering of methods in classes is as follows:

  1. __new__
  2. __init__
  3. Other magic methods (__)
  4. Properties
  5. Public methods
  6. Private methods This order is because magic methods, properties and public methods make up the interface of the class. Properties must precede public methods so that Sphinx creates them directly after Attributes.

Imports

Imports should be grouped in the following order:

  1. stdlib
  2. third party libraries
  3. local modules

Each group should be sorted alphabetically and should be separated by a newline.

Units

All physical quantities within MDMC should possess units.

Everything related to units in MDMC is defined in MDMC.common.units. This includes:

  • the system of units (in the SYSTEM dictionary)
  • A Unit class, which derives from str to add additional mul, div, and pow methods so that strings representing units (e.g. 'Ang', 's') can be combined by these operations: Unit('Ang') * Unit('s') == Unit('Ang s')
  • UnitFloat and UnitArray classes, which derive from float and numpy.ndArray to add additional a Unit object to the representation, by means of a unit attribute.

So all physical quantities must be a UnitFloat or a UnitArray, and have the correct Unit object as an attribute. This can be achieved by having the unit as a property, rather than an attribute, and using one of the following decorators (in MDMC.common.decorators):

  • unit_decorator. This should typically be the decorator that is used as it sets the property to be either a UnitFloat or UnitArray. It should be added to the property setter:
@property
def velocity(self):

    return self._velocity

@velocity.setter
@unit_decorator(unit=units.LENGTH / units.TIME)
def velocity(self, velocity):

    self._velocity = velocity
  • unit_decorator_getter. This only be used either where the property has no setter method or where the getter method performs a calculation before setting the value (which may result in the UnitFloat/UnitArray being cast to a float/ndArray). It should only be used in these cases as it is more expensive than unit_decorator, as it initialises a UnitFloat/UnitArray object every time the getter is called. It should be added to the property getter:
@property
@unit_decorator_getter(unit=units.LENGTH)
def dims(self):

    return self._dims

In both of the above examples the unit passed to the decorator is a constant from the SYSTEM of units defined in the units module; however it would be equally valid to define a unit in this argument:

from MDMC.common.units import Unit
@property
@unit_decorator_getter(unit=Unit('nm'))
def dims(self):

    return self._dims

It is then obviously important that all functions and methods that use these properties have the correct factors to ensure the unit conversions are correct.

Documentation

MDMC follows the NumPy documentation style, which is consistent with PEP 257. The exceptions to this are:

  • The Attributes section should be the last section in a class docstring. This is because Sphinx formats Attributes in the same manner to methods (without a heading), so it leads on to this.
  • Properties should not be documented in the Attributes section, as then they are duplicated in Sphinx. Instead they should only be documented in the getter method. As long as properties precede methods, the properties will follow the attributes in the Sphinx documentation.
  • Parameters that can only assume one of a fixed set of values, in NumPy these values are listed instead of the type. This is not the case in MDMC as at this early development stage these values are still in flux. Therefore instead just state the type. This may change at a later date.
"""
Parameters
----------
MDMC : str
    This is how MDMC does it.
NumPy : {'C', 'F', 'A'}
    This is how NumPy does it.
"""

Code Checker/Linter

Use a code checker or linter such as pylint or flake8, to ensure PEP 8 coding standards are adopted.

Sphinx

  • To build the documentation using Sphinx, pandoc must be installed. This is not the same as the pandoc which is available from pypi (e..g pip install pandoc).
  • All images should be included in doc/_static/images
  • All .ipynb tutorials should be included in doc/tutorials. When modifying these tutorials, jupyter notebook must be run from the doc directory, not the tutorials directory. This is because the Jupyter server then has access to the _static/images folder, which is required for some tutorials.

Clone this wiki locally