diff --git a/src/mpi/partitioner.hh b/src/mpi/partitioner.hh
index 75632d3..85b8cb2 100644
--- a/src/mpi/partitioner.hh
+++ b/src/mpi/partitioner.hh
@@ -1,64 +1,80 @@
/**
* @file
* @section LICENSE
*
* Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne),
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
*/
/* -------------------------------------------------------------------------- */
#ifndef PARTITIONER_HH
#define PARTITIONER_HH
/* -------------------------------------------------------------------------- */
#include "fftw_mpi_interface.hh"
#include "mpi_interface.hh"
#include "tamaas.hh"
#include "grid.hh"
#include
#include
#include
/* -------------------------------------------------------------------------- */
namespace tamaas {
template
struct Partitioner {
static decltype(auto) global_size(std::array local) {
local.front() = mpi::allreduce(local.front());
return local;
}
+ template
+ static decltype(auto) global_size(const Grid& grid) {
+ return global_size(grid.sizes());
+ }
+
static decltype(auto) local_size(std::array global) {
auto tup =
fftw::mpi::local_size_many(dim, cast_size(global).data(), 1);
global.front() = static_cast(std::get<1>(tup));
return global;
}
+ template
+ static decltype(auto) local_size(const Grid& grid) {
+ return local_size(grid.sizes());
+ }
+
static decltype(auto) local_offset(const std::array& global) {
auto tup =
fftw::mpi::local_size_many(dim, cast_size(global).data(), 1);
- return static_cast(std::get<2>(tup));
+ return static_cast(std::get<2>(tup));
+ }
+
+ template
+ static decltype(auto) local_offset(const Grid& grid) {
+ auto offset = local_offset(global_size(grid.sizes()));
+ return offset * grid.getStrides().front();
}
static decltype(auto) cast_size(const std::array& s) {
std::array n;
std::copy(s.begin(), s.end(), n.begin());
return n;
}
};
} // namespace tamaas
#endif
diff --git a/tests/SConscript b/tests/SConscript
index 6c1ceba..c6b9cc7 100644
--- a/tests/SConscript
+++ b/tests/SConscript
@@ -1,214 +1,217 @@
# -*- mode:python; coding: utf-8 -*-
# vim: set ft=python:
# @file
# @section LICENSE
#
# Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne),
# Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
from __future__ import print_function
from os.path import join
from SCons.Script import Split, Copy, Dir, Import
from detect import FindGTest, FindPybind11
# ------------------------------------------------------------------------------
def copyComStr(env, main):
if 'SHCXXCOMSTR' in main:
env['CXXCOMSTR'] = main['SHCXXCOMSTR']
if 'SHLINKCOMSTR' in main:
env['LINKCOMSTR'] = main['SHLINKCOMSTR']
# ------------------------------------------------------------------------------
def make_python_tests(env):
"""Copy python tests to build directory"""
test_env = env.Clone()
test_files = Split("""
test_hertz.py
test_westergaard.py
test_patch_westergaard.py
test_patch_plasticity.py
test_surface.py
test_hertz_disp.py
test_hertz_kato.py
test_saturated_pressure.py
test_flood_fill.py
test_integral_operators.py
test_dumper.py
test_tangential.py
test_boussinesq_surface.py
test_voigt.py
test_memory.py
test_epic.py
fftfreq.py
conftest.py
pytest.ini
""")
if env['legacy_bem']:
test_files += ['test_bem_grid.py', 'test_autocorrelation.py']
src_dir = "#/tests"
targets = [
test_env.Command(file, join(src_dir, file),
Copy("$TARGET", "$SOURCE"))
for file in test_files
]
test_env = env.Clone(tools=[pybind11])
# Helper module for integral operators
test_env['SHLIBPREFIX'] = ''
register = test_env.Pybind11Module(
target="register_integral_operators",
source=["register_integral_operators.cpp"],
LIBS=['Tamaas'])
Import('libTamaas')
test_env.Depends(register, libTamaas)
targets.append(register)
return targets
# ------------------------------------------------------------------------------
def compile_google_test(env, gtest_path):
gtest_obj = env.Object('gtest.o', [join(gtest_path, "src/gtest-all.cc")])
return env.StaticLibrary('gtest', gtest_obj)
# ------------------------------------------------------------------------------
def make_google_tests(env):
gtest_dir = Dir(env['GTEST_ROOT'])
gtest_env = env.Clone(CPPPATH=[gtest_dir],
CXXFLAGS=['-pthread', '-isystem',
join(str(gtest_dir), "include")])
colors = env['COLOR_DICT']
if not env['verbose']:
gtest_env['ARCOMSTR'] = u'{}[Ar]{} $TARGET'.format(colors['purple'],
colors['end'])
gtest_env['RANLIBCOMSTR'] = \
u'{}[Randlib]{} $TARGET'.format(colors['purple'],
colors['end'])
FindGTest(gtest_env)
libgtest = None
# Hugly hack to detect if we need to compile gtest submodule
if env['GTEST_ROOT'] == '#third-party/googletest/googletest':
gtest_path = str(gtest_dir)
libgtest = compile_google_test(gtest_env, gtest_path)
env.AppendUnique(LIBS=['Tamaas'],
CXXFLAGS=gtest_env['CXXFLAGS'])
google_test_files = Split("""
test_fft.cpp
test_grid.cpp
test_loop.cpp
test_model.cpp
test_static_types.cpp
test_integration.cpp
""")
# Necessary for the tests that use pybind11 calls to python
- use_python = []
+ uses = []
if env['build_python']:
google_test_files.append('test_fftfreq.cpp')
- use_python = ['USE_PYTHON']
+ uses.append('USE_PYTHON')
+
+ if env['use_mpi']:
+ google_test_files.append('test_mpi.cpp')
defines = env['CPPDEFINES']
if type(defines) is not list:
defines = [defines]
gtest_main = env.Object("tamaas_gtest_main.o", 'tamaas_gtest_main.cc',
- CPPDEFINES=defines + use_python)
+ CPPDEFINES=defines + uses)
gtest_all = env.Program('test_gtest_all', google_test_files + [gtest_main],
LIBS=(env['LIBS'] + ['gtest']))
Import('libTamaas')
env.Depends(gtest_all, libTamaas)
env.Depends(gtest_all, libgtest)
return [gtest_all]
# ------------------------------------------------------------------------------
def make_bare_tests(env):
rough = env.Program("test_rough_surface.cpp")
Import('libTamaas')
env.Depends(rough, libTamaas)
return [rough]
# ------------------------------------------------------------------------------
Import('main_env')
# Setup of test environment
test_env = main_env.Clone()
test_env.AppendUnique(LIBS=['Tamaas'], LIBPATH=['.'])
# Building tests that do not require any third party
targets = make_bare_tests(test_env)
# Build tests that required python bindings
if test_env['build_python']:
FindPybind11(test_env)
test_env.Tool(pybind11)
test_env.ParseConfig("${py_exec}-config --ldflags")
test_env['CCFLAGS'] = []
targets += make_python_tests(test_env)
# Building google tests
if test_env['use_googletest']:
targets += make_google_tests(test_env)
targets.append(test_env.Command('test_gtest.py', '#tests/test_gtest.py',
Copy('$TARGET', '$SOURCE')))
# Target alias to build tests
main_env.Alias('build-tests', targets)
# Check if pytest is installed
conf = Configure(main_env,
custom_tests={'CheckPythonModule': CheckPythonModule})
has_pytest = conf.CheckPythonModule('pytest')
conf.Finish()
# Define a command to execute tests
if has_pytest:
pytest_env = main_env.Clone()
pytest_env.PrependENVPath('PYTHONPATH',
pytest_env.subst('${build_dir}/python'))
pytest_env.PrependENVPath('LD_LIBRARY_PATH',
pytest_env.subst('${build_dir}/src'))
test_target = pytest_env.Command('#/.phony_test', targets,
'${py_exec} -m pytest ${build_dir}/tests')
main_env.Alias('test', test_target)
else:
# We still define a target here so that `scons test` still works
dummy_command(main_env, 'test',
'Cannot run tests: pytest is not installed')
diff --git a/tests/test_mpi.cpp b/tests/test_mpi.cpp
new file mode 100644
index 0000000..85d55f4
--- /dev/null
+++ b/tests/test_mpi.cpp
@@ -0,0 +1,88 @@
+/**
+ * @file
+ * @section LICENSE
+ *
+ * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne),
+ * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+/* -------------------------------------------------------------------------- */
+#include "fft_mpi_engine.hh"
+#include "grid.hh"
+#include "grid_hermitian.hh"
+#include "grid_view.hh"
+#include "partitioner.hh"
+#include "test.hh"
+
+#include
+
+using namespace tamaas;
+using fft = fftw::helper;
+
+/* -------------------------------------------------------------------------- */
+
+template
+struct span {
+ T* ptr;
+ std::size_t size;
+
+ ~span() { fftw::free(ptr); }
+ const T* begin() const { return ptr; }
+ const T* end() const { return ptr + size; }
+ T* begin() { return ptr; }
+ T* end() { return ptr + size; }
+
+ operator T*() { return ptr; }
+};
+
+/* -------------------------------------------------------------------------- */
+TEST(TestFFTWInterface, MPISizes) {
+ const std::ptrdiff_t N0 = 100, N1 = 100;
+ std::ptrdiff_t local_n0, local_n0_start;
+ auto alloc_local = fftw_mpi_local_size_2d(N0, N1 / 2 + 1, MPI_COMM_WORLD,
+ &local_n0, &local_n0_start);
+
+ const std::ptrdiff_t N[] = {N0, N1 / 2 + 1};
+ auto sizes = fftw::mpi::local_size_many(2, N, 1);
+
+ ASSERT_EQ(std::get<0>(sizes), alloc_local);
+ ASSERT_EQ(std::get<1>(sizes), local_n0);
+ ASSERT_EQ(std::get<2>(sizes), local_n0_start);
+}
+
+/* -------------------------------------------------------------------------- */
+TEST(TestPartitioner, LocalSizes) {
+ const std::ptrdiff_t N0 = 100, N1 = 100;
+ std::ptrdiff_t local_n0, local_n0_start;
+ fftw_mpi_local_size_2d(N0, N1 / 2 + 1, MPI_COMM_WORLD, &local_n0,
+ &local_n0_start);
+
+ auto local_size = Partitioner<2>::local_size({N0, N1 / 2 + 1});
+ ASSERT_EQ(local_size[0], local_n0);
+ ASSERT_EQ(local_size[1], N1 / 2 + 1);
+ ASSERT_EQ(Partitioner<2>::local_offset({N0, N1 / 2 + 1}), local_n0_start);
+}
+
+/* -------------------------------------------------------------------------- */
+TEST(TestPartitioner, LocalOffsetInGrid) {
+ std::array N = {10, 10};
+ std::array Nglobal = {mpi::allreduce(N.front()), N.back()};
+
+ Grid local(N, 1), global(Nglobal, 1);
+ auto offset = Partitioner<2>::local_offset(local);
+ auto local_n0_start = Partitioner<2>::local_offset(Nglobal);
+ ASSERT_EQ(offset, &global(local_n0_start, 0) - &global(0, 0));
+}