Documenting the interface (Programmer’s reference)¶
The DOLFIN Programmer’s Reference is generated for the DOLFIN C++ library and Python module from the source code using the documentation tool Sphinx. This page describes how to generate the DOLFIN documentation locally and how to extend the contents of the Programmer’s Reference.
How to locally build the DOLFIN documentation¶
The DOLFIN documentation can be generated and built from the DOLFIN source directly as follows:
Make sure that Sphinx is installed.
Build DOLFIN (for instructions, see installation_from_source).
Build the documentation by running:
make doc
in the DOLFIN build directory.
For make doc to successfully run, the DOLFIN Python module must be
installed.
How to improve and extend the DOLFIN Programmer’s reference¶
The documentation contents are extracted from specially formatted comments (docstring comments) in the source code, converted to reStructuredText, and formatted using Sphinx. The syntax used for these specially formatted comments is described below.
To document a feature,
- Add appropriate docstring comments to source files (see Syntax for docstring comments).
- If you made changes to C++ header files or docstrings in
dolfin_dir/dolfin/swig/*.iyou should update thedolfin_dir/dolfin/swig/codeexamples.pyfile with an example snippet if applicable and run the scriptdolfin_dir/dolfin/swig/generate.pyto update thedolfin_dir/dolfin/swig/docstrings.ifile. - Build the documentation as described in How to locally build the DOLFIN documentation to check the result.
Syntax for docstring comments¶
As Sphinx does not allow sections in the markup for class/function
documentation, we use italics (*italics*) and definition lists
to group information. This is to keep the markup as simple as
possible since the reST source for the Python documentation of classes
and functions will be used ‘as is’ in the docstrings of the DOLFIN
module.
Most information can be put in the three sections:
Arguments, which are formatted using definition lists following this structure:
*Arguments* <name> (<type>) <description> <name2> (<type>) <description>For example:
*Arguments* dim (int) some dimension. d (double) some value.Returns, which is formatted in a similar fashion:
*Returns* <return type> <description>For example:
*Returns* int Some random integer.Example, a very small code snippet that shows how the class/function works. It does not necessarily have to be a stand-alone program.
An example of how to document a feature¶
To make matters more concrete let’s consider the case of writing
documentation for the member function closest_cell of the DOLFIN
Mesh class. The Python interface to this class is generated by
Swig and it is not extended in the Python layer. Writing
documentation for other classes and functions in DOLFIN which are not
extended or added in the Python layer follow a similar procedure.
The Mesh::closest_cell function is defined in the file
dolfin_dir/dolfin/mesh/Mesh.h, and the comment lines and function
definition look as follows:
/// Computes the index of the cell in the mesh which is closest to the
/// point query.
///
/// *Arguments*
/// point (_Point_)
/// A _Point_ object.
///
/// *Returns*
/// uint
/// The index of the cell in the mesh which is closest to point.
///
/// *Example*
/// .. code-block:: c++
///
/// UnitSquare mesh(1, 1);
/// Point point(0.0, 2.0);
/// info("%d", mesh.closest_cell(point));
///
/// output::
///
/// 1
dolfin::uint closest_cell(const Point& point) const;
Note that the documentation of a function or class is placed above the definition in the source code. The structure and content follow the guidelines in the previous section.
The Point object is a class like Mesh and it is defined in the FEniCS
interface. To insert a link to the documentation of this class use
leading and trailing underscore i.e., _Point_. When parsing the
comment lines this string will be substituted with either
:cpp:class:`Point` or :py:class:`Point` depending on whether
documentation for the C++ or Python interface is being generated. The
return type, in this case dolfin::uint, will automatically be
mapped to the correct Python type when generating the documentation
for the Python interface. Note that if you are writing documentation
for one of the functions/classes which are added to the Python layer
manually you have to add manually the correct links and types.
The example code uses C++ syntax because it is located in the C++
header file. Translating this code to a correct Python equivalent is
rather difficult. It is therefore necessary to add example code using
the Python syntax manually. This code should be put in the
dolfin_dir/dolfin/swig/codeexamples.py which contains a simple
dictionary of example code. The dictionary containing only the
example code for the example above should look as follows:
codesnippets = {
"Mesh":{
"dolfin::uint closest_cell(const Point& point) const":
"""
.. code-block:: python
>>> mesh = dolfin.UnitSquare(1, 1)
>>> point = dolfin.Point(0.0, 2.0)
>>> mesh.closest_cell(point)
1
"""}
}
The first dictionary contains dictionaries for all classes with code examples for each function. Note that the full C++ function signature has been used to identify the function to which the code example belongs.
After adding the documentation to the Mesh.h file and Python code example
to the codeexamples.py file, you have to run the script
dolfin/dolfin/swig/generate.py to generate the
dolfin/dolfin/swig/docstrings.i file and then build DOLFIN to update the
docstrings in the dolfin Python module.
Why is the documentation procedure so elaborate?¶
The procedure for writing documentation might seem cumbersome so let’s have a look at the design considerations which have led to this ostensible case of overengineering.
The Python interface is (partially) generated automatically using
Swig from the C++ implementation of DOLFIN.
Some classes are extended when building (see the dolfin/dolfin/swig/*post.i
files) while others are added or extended manually in the Python layer defined
in dolfin/site-packages/dolfin.
While this approach saves a lot of work when implementing the Python interface
it puts some constraints on the way the documentation can be handled.
In addition we have the following design goals for writing and maintaining the
documentation:
- Avoid duplicate text
- In many cases the documentation of a feature will be virtually identical for the C++ and Python interfaces, and since the Python interface is generated from the C++ code, the documentation should be in the C++ source code. To avoid that the documentation on these pages and the comments in the source code (and the implementation itself) diverge, the documentation should be automatically generated from the C++ source code. Therefore the comments should be written using Sphinx markup.
- Help in the Python interpreter
- The documentation of a class/function when running
help(dolfin.foo)in the Python interpreter should be identical to what can be found online. In practice this means that we have to generate thedolfin/dolfin/swig/docstrings.ifile using the comments extracted from the C++ source before building the Python interface with Swig. - Simple markup
- Since the documentation is written directly in the C++ source code, we want markup to be simple such that we have ‘code with comments’ rather than ‘comments with code’. Another reason for preferring simple markup is that it is the raw docstring which will be available from the Python interpreter.