-
Notifications
You must be signed in to change notification settings - Fork 7
wrappers
The numbox tools can be linked with several third party software libraries. The linking is managed by wrappers in the src/wrappers
directory. Each wrapper with a corresponding compilation script should assure that
- it is possible to build numbox without a particular third party library
- third party library headers are included only within a particular wrapper
- third party library is build with a required data types (e.g., integers have required size)
- third party library provides a required functionality (e.g., support parallel environment)
- calling not-linked third party library ends with an error
- wrappers resolve a potential conflict of more third party libraries
Implemented third party libraries wrappers are the following:
- Math libraries: Currently the espreso core is based on the Intel MKL library. Without this library only mesio module can be used.
- Intel MKL (at least version 2018.4)
- Parallel graph partitioners: For loading external databases the library should be linked with an external parallel graph partitioner.
- Sequential graph partitioners: In other to use second level decomposition (e.q. Hybrid FETI) at external graph decomposer is needed. Except KaHIP, they are usually part of parallel graph decomposers.
- Third party solvers: The espreso is based on in-house FETI based solvers. However, in some circumstances utilization of other solvers can be advantageous (the small number of MPI processes, sequential run, etc.).
- Hypre
- Pardiso
- Intel MKL-PDSS (at least version 2018.4)
- SuperLU
- WSMP
- Other libraries: In the case of loading or storing results in the XDMF format, HDF5 library should be linked.
Implementation of a new wrapper consists of several steps:
- Create a new directory into
src/wrappers
. - Create
wscript
for checking whether a library is available in the system. - Call the newly created
wscript
during configuration and compilation process. - Create a header file that can be included in the source code (even the library is not available).
- Implement wrappers for functions provided by the library.
We describe the process with the example that adds a wrapper for library foo
that provides function foo_bar
. At first, we create directory foo
in directory src/wrappers
with the following content:
-- src
|--
|--
|--wrappers
|--
|--
|--foo
|--wscript
|--w.foo.h
|--w.foo.cpp
The configuration script below calls link_cxx
function that checks if library header(s) can be included and the libraries can be linked (click here for complete list of available parameters). Checking is implemented by the composition of a simple cpp
file with an empty main function. If parameter fragment
is set, then the fragment is put into the body of the main function in order to check a particular library property (the size of integers used by the foo
library in our case).
def options(opt):
pass
def configure(ctx):
ctx.link_cxx(
name="FOO", header_name=["foo.h"], libs=["foo"],
fragment="return sizeof(FOO_INT) != {0};".format(ctx.options.intwidth))
The configuration script has to be called from the main configuration script located at the numbox root. Hence, we add path to src/wrappers/foo
to the recurse
function, (optionally) add foo
to printed miscellaneous libraries, and add files that should be compiled to the build
function (code does not show the full content of particular functions):
def print_available(ctx):
def _print(msg, err, libs, color="RED"):
""" .... """
_print(
"Available miscellaneous libraries",
"NOT FOUND",
[ "foo", "pthread", "hdf5" ],
"YELLOW")
def recurse(ctx):
""" .... """
ctx.recurse("src/wrappers/foo")
""" .... """
def build(ctx):
def build(files, target, use=[]):
""" .... """
build(ctx.path.ant_glob('src/wrappers/foo/**/*.cpp'), "wfoo", [ "FOO" ])
""" .... """
If the configuration process is successful (both headers and libraries are available), the following variables are added to the waf environment (can be listed by ./waf env
):
DEFINES_FOO: ['HAVE_FOO']
INCLUDES_FOO: []
LIBPATH_FOO: []
LIB_FOO: ['foo']
These variables are used whenever the building process needs to use foo
library specified by the last parameter FOO
of the build
function, where FOO
is the name from the link_cxx
function (uppercase always). Note: Since the usage of libraries is transient, foo
parameters are also used by all building tasks that depends on wfoo
.
When the configuration script is ready, we can implement a wrapper for the foo_bar
function. At first, we create the header file with a corresponding function and optionally with a guard for checking if the foo
library is linked.
#ifndef SRC_WRAPPERS_FOO_W_FOO_H_
#define SRC_WRAPPERS_FOO_W_FOO_H_
namespace espreso {
namespace foo {
bool isLinked();
int bar(esint i, esint j);
}
}
#endif /* SRC_WRAPPERS_FOO_W_FOO_H_ */
The implementation of declared functions is dependent whether definition HAVE_FOO
is defined or not. In the case that the library is linked, the library functions are called, otherwise calling ends with an error.
#include "w.foo.h"
#include "esinfo/eslog.h"
using namespace espreso;
#ifdef HAVE_FOO
#include "foo.h"
bool foo::isLinked()
{
return true;
}
int foo::bar(esint i, esint j)
{
return foo_bar(i, j);
}
#else
bool foo::isLinked()
{
return false;
}
int foo::bar(esint i, esint j)
{
eslog::error("run-time error: cannot call 'foo_bar'. The 'foo' library is not linked\n");
}
#endif