Commit 823ba7d6 authored by Emilia Juda's avatar Emilia Juda

cleanup of the document structure and contents

parent f932b4cb
This diff is collapsed.
\subsection{Documentation}
\chapter{Documentation}
The NekPy package certainly had to be documented in order to provide an easily accessible information about the wrapped classes to both users and developers. Ideally, the documentation should be:
The NekPy package certainly had to be documented in order to provide an easily accessible
information about the wrapped classes to both users and developers. Ideally, the documentation should be:
\begin{itemize}
\item easily readable by humans,
\item accessible using Python's inbuilt \texttt{help} method,
\item compatible with the existing Nektar++ doxygen-based documentation.
\item easily readable by humans,
\item accessible using Python's inbuilt \texttt{help} method,
\item compatible with the existing Nektar++ doxygen-based documentation.
\end{itemize}
Traditionally, Python classes and functions are documented using a docstring -- a string occurring as the very first statement after the function or class is defined. This string is then accessible as the \texttt{\_\_doc\_\_} attribute of the function or class. The conventions associated with Python docstrings are described in PEP 257 document \cite{PEP257}.
Traditionally, Python classes and functions are documented using a docstring -- a string occurring
as the very first statement after the function or class is defined. This string is then accessible
as the \texttt{\_\_doc\_\_} attribute of the function or class. The conventions associated with
Python docstrings are described in PEP 257 document \cite{PEP257}.
Boost.Python provides an easy way to include docstrings in the wrapped methods and classes as shown in Listing \ref{lst:doc_example}. The included docstrings will appear when Python \texttt{help} method is used.
Boost.Python provides an easy way to include docstrings in the wrapped methods and classes as
shown in Listing \ref{lst:doc_example}. The included docstrings will appear when Python
\texttt{help} method is used.
\begin{lstlisting}[caption={Example of class and method documentation in Boost.Python}, label={lst:doc_example}, language=C++]
void export_Points()
......@@ -32,7 +38,16 @@ void export_Points()
}
\end{lstlisting}
In order to fully document the existing bindings a number of enumeration type classes such as \texttt{PointsType} had to have docstrings included which proved to be a challenge since Boost.Python does not provide a way to do this. Instead a direct call to Python C API has to be made and the method adapted from \cite{python_enum_docstring} was used, as shown in Listing \ref{lst:enum_doc}. A downside of this solution is that it does requires the developer to manually update the Python documentation if the enumeration type is ever changed (e.g. adding a new type of point) as the code does not automatically gather information from the C++ class. In theory it could be possible to create a Python script which would generate Python docstrings based on the existing C++ documentation using regular expressions; however it would be difficult to integrate this solution into the existing framework.
In order to fully document the existing bindings a number of enumeration type classes such
as \texttt{PointsType} had to have docstrings included which proved to be a challenge
since Boost.Python does not provide a way to do this. Instead a direct call to Python C API
has to be made and the method adapted from \cite{PythonEnumDocstring} was used, as shown in
Listing \ref{lst:enum_doc}. A downside of this solution is that it does requires the developer
to manually update the Python documentation if the enumeration type is ever changed (e.g.
adding a new type of point) as the code does not automatically gather information from the
C++ class. In theory it could be possible to create a Python script which would generate Python
docstrings based on the existing C++ documentation using regular expressions; however it would
be difficult to integrate this solution into the existing framework.
\begin{lstlisting}[caption={Code used to include dosctings in enumetation type classes - part of \texttt{NekPyConfig.hpp}}, label={lst:enum_doc}, language=C++]
#define NEKPY_WRAP_ENUM_STRING_DOCS(ENUMNAME,MAPNAME,DOCSTRING) \
......@@ -50,6 +65,15 @@ In order to fully document the existing bindings a number of enumeration type cl
}
\end{lstlisting}
There are many docstrings conventions that are popular in Python such as Epytext, reST and Google therefore a choice had to be made as to which docstring style to use. After considering the criteria which the documentation had to fulfill it was decided to use Google Python Style \cite{python_google} as it is highly readable by humans (and hence an excellent choice for documentation which will be primarily accessible by Python \texttt{help} method) and can be used to generate automated documentation pages with Sphinx (a tool for creating Python documentation).
There are many docstrings conventions that are popular in Python such as Epytext, reST and
Google therefore a choice had to be made as to which docstring style to use. After considering
the criteria which the documentation had to fulfill it was decided to use Google Python Style
\cite{PythonGoogle} as it is highly readable by humans (and hence an excellent choice for
documentation which will be primarily accessible by Python \texttt{help} method) and can be
used to generate automated documentation pages with Sphinx (a tool for creating Python documentation).
Unfortunately, it proved to be difficult to include the documentation of NumPy package in the existing doxygen-based documentation due to the fact that the docstrings are generated by Boost.Python. It was decided that if the time constraints of the project permit this problem could be resolved at a later date and the possibility of accessing the documentation though inbuilt \texttt{help} method was deemed sufficient.
Unfortunately, it proved to be difficult to include the documentation of NumPy package in the
existing doxygen-based documentation due to the fact that the docstrings are generated by Boost.Python.
It was decided that if the time constraints of the project permit this problem could be resolved
at a later date and the possibility of accessing the documentation though inbuilt \texttt{help}
method was deemed sufficient.
\section{Installing NekPy}
\chapter{Installing NekPy}
NekPy has the following list of requirements:
......@@ -14,20 +14,20 @@ systems, as we describe below. We also have a requirement on the \texttt{Boost.N
package, which is available in Boost 1.63 or later. If this isn't found on your
system, it will be automatically downloaded and compiled.
\subsection{Compiling and installing Nektar++}
\section{Compiling and installing Nektar++}
Nektar++ should be compiled as per the user guide instructions and installed
into a directory which we will refer to as \texttt{$NEKDIR}. By default this is the
into a directory which we will refer to as \texttt{\$NEKDIR}. By default this is the
\texttt{dist} directory inside the Nektar++ build directory.
Note that Nektar++ must, at a minimum, be compiled with \texttt{NEKTAR_BUILD_LIBRARY},
\texttt{NEKTAR_BUILD_UTILITIES} , \texttt{NEKTAR_BUILD_SOLVERS} and \texttt{NEKTAR_BUILD_PYTHON}.
Note that Nektar++ must, at a minimum, be compiled with \texttt{NEKTAR\_BUILD\_LIBRARY},
\texttt{NEKTAR\_BUILD\_UTILITIES} , \texttt{NEKTAR\_BUILD\_SOLVERS} and \texttt{NEKTAR\_BUILD\_PYTHON}.
This will automatically download and install \texttt{Boost.NumPy} if required. Note
that all solvers may be disabled as long as the \texttt{NEKTAR_BUILD_SOLVERS} option is set.
that all solvers may be disabled as long as the \texttt{NEKTAR\_BUILD\_SOLVERS} option is set.
\subsubsection{macOS}
\subsection{macOS}
\paragraph{Homebrew}
\subsubsection{Homebrew}
Users of Homebrew should make sure their installation is up-to-date with
\texttt{brew upgrade}. Then run
......@@ -42,10 +42,10 @@ To install the NumPy package, use the \texttt{pip} package manager:
pip install numpy
\end{lstlisting}
\paragraph{MacPorts}
\subsubsection{MacPorts}
Users of MacPorts should sure their installation is up-to-date with
\texttt{sudo port selfupdate && sudo port upgrade outdated}. Then run
\texttt{sudo port selfupdate \&\& sudo port upgrade outdated}. Then run
\begin{lstlisting}[language=bash]
sudo port install python27 py27-numpy
......@@ -53,18 +53,18 @@ sudo port select --set python python27
\end{lstlisting}
\subsubsection{Linux: Ubuntu/Debian}
\subsection{Linux: Ubuntu/Debian}
Users of Debian and Ubuntu Linux systems should sure their installation is
up-to-date with \texttt{sudo apt-get update && sudo apt-get upgrade}
up-to-date with \texttt{sudo apt-get update \&\& sudo apt-get upgrade}
\begin{lstlisting}[language=bash]
sudo apt-get install libboost-python-dev python-numpy
\end{lstlisting}
\subsubsection{Compiling the wrappers}
\subsection{Compiling the wrappers}
Run the following command in \texttt{$NEKDIR\build} directory to install the Python package
Run the following command in \texttt{\$NEKDIR\textbackslash{}build} directory to install the Python package
for the current user:
\begin{lstlisting}[language=bash]
......@@ -78,10 +78,11 @@ make nekpy-install-system
\end{lstlisting}
\subsection{Using the bindings}
\section{Using the bindings}
By default, the bindings will install into the \texttt{dist} directory, along with a
number of examples that are stored in the \texttt{$NEKDIR\library\Demos\Python} directory. To
number of examples that are stored in the
\texttt{\$NEKDIR\textbackslash{}library\textbackslash{}Demos\textbackslash{}Python} directory. To
test your installation, you can for example run one of these (e.g. \texttt{python Basis.py}) or
launch an interactive session:
......@@ -96,10 +97,11 @@ Type "help", "copyright", "credits" or "license" for more information.
<NekPy.LibUtilities._LibUtilities.PointsKey object at 0x11005c310>
\end{lstlisting}
\subsubsection{Examples}
\subsection{Examples}
A number of examples of the wrappers can be found in the \texttt{$NEKDIR\library\Demos\Python}
directory, along with a sample mesh \texttt{newsquare_2x2.xml}:
A number of examples of the wrappers can be found in the
\texttt{\$NEKDIR\textbackslash{}library\textbackslash{}Demos\textbackslash{}Python}
directory, along with a sample mesh \texttt{newsquare\_2x2.xml}:
\begin{itemize}
\item \texttt{SessionReader.py} is the simplest example and shows how to construct a
......@@ -110,9 +112,9 @@ directory, along with a sample mesh \texttt{newsquare_2x2.xml}:
duplicates the functionality of \texttt{Basis.py} using the \texttt{StdExpansion} class. Run
this as \texttt{python StdProject.py}.
\item \texttt{MeshGraph.py} loads a mesh and prints out some basic properties of its
quadrilateral elements. Run it as \texttt{python MeshGraph.py newsquare_2x2.xml}.
quadrilateral elements. Run it as \texttt{python MeshGraph.py newsquare\_2x2.xml}.
\end{itemize}
If you want to modify the source files, it's advisable to edit them in the
\texttt{$NEKDIR\library\Demos\Python} directory and re-run \texttt{make install}, otherwise local
changes will be overwritten by the next \texttt{make install}.
\texttt{\$NEKDIR\textbackslash{}library\textbackslash{}Demos\textbackslash{}Python} directory and
re-run \texttt{make install}, otherwise local changes will be overwritten by the next \texttt{make install}.
\section{Introduction}
\chapter{Introduction}
This repository contains the \texttt{NekPy} Python wrappers for the Nektar++
spectral/\textit{hp} element framework. \emph{As a disclaimer, these wrappings are
experimental and incomplete.} You should not rely on their current
structure and API remaining unchanged.
This part of the guide contains the information on using and developing \texttt{NekPy}
Python wrappers for the \nek{} spectral/\textit{hp} element framework.
\emph{As a disclaimer, these wrappings are experimental and incomplete.}
You should not rely on their current structure and API remaining unchanged.
Currently, representative classes from the \texttt{LibUtilities}, \texttt{StdRegions},
\texttt{SpatialDomains} and \texttt{LocalRegions} libraries have been wrapped in order to show
the proof-of-concept.
\texttt{SpatialDomains}, \texttt{LocalRegions} and \texttt{MultiRegions} libraries
have been wrapped in order to show the proof-of-concept.
\subsection{Features and functionality}
\section{Features and functionality}
\texttt{NekPy} uses the \texttt{Boost.Python} library to provide a set of high-quality,
hand-written Python bindings for selected functions and classes in Nektar++. A
typical snippet could look something like:
hand-written Python bindings for selected functions and classes in Nektar++.
It is worth noting that Python (CPython, the standard Python implementation written in C,
in particular) includes C API and that everything in Python is strictly speaking a C
structure called \texttt{PyObject}. Hence, defining a new class, method etc. in Python
is in reality creating a new \texttt{PyObject} structure.
Boost.Python is essentially a wrapper for Python C API which conveniently exports C++
classes and methods into \texttt{PyObjects}. At compilation time a dynamic library
is created which is then imported to Python, as shown in Figure \ref{fig:boost_python}.
\begin{figure}[h!]
\centering
\includegraphics[width=0.9\textwidth]{img/boost_python}
\caption{A schematic diagram of how C++ code is converted into Python with Boost.Python \cite{C++Api}}
\label{fig:boost_python}
\end{figure}
A typical snippet could look something like:
\begin{lstlisting}[caption={NekPy sample snippet}, label={lst:nekpy_sample}, language=Python]
from NekPy.LibUtilities import PointsKey, PointsType, BasisKey, BasisType
......
This diff is collapsed.
\chapter{Package structure}
The NekPy wrapper is designed to mimic the library structure of Nektar++, with
directories for the \texttt{LibUtilities}, \texttt{SpatialDomains} and \texttt{StdRegions}
libraries. This is a deliberate design decision, so that classes and definitions
......@@ -13,16 +15,17 @@ There are also some other directories and files:
\item \texttt{cmake} has some CMake configuration files.
\end{itemize}
\subsubsection{Package structure}
\label{sec:package_str}
Since the Python wrapper files have been moved to the main Nektar++ codebase as opposed to being contained in a separate repository, the structure of the package had to be modified. As suggested in \cite{thesis_spooner}, the namespaces of the NekPy package follow the namespaces of Nektar++ main codebase -- this is reflected both in the filenames as well as package modules (e.g. \texttt{NekPy.LibUtilities} module contains bindings of classess and methods found in \texttt{LibUtilities} library of Nektar++).
Figure \ref{fig:package_str} shows the location of Python wrapper files within Nektar++ structure. Every sub-module of Nektar++ hosts an additional folder for Python files and the structure of the Python folder mimics the structure of the sub-module itself, as shown on the left of the figure with \texttt{LibUtilities} sub-module. Individual \texttt{.cpp} files, such as \texttt{Expansion.cpp} contain the wrappers for classes and methods from corresponding Nektar++ library files whereas \texttt{.cpp} files named after sub-modules (e.g. \texttt{LibUtilities.cpp}) contain \texttt{BOOST\_PYTHON\_MODULE} definitions which create package modules (e.g. \texttt{NekPy.LibUtilities}).
Figure \ref{fig:package_str} shows the location of Python wrapper files within Nektar++ structure.
Every sub-module of Nektar++ hosts an additional folder for Python files and the structure of the
Python folder mimics the structure of the sub-module itself, as shown on the left of the figure
with \texttt{LibUtilities} sub-module. Individual \texttt{.cpp} files, such as \texttt{Expansion.cpp}
contain the wrappers for classes and methods from corresponding Nektar++ library files whereas
\texttt{.cpp} files named after sub-modules (e.g. \texttt{LibUtilities.cpp}) contain
\texttt{BOOST\_PYTHON\_MODULE} definitions which create package modules (e.g. \texttt{NekPy.LibUtilities}).
\begin{figure}[h!]
\centering
\includegraphics[width=0.9\textwidth]{package_str}
\includegraphics[width=0.9\textwidth]{img/package_str}
\caption{The location of Python wrapper files within Nektar++ structure.}
\label{fig:package_str}
\end{figure}
\ No newline at end of file
\section{NekPy wrapping guide}
\chapter{NekPy wrapping guide}
This section attempts to outline some of the basic principles of the NekPy
wrapper, which relies entirely on the excellent \texttt{Boost.Python} library. An
......@@ -11,19 +11,19 @@ Python interpreter when you try to use your wrapper. Judicious use of Google is
therefore recommended to track down these issues!
You may also find the following resources useful:
\begin{itemise}
\begin{itemize}
\item The \texttt{Boost.Python} tutorial \cite{BoostPythonTutorial},
\item The \texttt{Boost.Python} entry on the Python wiki \cite{BoostPythonWikiEntry},
\item Examples on GitHub \cite{GitHubExamples},
\item The OpenStreetGraph cookbook \cite{OpenStreetGraphCookbook} and rationale
for using manual wrapping \cite{ManualWrappingRationale} which served as a starting
point for this project.
\end{itemise}
\end{itemize}
To demonstrate how to wrap classes, we'll refer to a number of existing parts of
the code below.
\subsection{Defining a library}
\section{Defining a library}
First consider \texttt{LibUtilities}. An abbreviated version of the base file,
\texttt{LibUtilities.cpp} has the following structure:
......@@ -45,19 +45,19 @@ BOOST_PYTHON_MODULE(_LibUtilities)
}
\end{lstlisting}
The \texttt{BOOST_PYTHON_MODULE(name)} macro allows us to define a Python module inside
The \texttt{BOOST\_PYTHON\_MODULE(name)} macro allows us to define a Python module inside
C++. Note that in this case, the leading underscore in the name
(i.e. \texttt{_LibUtilities}) is deliberate. To define the contents of the module, we
call a number of functions that are prefixed by \texttt{export_}, which will define one
(i.e. \texttt{\_LibUtilities}) is deliberate. To define the contents of the module, we
call a number of functions that are prefixed by \texttt{export\_}, which will define one
or more Python classes that live in this module. These Python classes correspond
with our Nektar++ classes. We adopt this approach simply so that we can split up
the different classes into different files, because it is not possible to call
\texttt{BOOST_PYTHON_MODULE} more than once. These functions are defined in
appropriately named files, for example \texttt{export_Basis()} lives in the file
\texttt{NekPy/LibUtilities/Foundations/Basis.cpp}. This corresponds to the Nektar++ file
\texttt{library/LibUtilities/Foundations/Basis.cpp} and the classes defined therein.
\texttt{BOOST\_PYTHON\_MODULE} more than once. These functions are defined in
appropriately named files, for example \texttt{export\_Basis()} lives in the file
\texttt{NekPy\/LibUtilities\/Foundations\/Basis.cpp}. This corresponds to the Nektar++ file
\texttt{library\/LibUtilities\/Foundations\/Basis.cpp} and the classes defined therein.
\subsection{Basic class wrapping}
\section{Basic class wrapping}
As a very basic example of wrapping a class, let's consider the \texttt{SessionReader}
wrapper.
......@@ -81,14 +81,14 @@ void export_SessionReader()
}
\end{lstlisting}
\subsubsection{\texttt{py::class_<>}}
\subsection{\texttt{py::class\_<>}}
This \texttt{Boost.Python} object defines a Python class in C++. It is templated, and
in this case we have the following template arguments:
\begin{itemize}
\item \texttt{SessionReader} is the class that will be wrapped
\item \texttt{std::shared_ptr<SessionReader>} indicates that this object should be stored
\item \texttt{std::shared\_ptr<SessionReader>} indicates that this object should be stored
inside a shared (or smart) pointer, which we frequently use throughout the
library, as can be seen by the frequent use of \texttt{SessionReaderSharedPtr}
\item \texttt{boost::noncopyable} indicates that \texttt{Boost.Python} shouldn't try to
......@@ -101,14 +101,14 @@ We then have two arguments:
\begin{itemize}
\item \texttt{"SessionReader"} is the name of the class in Python.
\item \texttt{py::no_init} indicates this object has no publically-accessible
\item \texttt{py::no\_init} indicates this object has no publically-accessible
initialiser. This is because for \texttt{SessionReader}, we define a factory-type
function called \texttt{CreateInstance} instead.
\end{itemize}
\subsubsection{Wrapping member functions}
\subsection{Wrapping member functions}
We then call the \texttt{.def} function on the \texttt{class_<>}, which allows us to define
We then call the \texttt{.def} function on the \texttt{class\_<>}, which allows us to define
member functions on our class. This is equivalent to \texttt{def}-ing a function in
Python. \texttt{.def} has two required parameters, and one optional parameter:
......@@ -128,7 +128,7 @@ However, there are some instances where we need to do some additional
conversion, mask some C++ complexity from the Python interface, or deal with
functions that return references. We describe ways to deal with this below.
\paragraph{Thin wrappers}
\subsubsection{Thin wrappers}
Instead of defining a function pointer to a member of the C++ class, we can
define a function pointer to a separate function that defines some extra
......@@ -153,18 +153,18 @@ SessionReaderSharedPtr SessionReader_CreateInstance(py::list &ns)
In Python, we can then simply call \texttt{session = SessionReader.CreateInstance(sys.argv)}.
\paragraph{Dealing with references}
\subsubsection{Dealing with references}
When dealing with functions in C++ that return references, e.g. \texttt{const NekDouble &GetFactor()}
When dealing with functions in C++ that return references, e.g. \texttt{const NekDouble \&GetFactor()}
we need to supply an additional argument to \texttt{.def()}, since Python immutable
types such as strings and integers cannot be passed by reference. For a full
list of options, consult the \texttt{Boost.Python} guide. However a good rule of thumb
is to use \texttt{copy_const_reference} as highlighted above, which will create a copy
is to use \texttt{copy\_const\_reference} as highlighted above, which will create a copy
of the const reference and return this.
\paragraph{Dealing with \texttt{Array<OneD, >}}
\subsubsection{Dealing with \texttt{Array<OneD, >}}
The \texttt{LibUtilities/SharedArray.cpp} file contains a number of functions that
The \texttt{LibUtilities\/SharedArray.cpp} file contains a number of functions that
allow for the automatic conversion of Nektar++ \texttt{Array<OneD, >} storage to and
from NumPy \texttt{ndarray} objects. This means that you can wrap functions that take
these as parameters and return arrays very easily. However bear in mind the
......@@ -190,7 +190,7 @@ following caveats:
\item \texttt{TwoD} and \texttt{ThreeD} arrays are not supported.
\end{itemize}
\subsubsection{Inheritance}
\subsection{Inheritance}
Nektar++ makes heavy use of inheritance, which can be translated to Python quite
easily using \texttt{Boost.Python}. For a good example of how to do this, you can
......@@ -229,7 +229,7 @@ Note the following:
\texttt{Boost.Python} already knows how to convert.
\end{itemize}
\subsubsection{Wrapping enums}
\subsection{Wrapping enums}
Most Nektar++ enumerators come in the form:
......@@ -246,8 +246,8 @@ static const char *MyEnumMap[] = {
};
\end{lstlisting}
To wrap this, you can use the \texttt{NEKPY_WRAP_ENUM} macro defined in
To wrap this, you can use the \texttt{NEKPY\_WRAP\_ENUM} macro defined in
\texttt{NekPyConfig.hpp}, which in this case can be used as
\texttt{NEKPY_WRAP_ENUM(MyEnum, MyEnumMap)}. Note that if instead of
\texttt{NEKPY\_WRAP\_ENUM(MyEnum, MyEnumMap)}. Note that if instead of
\texttt{const char *} the map is defined as a \texttt{const std::string},
you can use the \texttt{NEKPY_WRAP_ENUM_STRING} macro.
you can use the \texttt{NEKPY\_WRAP\_ENUM\_STRING} macro.
\input{nekpy_introduction.tex}
\input{nekpy_installation.tex}
\input{nekpy_structure.tex}
\input{nekpy_wrapping.tex}
\input{nekpy_documentation.tex}
\input{nekpy_memory_management.tex}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment