diff --git a/python/SConscript b/python/SConscript index 355cd31..a90bc7b 100644 --- a/python/SConscript +++ b/python/SConscript @@ -1,59 +1,58 @@ from os.path import abspath from distutils.version import StrictVersion from subprocess import check_output, STDOUT Import('main_env') env_swig = main_env.Clone( tools=['swig'], - SWIG='swig3.0', SHLIBPREFIX='', ) if env_swig['SWIGVERSION'] is None: print "Could not find swig version" print env_swig.Dictionary() print env_swig.Environment Exit(1) if StrictVersion(env_swig['SWIGVERSION']) < StrictVersion("3.0"): print "Swig version {} is not supported by tamaas".format(env_swig['SWIGVERSION']) Exit(1) # Check user's prefered version version_out = check_output([main_env['py_exec'], "--version"], stderr=STDOUT).split(' ') version = StrictVersion(version_out[-1]) python3 = version >= StrictVersion("3.0") print("Building wrapper for python version {}".format(version)) # Run code below to get versions from user preference and not scons versions_script = """ from __future__ import print_function import distutils.sysconfig as dsys from numpy.distutils.misc_util import get_numpy_include_dirs as numpy_dirs print(dsys.get_python_inc()) for d in numpy_dirs(): print(d)""" includes = check_output([main_env['py_exec'], "-c", versions_script]).split('\n') env_swig.AppendUnique(CPPPATH=includes) env_swig.AppendUnique(SWIGPATH=['$CPPPATH']) env_swig.AppendUnique(SWIGFLAGS=['-python', '-c++']) if python3: env_swig.AppendUnique(SWIGFLAGS=['-py3']) verbose = main_env['verbose'] if not verbose: env_swig.AppendUnique(SWIGFLAGS=['-w325', '-w315']) env_swig.AppendUnique(CXXFLAGS=['-Wno-maybe-uninitialized']) # for some warning in wrapper code env_swig.SharedLibrary( target='_tamaas.so', source=['tamaas.i'], LIBS=['Tamaas'], RPATH=[abspath('../src')] ) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 2c4de46..4b09fcb 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -1,42 +1,101 @@ +from SCons.Script import * +from os.path import * + +import os + + def fftw(env): - """A Tool to search for fftw headers and libraries""" - - if env.GetOption('clean'): return - env.SetDefault(FFTW_VERSION='3') - env.SetDefault(FFTW_LIBRARY_WISH=[]) - - print("Building for fftw version {}".format(env['FFTW_VERSION'])) - - if 'FFTW_INCLUDE_DIR' in env: - env.AppendUnique(CPPPATH=env['FFTW_INCLUDE_DIR']) - - if 'FFTW_LIBRARY_DIR' in env: - env.AppendUnique(LIBPATH=env['FFTW_LIBRARY_DIR']) - - version = env['FFTW_VERSION'] - if version == "2": - lib_names = {'main':'fftw'} - inc_names = ['fftw.h'] - else: - lib_names = {'main':'fftw3','thread':'fftw3_threads','omp':'fftw3_omp'} - inc_names = ['fftw3.h'] - - try: - libs = [lib_names[i] for i in env['FFTW_LIBRARY_WISH']] - except: - raise SCons.Errors.StopError( - 'Incompatible wishlist {0} from version {1}'.format( - env['FFTW_LIBRARY_WISH'], env['FFTW_VERSION'])) - - env.AppendUnique(LIBS=libs) - if version == "2": - env.Append(LIBS='m') - - conf = Configure(env) - if not conf.CheckLibWithHeader(libs, inc_names, 'c++'): - raise SCons.Errors.StopError( - 'Failed to find libraries {0} or ' - 'headers {1}.'.format(str(lib_names), str(inc_names))) - - env = conf.Finish() - + """A Tool to search for fftw headers and libraries""" + + if env.GetOption('clean'): + return + env.SetDefault(FFTW_VERSION='3') + env.SetDefault(FFTW_LIBRARY_WISH=[]) + + print("Building for fftw version {}".format(env['FFTW_VERSION'])) + + if 'FFTW_INCLUDE_DIR' in env: + env.AppendUnique(CPPPATH=env['FFTW_INCLUDE_DIR']) + + if 'FFTW_LIBRARY_DIR' in env: + env.AppendUnique(LIBPATH=env['FFTW_LIBRARY_DIR']) + + version = env['FFTW_VERSION'] + if version == "2": + lib_names = {'main': 'fftw'} + inc_names = ['fftw.h'] + else: + lib_names = {'main': 'fftw3', + 'thread': 'fftw3_threads', + 'omp': 'fftw3_omp'} + inc_names = ['fftw3.h'] + + try: + libs = [lib_names[i] for i in env['FFTW_LIBRARY_WISH']] + except: + raise SCons.Errors.StopError( + 'Incompatible wishlist {0} from version {1}'.format( + env['FFTW_LIBRARY_WISH'], env['FFTW_VERSION'])) + + env.AppendUnique(LIBS=libs) + if version == "2": + env.Append(LIBS='m') + + conf = Configure(env) + if not conf.CheckLibWithHeader(libs, inc_names, 'c++'): + raise SCons.Errors.StopError( + 'Failed to find libraries {0} or ' + 'headers {1}.'.format(str(lib_names), str(inc_names))) + + env = conf.Finish() + + +def criterion(env): + """A tool to configure for Criterion (test suite)""" + if env.GetOption('clean'): + return + + env.AppendUnique(CPPPATH=[Dir("#third-party/Criterion/include")], + LIBPATH=[abspath(str(Dir("#third-party/Criterion/build")))]) + conf = Configure(env) + if not conf.CheckLibWithHeader('criterion', + 'criterion/criterion.h', + 'c++'): + raise SCons.Errors.StopError( + 'Failed to find library Criterion or criterion.h header in third-party') + env = conf.Finish() + + +def tamaas(env): + """Sets correct environement variables for Tamaas. + - 'TAMAAS_PATH' is list of potential tamaas repositories + - 'TAMAAS_BUILD_TYPE' is the build type of tamaas (release, debug, profiling)""" + if env.GetOption("clean"): + return + + try: + build_type = env['TAMAAS_BUILD_TYPE'] + except: + build_type = "release" + + if 'TAMAAS_PATH' not in env: + print "Please set the 'TAMAAS_PATH' variable in your scons environment" + Exit(1) + + tm_path = env['TAMAAS_PATH'] + include_dir = tm_path + '/src' + subdirs = "core model bem surface".split(" ") + include_dirs = [abspath(join(include_dir, x)) for x in subdirs] + lib_dir = join(tm_path, 'build-{}/src'.format(build_type)) + + env.AppendUnique(CPPPATH = include_dirs) + env.AppendUnique(LIBPATH = [abspath(lib_dir)]) + env.AppendUnique(RPATH = [abspath(lib_dir)]) + env.AppendUnique(CXXFLAGS = ["-std=c++14"]) + + conf = Configure(env) + if not conf.CheckLibWithHeader("Tamaas", "tamaas.hh", language="CXX"): + raise SCons.Errors.StopError( + "Tamaas ({}) not found in {}".format(build_type, abspath(tm_path))) + env['TAMAAS_PATH'] = abspath(tm_path) + env = conf.Finish() diff --git a/src/core/iterator.hh b/src/core/iterator.hh index 6f864d0..8ac90bd 100644 --- a/src/core/iterator.hh +++ b/src/core/iterator.hh @@ -1,190 +1,192 @@ /** * * @author Lucas Frérot * * @section LICENSE * * Copyright (©) 2016 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Tamaas is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Tamaas is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Tamaas. If not, see . * */ /* -------------------------------------------------------------------------- */ #ifndef __ITERATOR_HH__ #define __ITERATOR_HH__ /* -------------------------------------------------------------------------- */ #include "tamaas.hh" #include #include +#include #include /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ namespace iterator_ { template class iterator { public: typedef T value_type; typedef std::ptrdiff_t difference_type; typedef T * pointer; typedef T & reference; + typedef std::random_access_iterator_tag iterator_category; /* -------------------------------------------------------------------------- */ /* Constructors/Destructors */ /* -------------------------------------------------------------------------- */ /// constructor iterator(pointer data, std::size_t start, ptrdiff_t offset): data(data), index(start), step(offset), access_offset(0) {} /// copy constructor iterator(const iterator & o): data(o.data), index(o.index), step(o.step), access_offset(o.access_offset) {} /// destructor ~iterator() {} /// copy operator iterator & operator=(const iterator & o) { data = o.data; index = o.index; step = o.step; access_offset = o.access_offset; return *this; } protected: /// Compute access offset for data from index inline void computeAccessOffset() { access_offset = index; } public: /// dereference iterator inline reference operator*() { return data[access_offset]; } /// increment with given offset inline iterator & operator+=(difference_type a) { index += a*step; computeAccessOffset(); return *this; } /// increment iterator inline iterator & operator++() { *this += 1; return *this; } /// comparison inline bool operator<(const iterator & a) const { return index < a.index; } /// inequality inline bool operator!=(const iterator & a) const { return index != a.index; } /// equality inline bool operator==(const iterator & a) const { return index == a.index; } /// needed for OpenMP range calculations inline difference_type operator-(const iterator & a) const { return (index - a.index)/step; } protected: T * data; std::size_t index; difference_type step; difference_type access_offset; }; template class iterator_view : iterator { public: typedef T value_type; typedef std::ptrdiff_t difference_type; typedef T * pointer; typedef T & reference; /// Constructor iterator_view(pointer data, std::size_t start, ptrdiff_t offset, std::vector strides, std::vector sizes): iterator(data, start, offset), strides(strides), n(sizes), tuple(strides.size()) {} iterator_view(const iterator_view & o): iterator(o), strides(o.strides), n(o.n), tuple(o.tuple) {} iterator_view & operator=(const iterator_view & o) { iterator::operator=(o); strides = o.strides; n = o.n; tuple = o.tuple; } protected: void computeAccessOffset() { UInt index_copy = this->index; this->tuple.back() = index_copy % this->n.back(); index_copy -= this->tuple.back(); index_copy /= this->n.back(); /// Computing tuple from index for (UInt d = this->tuple.size()-2 ; d > 0 ; d--) { this->tuple[d] = index_copy % this->n[d]; index_copy -= this->tuple[d]; index_copy /= this->n[d]; } this->tuple[0] = index_copy; this->access_offset = 0; for (UInt i = 0 ; i < this->tuple.size() ; ++i) this->access_offset += this->tuple[i] * this->strides[i]; } std::vector strides; std::vector n; std::vector tuple; }; } // end namespace iterator_ __END_TAMAAS__ /* -------------------------------------------------------------------------- */ #endif // __ITERATOR_HH__ diff --git a/tests/SConscript b/tests/SConscript index 844950b..b02e9f8 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -1,30 +1,44 @@ from __future__ import print_function -from os.path import join +from os.path import join, abspath Import('main_env') -test_env = main_env.Clone(PRINT_CMD_LINE_FUNC=main_env['gen_print']("Copying", "red", main_env)) +print("Environment for tests") +test_env = main_env.Clone( + tools=[criterion], + PRINT_CMD_LINE_FUNC=main_env['gen_print']("Copying", "red", main_env)) test_files = Split(""" run_tests.sh test_hertz_pressure.py test_westergaard.py test_patch_westergaard.py test_surface.py test_autocorrelation.py test_hertz_disp.py test_hertz_kato.py test_hertz_adhesion.py test_saturated_pressure.py test_fftransform.py test_bem_grid.py test_flood_fill.py """) src_dir = "#/tests" build_dir = 'build-' + main_env['build_type'] + '/tests' for file in test_files: source = join(src_dir, file) test_env.Command(file, source, Copy("$TARGET", "$SOURCE")) + + +test_env.AppendUnique(LIBS=['Tamaas'], + LIBPATH=[abspath('build-' + main_env['build_type'] + '/src')]) + +cpp_test_files = Split(""" +test_grid.cpp +""") + +for file in cpp_test_files: + test_env.Program(file) diff --git a/tests/run_tests.sh b/tests/run_tests.sh index f00f38b..8bffa35 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -1,70 +1,96 @@ #!/bin/bash ######################################################################### function test_output { name=$( basename $1 .verified ) echo "Testing $name output" echo -n "------------ " diff $name.out $name.verified >/dev/null 2>&1 return $? } ######################################################################### function test_return { name=$( basename $1 .py ) echo "Testing $name.py" echo -n "------------ " PYTHONPATH=../python python $name.py >$name.out 2>$name.err return $? } ######################################################################### +function test_exec { + echo "Testing $1" + echo -n "------------ " + ./$1 >/dev/null 2>&1 + return $? +} +######################################################################### + tamaas_path=$(PYTHONPATH=../python python -c 'import tamaas; print(tamaas)' | cut -d "'" -f 4 ) tamaas_lib_path=$(ldd ../python/_tamaas.so | grep -e 'libTamaas.so' | cut -d " " -f 3) echo "Tamaas test suite" echo "=================" echo "" echo "importing tamaas from $tamaas_path" echo "importing library from $tamaas_lib_path" echo "" n_tests=0 passed_tests=0 + +for test_file in test_*; do + # Filtering files with an extension + if [[ ! $test_file =~ test_[^\.]+$ ]]; then + continue + fi + + ((n_tests++)) + test_exec $test_file + result=$? + if [ $result -ne 0 ]; then + echo "failure" + else + echo "success" + ((passed_tests++)) + fi +done + for test_file in test_*.py; do ((n_tests++)) test_return $test_file result=$? if [ $result -ne 0 ]; then echo "failure" else echo "success" ((passed_tests++)) fi done for test_file in test_*.verified; do # In case no .verified files exist if [ $test_file = "test_*.verified" ]; then break fi ((n_tests++)) test_output $test_file result=$? if [ $result -ne 0 ]; then echo "failure" else echo "success" ((passed_tests++)) fi done failed_tests=$((n_tests - passed_tests)) echo "" echo "Test results :" echo " - passed $passed_tests/$n_tests" echo " - failed $failed_tests/$n_tests" diff --git a/tests/test_grid.cpp b/tests/test_grid.cpp new file mode 100644 index 0000000..a3cf2d0 --- /dev/null +++ b/tests/test_grid.cpp @@ -0,0 +1,147 @@ +/** + * + * @author Lucas Frérot + * + * @section LICENSE + * + * Copyright (©) 2016 EPFL (Ecole Polytechnique Fédérale de + * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des + * Solides) + * + * Tamaas is free software: you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * Tamaas is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Tamaas. If not, see . + * + */ +/* -------------------------------------------------------------------------- */ +#include +#include +#include +#include +#include "grid.hh" + +using namespace tamaas; + +void tamaas_init() { + initialize(); +} + +void tamaas_teardown() { + finalize(); +} + +/* -------------------------------------------------------------------------- */ + +TestSuite(grid_creation, .init = tamaas_init, .fini = tamaas_teardown); + +// Testing 1D creation +Test(grid_creation, creation_1d) { + Grid grid({5}, 2); + std::array correct_size = {5}; + cr_assert(grid.sizes() == correct_size, "Wrong sizes"); + + std::array correct_strides = {2, 1}; + cr_assert(grid.getStrides() == correct_strides, "Wrong strides"); +} + +// Testing 2D creation +Test(grid_creation, creation_2d) { + Grid grid({5, 2}, 3); + std::array correct_size = {5, 2}; + cr_assert(grid.sizes() == correct_size, "Wrong sizes"); + + std::array correct_strides = {6, 3, 1}; + cr_assert(grid.getStrides() == correct_strides, "Wrong strides"); +} + +// Testing 3D creation +Test(grid_creation, creation_3d) { + Grid grid({8, 5, 2}, 3); + std::array correct_size = {8, 5, 2}; + cr_assert(grid.sizes() == correct_size, "Wrong sizes"); + + std::array correct_strides = {30, 15, 3, 1}; + cr_assert(grid.getStrides() == correct_strides, "Wrong strides"); +} + +/* -------------------------------------------------------------------------- */ + +TestSuite(grid_iterators, .init = tamaas_init, .fini = tamaas_teardown); + +// Testing iterators in STL function iota +Test(grid_iterators, iota) { + constexpr UInt N = 20; + Grid grid({N}, 1); + std::iota(grid.begin(), grid.end(), 0); + + const UInt * p = grid.getInternalData(); + for (UInt i = 0 ; i < N ; ++i) + cr_assert(p[i] == i, "Iota fill failed"); +} + +// Testing filling grid with OpenMP loop +Test(grid_iterators, openmp_loops) { + Grid grid({20}, 1); + +#pragma omp parallel for + for (auto it = grid.begin() ; it < grid.end() ; ++it) { + UInt i = it - grid.begin(); + *it = i; + } + + std::vector correct(20); + std::iota(correct.begin(), correct.end(), 0); + + cr_assert(std::mismatch(correct.begin(), correct.end(), grid.begin(), grid.end()) + == std::make_pair(correct.end(), grid.end()), + "Fill using OpenMP loop failed"); +} + +/* -------------------------------------------------------------------------- */ + +TestSuite(grid_operators, .init = tamaas_init, .fini = tamaas_teardown); + +// Testing scalar simple loop-based operators +Test(grid_operators, loop_operators) { + Grid grid({20}, 1); + grid = 1; + cr_expect(std::all_of(grid.begin(), grid.end(), [](UInt x){return x == 1;}), + "Assignement operator failed"); + grid += 1; + cr_expect(std::all_of(grid.begin(), grid.end(), [](UInt x){return x == 2;}), + "Plus-equal operator failed"); +} + +// Testing loop-based functions with reductions +Test(grid_operators, stats) { + constexpr UInt N = 20; + Grid grid({N}, 1); + std::iota(grid.begin(), grid.end(), 0); + + auto b = grid.begin(), e = grid.end(); + Real min = *std::min_element(b, e); + Real max = *std::max_element(b, e); + Real acc = std::accumulate(b, e, 0); + Real mea = acc / N; + Real var = 35; + + cr_expect(grid.min() == min, + "Minimum function failed"); + cr_expect(grid.max() == max, + "Maximum function failed"); + cr_expect(grid.sum() == acc, + "Sum function failed"); + cr_expect(grid.mean() == mea, + "Mean function failed"); + cr_expect(grid.var() == var, + "Var function failed"); +}