...
 
Commits (19)
......@@ -518,6 +518,98 @@ year = "2011"
}
%% NekPy references
@misc{BoostPythonTutorial,
author = "de Guzman J., Abrahams D.",
title = "Boost.Python Tutorial - 1.67.0",
year = "2018",
url = "https://www.boost.org/doc/libs/1_67_0/libs/python/doc/html/tutorial/index.html",
note = "[Accessed 1st May 2018]"
}
@misc{BoostPythonWikiEntry,
title = "boost.python/HowTo - Python Wiki",
year = "2015",
url = "https://wiki.python.org/moin/boost.python/HowTo",
note = "[Accessed 1st May 2018]"
}
@misc{GitHubExamples,
title = "GitHub - TNG/boost-python-examples: Some examples for the use of boost::python",
year = "2016",
url = "https://github.com/TNG/boost-python-examples",
note = "[Accessed 7th May 2018]"
}
@misc{OpenStreetGraphCookbook,
title = "osgboostpython/WrappingCookbook.md at wiki · Skylark13/osgboostpython · GitHub",
year = "2015",
url = "https://github.com/Skylark13/osgboostpython/blob/wiki/WrappingCookbook.md",
note = "[Accessed 7th May 2018]"
}
@misc{ManualWrappingRationale,
title = "osgboostpython/ManualWrappingRationale.md at wiki · Skylark13/osgboostpython · GitHub",
year = "2015",
url = "https://github.com/Skylark13/osgboostpython/blob/wiki/ManualWrappingRationale.md",
note = "[Accessed 7th May 2018]"
}
@misc{PEP257,
title = "PEP 257 -- Docstring Conventions",
author = "Goodger D., van Rossum G.",
year = "2001",
url = "https://www.python.org/dev/peps/pep-0257/",
note = "[Accessed 16th May 2018]"
}
@misc{PythonEnumDocstring,
title = "Piotr Jaroszyński's blog: Boost.Python: docstrings in enums",
author = "Jaroszyński P.",
year = "2007",
url = "http://blog.piotrj.org/2007/07/boostpython-docstrings-in-enums.html",
note = "[Accessed 16th May 2018]"
}
@misc{PythonGoogle,
title = "Google Python Style Guide",
author = "Patel A., Picard A., Jhong E. et al.",
url = "https://google.github.io/styleguide/pyguide.html",
note = "[Accessed 16th May 2018]"
}
@book{C++Api,
title = "API Design for C++",
author = "Reddy M.",
year = "2011",
publisher = "Burlington: Elsevier"
}
@book{C++Memory,
author = "Daconta M.",
title = "C++ pointers and dynamic memory management",
publisher = "New York: Wiley",
year = "1995"
}
@misc{C++SharedPtr,
title =" std::shared\_ptr - cppreference.com",
url = "http://en.cppreference.com/w/cpp/memory/shared\_ptr",
note = "[Accessed 21 March 2018]"
}
@misc{PythonMemory,
author = "Python Software Foundation",
title = "Memory Management - Python 2.7.14 documentation",
url = "https://docs.python.org/2/c-api/memory.html",
note = "[Accessed 21 March 2018]"
}
@misc{PythonManualMemory,
author = "Python Software Foundation",
title = "Reference Counting - Python 2.7.14 documentation",
url = "https://docs.python.org/2/c-api/refcounting.html",
note = "[Accessed 21 March 2018]"
}
......@@ -98,6 +98,10 @@
\input{utilities/utilities-master.tex}
\part{NekPy: Python interface to \nek{}} \label{part:nekpy}
\input{python/python-master.tex}
\bibliographystyle{plain}
\bibliography{developers-guide}
......
\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:
\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.
\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}.
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()
{
py::class_<PointsKey>("PointsKey",
"Create a PointsKey which uniquely defines quadrature points.\n"
"\n"
"Args:\n"
"\tnQuadPoints (integer): The number of quadrature points.\n"
"\tpointsType (PointsType object): The type of quadrature points.",
py::init<const int, const PointsType&>())
.def("GetNumPoints", &PointsKey::GetNumPoints,
"Get the number of quadrature points in PointsKey.\n"
"\n"
"Args:\n"
"\tNone\n"
"Returns:\n"
"\tInteger defining the number of quadrature points in PointsKey.")
}
\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{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) \
{ \
py::enum_<ENUMNAME> tmp(#ENUMNAME); \
for (int a = 0; a < (int)SIZENAME(ENUMNAME); ++a) \
{ \
tmp.value(MAPNAME[a].c_str(), (ENUMNAME)a); \
} \
tmp.export_values(); \
PyTypeObject * pto = \
reinterpret_cast<PyTypeObject*>(tmp.ptr()); \
PyDict_SetItemString(pto->tp_dict, "__doc__", \
PyString_FromString(DOCSTRING)); \
}
\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{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.
\chapter{FieldConvert in NekPy}
This chapter will describe the idea behind the \texttt{FieldConvert} utility in
NekPy and discuss how the process is implemented through a Python
\texttt{FieldConverter} class. As this part of the project has not been fully
completed yet, this chapter also outlines the tasks that need to be done in order
to show the proof-of-concept, as well as some ideas for further improving the tool.
\section{Idea and motivation}
The idea behind porting \texttt{FieldConvert} utility to Python is to allow the
user to execute a seamless workflow, from converting the mesh and running
calculations to preparing data visualisations. This solution is a potential
improvement over the original \texttt{FieldConvert} tool, as the Python-based
workflow can potentially be executed from a single Python script.
Another advantage of the Python version of the tool is the possibility of
interacting with other software featuring Python interface, e.g. Paraview.
This could make the process of visualising the results of computations done
with \nek{} even easier.
\section{Design and implementation}
\texttt{FieldConvert} utility in Python was designed to provide the user with
an easy workflow. Hence, a minimum effort is required to set up the conversion
and the majority of work is done behind the scenes.
This section will discuss the design of the utility, including the description
of work yet to be done, as well as the user workflow required to execute the
basic conversions.
\subsection{\texttt{FieldConverter} class}
The entire procedure outlined in the original \texttt{FieldConvert} utility is
managed by the \texttt{FieldConverter} class located in
\path{utilities/FieldConvert/Python/FieldConvert.py}. The class has the following
attributes:
\begin{itemize}
\item \texttt{fieldSharedPtr}: a shared pointer to the field, necessary to
initialise a module;
\begin{itemize}
\item \emph{TO-DO}: \texttt{FieldSharedPtr} type remains to be wrapped.
\item \emph{TO-DO}: Whether a separate \texttt{FieldSharedPtr} is
needed for each module initialisation needs to be discussed.
\end{itemize}
\item \texttt{moduleFactory}: would be equivalent to \texttt{GetModuleFactory()}
in the original utility;
\begin{itemize}
\item \emph{TO-DO}: Whether this is possible needs to be discussed.
\item \emph{TO-DO}: It would be best to wrap the general template
\texttt{NekFactory} and initialise a module factory this way.
\end{itemize}
\item \texttt{availableModuleList}: holds a list of available modules in
order to assert that the modules requested by the user are available;
\begin{itemize}
\item \emph{TO-DO}: \texttt{PrintAvailableClasses} method needs to
be wrapped.
\item \emph{TO-DO}: It would definitely be neater if the available modules
existed as an enum rather than just strings containing names.
\end{itemize}
\item \texttt{sessionFile}, \texttt{inputFile}, \texttt{outputFile}:
hold the necessary filenames;
\item \texttt{moduleList}: holds a list of \texttt{Module} objects, created as
requested by the user;
\item \texttt{variableMap}: equivalent of \texttt{vm} variable from the original
utility.
\begin{itemize}
\item \emph{TO-DO}: This variable map needs to be constructed from user
input as it needs to be passed into \texttt{Process} method of
\texttt{Module} class.
\end{itemize}
\end{itemize}
\subsection{User workflow}
The currently suggested user workflow is as follows:
\begin{enumerate}
\item Initialise an instance of \texttt{FieldConverter} class.
\item Add a session file as well as an input and an output file.
\begin{itemize}
\item The converter will assert that the session file and the input file
exist, assert that the file extensions are supported and create appropriate
modules.
\end{itemize}
\item Add any modules, e.g. \texttt{vorticity}.
\begin{itemize}
\item Currently the argument passed into \texttt{addProcessModule} is a
string containing the module name.
\item \emph{TO-DO}: As mentioned before, it would be neater if said
argument was an enum.
\item \emph{TO-DO}: Some more thought is required about how to support
modules requiring or permitting parameters. One solution would be to pass
a tuple containing module name and parameters. Care needs to be taking in
asserting the validity of parameters.
\item As before, the converter will assert that the module exists and create
an appropriate \texttt{Module} class object.
\end{itemize}
\item Run the conversion.
\end{enumerate}
The code showcasing the suggested workflow can be found in the main function of the
\texttt{FieldConvert.py} file.
\subsection{Conversion process}
\section{Further development and improvement}
\ No newline at end of file
\chapter{Installing NekPy}
NekPy has the following list of requirements:
\begin{itemize}
\item Boost with Python support
\item Nektar++ \texttt{master} branch compiled from source (i.e. not from packages)
\item Python 2.7+ (note that examples rely on Python 2.7)
\item NumPy
\end{itemize}
Most of these can be installed using package managers on various operating
systems, as we describe below. We also have a requirement on the \texttt{Boost.NumPy}
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.
\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
\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}.
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.
\subsection{macOS}
\subsubsection{Homebrew}
Users of Homebrew should make sure their installation is up-to-date with
\texttt{brew upgrade}. Then run
\begin{lstlisting}[language=bash]
brew install python boost-python
\end{lstlisting}
To install the NumPy package, use the \texttt{pip} package manager:
\begin{lstlisting}[language=bash]
pip install numpy
\end{lstlisting}
\subsubsection{MacPorts}
Users of MacPorts should sure their installation is up-to-date with
\texttt{sudo port selfupdate \&\& sudo port upgrade outdated}. Then run
\begin{lstlisting}[language=bash]
sudo port install python27 py27-numpy
sudo port select --set python python27
\end{lstlisting}
\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}
\begin{lstlisting}[language=bash]
sudo apt-get install libboost-python-dev python-numpy
\end{lstlisting}
\subsection{Compiling the wrappers}
Run the following command in \path{$NEKDIR/build} directory to install the Python package
for the current user:
\begin{lstlisting}[language=bash]
make nekpy-install-user
\end{lstlisting}
Alternatively, the following command can be used to install the package for all users:
\begin{lstlisting}[language=bash]
make nekpy-install-system
\end{lstlisting}
\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
\path{$NEKDIR/library/Demos/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:
\begin{lstlisting}[language=bash]
$ cd builds
$ python
Python 2.7.13 (default, Apr 4 2017, 08:47:57)
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from NekPy.LibUtilities import PointsKey, PointsType
>>> PointsKey(10, PointsType.GaussLobattoLegendre)
<NekPy.LibUtilities._LibUtilities.PointsKey object at 0x11005c310>
\end{lstlisting}
\subsection{Examples}
A number of examples of the wrappers can be found in the
\path{$NEKDIR/library/Demos/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
session reader object. Run it as \texttt{python SessionReader.py mesh.xml}.
\item \texttt{Basis.py} shows functionality of basic \texttt{LibUtilities} points and basis
classes. Run this as \texttt{python Basis.py}.
\item \texttt{StdProject.py} shows how to use some of the \texttt{StdRegions} wrappers and
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}.
\end{itemize}
If you want to modify the source files, it's advisable to edit them in the
\path{$NEKDIR/library/Demos/Python} directory and
re-run \texttt{make install}, otherwise local changes will be overwritten by the next \texttt{make install}.
\chapter{Introduction}
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}, \texttt{LocalRegions} and \texttt{MultiRegions} libraries
have been wrapped in order to show the proof-of-concept.
\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++.
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
from NekPy.StdRegions import StdQuadExp
import numpy as np
numModes = 8
numPts = 9
ptsKey = PointsKey(numPts, PointsType.GaussLobattoLegendre)
basisKey = BasisKey(BasisType.Modified_A, numModes, ptsKey)
quadExp = StdQuadExp(basisKey, basisKey)
x, y = quadExp.GetCoords()
fx = np.sin(x) * np.cos(y)
proj = quadExp.FwdTrans(fx)
\end{lstlisting}
\texttt{NekPy} uses the \texttt{Boost.NumPy} library, contained in Boost 1.63+, to
automatically convert C++ \texttt{Array<OneD, >} objects to and from the commonly-used
\texttt{numpy.ndarray} object, which makes the integration more seamless between Python
and C++.
\ No newline at end of file
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
in Nektar++ can be easily located inside NekPy.
There are also some other directories and files:
\begin{itemize}
\item \path{LibUtilities/Python/NekPyConfig.hpp} is a convenience header that all
\texttt{.cpp} files should import. It sets appropriate namespaces for
\texttt{boost::python} and \texttt{boost::python::numpy}, depending on whether
the \texttt{Boost.NumPy} library was compiled or is included in Boost,
\item \path{cmake/python} contains templates for \texttt{init.py} and \texttt{setup.py}
files which every Python package should contain,
\item \path{cmake/ThirdPartyPython.cmake} is a CMake configuration file which searches for
\texttt{Boost.Python} and prepares \texttt{make} targets for installing NekPy.
\end{itemize}
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]{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
This diff is collapsed.
\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}
\input{nekpy_fieldconvert.tex}
\ No newline at end of file
......@@ -21,6 +21,8 @@ openany, % A chapter may start on either a recto or verso page.
\usepackage{lmodern}
\usepackage[english]{babel} % English please
\usepackage[final]{microtype} % Less badboxes
\usepackage[stable]{footmisc} % Footnotes in section titles
\usepackage{url}
% \usepackage{kpfonts} %Font
......@@ -30,6 +32,7 @@ openany, % A chapter may start on either a recto or verso page.
\usepackage{graphicx} % Include figures
\usepackage{makeidx}
\usepackage{import}
\usepackage{subcaption}
%%% PAGE LAYOUT
%%%-----------------------------------------------------------------------------
......@@ -89,8 +92,9 @@ openany, % A chapter may start on either a recto or verso page.
\captionnamefont{\small\bfseries} % Font of the caption name
\captiontitlefont{\small\normalfont} % Font of the caption text
\changecaptionwidth % Change the width of the caption
\captionwidth{1\textwidth} %
% incompatible with subcaption package so commented out for now
%\changecaptionwidth % Change the width of the caption
%\captionwidth{1\textwidth} %
%%% ABSTRACT
%%%------------------------------------------------------------------------------
......@@ -172,6 +176,9 @@ openany, % A chapter may start on either a recto or verso page.
\usepackage{listings} % Display code / shell commands
\usepackage{lstautogobble}
%\newcommand{\shellcommand}[1]{\begin{lstlisting} \#1 \end{lstlisting}
\lstset{
breaklines=true
}
\lstdefinestyle{BashInputStyle}{
language=bash,
basicstyle=\small\ttfamily,
......@@ -184,6 +191,7 @@ openany, % A chapter may start on either a recto or verso page.
linewidth=0.95\linewidth,
xleftmargin=0.05\linewidth,
keepspaces=true,
breaklines=true,
framesep=5pt,
rulecolor=\color{black!30},
aboveskip=10pt,
......@@ -206,7 +214,8 @@ openany, % A chapter may start on either a recto or verso page.
morecomment=[s]{<!--}{-->},
commentstyle=\color{gray},
stringstyle=\color{orange},
identifierstyle=\color{darkblue}
identifierstyle=\color{darkblue},
breaklines=true
}
\lstdefinestyle{XMLStyle}{
language=XML,
......@@ -218,7 +227,8 @@ openany, % A chapter may start on either a recto or verso page.
columns=fullflexible,
backgroundcolor=\color{black!05},
linewidth=0.95\linewidth,
xleftmargin=0.05\linewidth
xleftmargin=0.05\linewidth,
breaklines=true
}
\lstdefinestyle{C++Style}{
language=C++,
......@@ -232,7 +242,8 @@ openany, % A chapter may start on either a recto or verso page.
linewidth=0.9\linewidth,
xleftmargin=0.1\linewidth,
showspaces=false,
showstringspaces=false
showstringspaces=false,
breaklines=true
}
\ifdefined\HCode
......