Skip to content

wrappers

Ondrej Meca edited this page Apr 22, 2021 · 3 revisions

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:

  1. Math libraries: Currently the espreso core is based on the Intel MKL library. Without this library only mesio module can be used.
  1. Parallel graph partitioners: For loading external databases the library should be linked with an external parallel graph partitioner.
  1. 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.
  1. 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.).
  1. Other libraries: In the case of loading or storing results in the XDMF format, HDF5 library should be linked.

How to create a new wrapper

Implementation of a new wrapper consists of several steps:

  1. Create a new directory into src/wrappers.
  2. Create wscript for checking whether a library is available in the system.
  3. Call the newly created wscript during configuration and compilation process.
  4. Create a header file that can be included in the source code (even the library is not available).
  5. 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

Libraries

HDF5

Hypre

KaHIP

METIS

MKL

MKL Parallel Direct Sparse Solver

PARDISO

ParMETIS

pthread

PtScotch

Scotch

SuperLU

WSMP

Clone this wiki locally