diff --git a/SConstruct b/SConstruct
index b6ff920..4b46b08 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1,311 +1,313 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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 .
# ------------------------------------------------------------------------------
# Imports
# ------------------------------------------------------------------------------
from __future__ import print_function
import os
from os.path import join, abspath
from version import write_info_file
from detect import FindFFTW, FindBoost, FindThrust, FindCuda, FindExpolit
# ------------------------------------------------------------------------------
EnsurePythonVersion(2, 7)
EnsureSConsVersion(2, 4)
# ------------------------------------------------------------------------------
def detect_dependencies(env):
"""Detect all dependencies"""
FindFFTW(env, ['omp'])
FindBoost(env, ['boost/preprocessor/seq.hpp'])
FindExpolit(env)
thrust_var = 'THRUST_ROOT'
# Take cuda version of thrust if available
if 'CUDA_ROOT' in env['ENV']:
thrust_var = 'CUDA_ROOT'
FindThrust(env, env['backend'], thrust_var)
# Activate cuda if needed
if env['backend'] == 'cuda':
FindCuda(env)
# ------------------------------------------------------------------------------
# Main compilation
# ------------------------------------------------------------------------------
# Compilation colors
colors = {
'cyan': '\033[96m',
'purple': '\033[95m',
'blue': '\033[94m',
'green': '\033[92m',
'yellow': '\033[93m',
'red': '\033[91m',
'end': '\033[0m'
}
# Inherit all environment variables (for CXX detection, etc.)
main_env = Environment(ENV=os.environ)
main_env['COLOR_DICT'] = colors
# Compiler detection
compiler_default = os.getenv('CXX', 'g++')
# Build variables
vars = Variables('build-setup.conf')
vars.AddVariables(
EnumVariable('build_type', 'Build type', 'release',
allowed_values=('release', 'profiling', 'debug'),
ignorecase=2),
EnumVariable('backend', 'Thrust backend', 'omp',
allowed_values=('omp'),
# allowed_values=('omp', 'cuda'),
ignorecase=2),
EnumVariable('sanitizer', 'Sanitizer type', 'none',
allowed_values=('none', 'memory', 'leaks', 'address'),
ignorecase=2),
PathVariable('prefix',
'Prefix where to install', '/usr/local'),
# Dependencies paths
PathVariable('FFTW_ROOT',
'FFTW custom path', os.getenv('FFTW_ROOT', ''),
PathVariable.PathAccept),
PathVariable('THRUST_ROOT',
'Thrust custom path', os.getenv('THRUST_ROOT', ''),
PathVariable.PathAccept),
PathVariable('BOOST_ROOT',
'Boost custom path', os.getenv('BOOST_ROOT', ''),
PathVariable.PathAccept),
PathVariable('CUDA_ROOT',
'Cuda custom path', os.getenv('CUDA_ROOT', ''),
PathVariable.PathAccept),
# Dependencies provided as submodule get different default
PathVariable('GTEST_ROOT',
'Googletest custom path',
os.getenv('GTEST_ROOT', '#third-party/googletest/googletest'),
PathVariable.PathAccept),
PathVariable('PYBIND11_ROOT',
'Pybind11 custom path',
os.getenv('PYBIND11_ROOT', '#third-party/pybind11/include'),
PathVariable.PathAccept),
PathVariable('EXPOLIT_ROOT',
'Expolit custom path',
os.getenv('EXPOLIT_ROOT', '#third-party/expolit/include'),
PathVariable.PathAccept),
# Executables
('CXX', 'Compiler', compiler_default),
('py_exec', 'Python executable', 'python3'),
# Cosmetic
BoolVariable('verbose', 'Activate verbosity', False),
BoolVariable('color', 'Color the non-verbose compilation output', False),
# Tamaas components
BoolVariable('build_doc', 'Build documentation', False),
BoolVariable('build_tests', 'Build test suite', False),
BoolVariable('build_python', 'Build python wrapper', True),
# Dependencies
BoolVariable('use_googletest', 'Build tests using GTest', False)
)
# Set variables of environment
vars.Update(main_env)
Help(vars.GenerateHelpText(main_env))
# Save all options, not just those that differ from default
with open('build-setup.conf', 'w') as setup:
for key in vars.keys():
setup.write("{} = '{}'\n".format(key, main_env[key]))
main_env['should_configure'] = \
not main_env.GetOption('clean') and not main_env.GetOption('help')
build_type = main_env['build_type']
build_dir = 'build-' + main_env['build_type']
if main_env['should_configure']:
print("Building in " + build_dir)
verbose = main_env['verbose']
# Remove colors if not set
if not main_env['color']:
for key in colors:
colors[key] = ''
if not verbose:
main_env['SHCXXCOMSTR'] = \
u'{0}[Compiling ($SHCXX)] {1}$SOURCE'.format(colors['green'],
colors['end'])
main_env['SHLINKCOMSTR'] = \
u'{0}[Linking] {1}$TARGET'.format(colors['purple'],
colors['end'])
# Include paths
main_env.AppendUnique(CPPPATH=['#/src',
'#/src/core',
'#/src/bem',
'#/src/surface',
'#/src/python',
'#/src/percolation',
'#/src/model',
'#/src/model/elasto_plastic',
'#/src/solvers',
'#/src/gpu',
'#/python'])
# Changing the shared object extension
main_env['SHOBJSUFFIX'] = '.o'
# Back to gcc if cuda is activated
if main_env['backend'] == "cuda":
main_env['CXX'] = "g++"
compiler_aliases = {
"c++": "g++",
"g++-7": "g++",
"g++-6": "g++",
"clang++-6.0": "clang++",
}
def cxx_alias(alias):
return compiler_aliases.get(alias, alias)
# OpenMP flags - compiler dependent
omp_libs = {
"g++": ["gomp"],
"clang++": [],
"icpc": []
}
omp_flags = {
"g++": ["-fopenmp"],
"clang++": ["-fopenmp=libomp"],
"icpc": ["-qopenmp"]
}
cxx = cxx_alias(main_env['CXX'])
omp_lib = omp_libs[cxx]
omp_flag = omp_flags[cxx]
main_env.AppendUnique(LIBS=omp_lib)
main_env.AppendUnique(LINKFLAGS=omp_flag)
# Flags and options
main_env.AppendUnique(CXXFLAGS=['-std=c++14',
'-Wall',
omp_flag])
if main_env['build_python']:
main_env.AppendUnique(CPPDEFINES=['USE_PYTHON'])
# Adding compile flags defined in evironment
main_env.AppendUnique(CXXFLAGS=Split(os.getenv('CXXFLAGS', "")))
if build_type == 'debug':
main_env.AppendUnique(CPPDEFINES=['TAMAAS_DEBUG'])
# Compilation flags
cxxflags_dict = {
"debug": Split("-g -O0"),
"profiling": Split("-g -O3 -fno-omit-frame-pointer"),
"release": Split("-O3")
}
# Link flags for shared libs
shlinkflags_dict = {
"debug": Split(""),
"profiling": Split("-g -O3 -fno-omit-frame-pointer"),
"release": []
}
if main_env['sanitizer'] != 'none':
if main_env['backend'] == 'cuda':
print("Sanitizers with cuda are not yet supported!")
Exit(1)
cxxflags_dict[build_type].append(
'-fsanitize={}'.format(main_env['sanitizer']))
shlinkflags_dict[build_type].append(
'-fsanitize={}'.format(main_env['sanitizer']))
main_env.AppendUnique(CXXFLAGS=cxxflags_dict[build_type])
main_env.AppendUnique(SHLINKFLAGS=shlinkflags_dict[build_type])
main_env.AppendUnique(LINKFLAGS=shlinkflags_dict[build_type])
main_env['LIBPATH'] = [abspath(join(build_dir, 'src'))]
main_env['RPATH'] = "$LIBPATH"
if main_env['should_configure']:
detect_dependencies(main_env)
# Writing information file
write_info_file("src/tamaas_info.cpp", main_env['build_type'])
def write_env_file(target, source, env):
"""Builder to write content to file"""
env_content = """
export PYTHONPATH=$PYTHONPATH:{0}/python
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{0}/src
"""
with open(str(target[0]), 'w') as env_file:
env_file.write(env_content.format(abspath(build_dir)))
# Saving the env file
main_env['gen_print'] = gen_print
env_file_env = main_env.Clone(
PRINT_CMD_LINE_FUNC=gen_print("Writing", "cyan", main_env)
)
# Need to have a command and manage tamaas_environement.sh as target because
# the build directory does not always exist
env_file_env.Command(join(build_dir, 'tamaas_environement.sh'),
None, write_env_file)
# Building subdirs
def subdir(dir):
return SConscript(join(dir, 'SConscript'),
variant_dir=join(build_dir, dir),
duplicate=True)
# Building Tamaas library
Export('main_env')
subdir('src')
# Building Tamaas extra components
for dir in ['python', 'tests', 'doc']:
if main_env['build_{}'.format(dir)] and not main_env.GetOption('help'):
subdir(dir)
diff --git a/doc/SConscript b/doc/SConscript
index 84fc09d..48ed23c 100644
--- a/doc/SConscript
+++ b/doc/SConscript
@@ -1,35 +1,56 @@
+# -*- coding: utf-8 -*-
+
+# @file
+# @section LICENSE
+#
+# Copyright (©) 2016-19 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 os.path import join, exists
from subprocess import call
Import('main_env')
doxygen_tool = str(Dir('#site_scons/site_tools/doxygen'))
if not exists(doxygen_tool):
print("Cloning scons doxygen builder")
# Checking if Mercurial is installed
env = Environment()
conf = Configure(env)
if not conf.CheckProg('hg'):
print("Could not find Mercurial needed to clone the doxygen tool")
Exit(1)
try:
call(['hg', 'clone', 'https://bitbucket.org/russel/scons_doxygen', doxygen_tool])
except:
print("Error while running Mercurial to clone the doxygen tool")
Exit(1)
env_doxygen = main_env.Clone(
tools = ['default', 'doxygen']
)
src_dir = "#/doc/doxygen"
file = "Doxyfile"
build_dir = 'build-' + main_env['build_type'] + '/doc/doxygen'
Command(join("doxygen", file), join(src_dir, file), [
Mkdir(build_dir),
Copy("$TARGET", "$SOURCE")
]
)
env_doxygen.Doxygen("doxygen/Doxyfile")
diff --git a/examples/SConstruct b/examples/SConstruct
index 7866343..00a3f4d 100644
--- a/examples/SConstruct
+++ b/examples/SConstruct
@@ -1,28 +1,30 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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 .
import os
env = Environment(CC = 'g++',
CXXFLAGS = ['-std=c++11', '-g', '-fopenmp', '-O3'],
TOOLS = ['default', 'tamaas'],
TAMAAS_PATH = '../build-release/',
ENV = os.environ)
env.Program('rough_surface.cc')
diff --git a/python/SConscript b/python/SConscript
index 06764c5..705549f 100644
--- a/python/SConscript
+++ b/python/SConscript
@@ -1,67 +1,67 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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
-import SCons
-
-from os.path import abspath, join
-from SCons.Script import Import, Depends, Split, Copy
+from os.path import abspath
+from SCons.Script import Import, Split, Copy
Import('main_env')
# Pybind11 wrapper
env_pybind = main_env.Clone(SHLIBPREFIX='')
env_pybind.Tool(pybind11)
pybind_sources = Split("""
tamaas_module.cpp
wrap/core.cpp
wrap/percolation.cpp
wrap/surface.cpp
wrap/model.cpp
wrap/solvers.cpp
wrap/test_features.cpp
""")
if main_env['CXX'] != 'icpc':
pybind_sources += ["wrap/bem.cpp"]
env_pybind.AppendUnique(CPPDEFINES="LEGACY_BEM")
# Building the pybind library
tamaas_wrap = env_pybind.SharedLibrary(
target='tamaas/_tamaas',
source=pybind_sources,
LIBS=['Tamaas'],
RPATH=[abspath('../src')]
)
# For some reason link happens too early
Import('libTamaas')
env_pybind.Depends(tamaas_wrap, libTamaas)
# Copying the __init__.py file with extra python classes
copy_env = env_pybind.Clone(
PRINT_CMD_LINE_FUNC=main_env['gen_print']("Copying", "red", main_env))
copy_env.Command(Dir('tamaas'), Dir('#python/tamaas'), Copy("$TARGET", "$SOURCE"))
copy_env.Command('setup.py', '#python/setup.py', Copy("$TARGET", "$SOURCE"))
# Cleaning the tamaas python package
main_env.Execute(Delete('tamaas'))
diff --git a/site_scons/detect.py b/site_scons/detect.py
index 535712a..1a6d06d 100644
--- a/site_scons/detect.py
+++ b/site_scons/detect.py
@@ -1,208 +1,210 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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 .
import SCons
from os.path import join, abspath, exists, isdir, basename
from SCons.Script import Configure
# ------------------------------------------------------------------------------
def _get_path(env, ext, module_var):
path = ""
if module_var in env and env[module_var] != "":
root = abspath(env[module_var])
if not exists(root) or not isdir(root):
raise RuntimeError("{} is set to a non-existing path '{}'".format(
module_var, root))
path = join(root, ext)
if not exists(path) or not isdir(path):
raise RuntimeError("{} does not contain '{}' directory".format(
module_var, ext))
return [path]
# ------------------------------------------------------------------------------
def FindFFTW(env, components=[], module_var='FFTW_ROOT'):
"""Find FFTW3 and set relevant environment variables"""
if not env.get('should_configure', True):
return
fftw_vars = {}
fftw_vars['CPPPATH'] = _get_path(env, 'include', module_var)
fftw_vars['LIBPATH'] = _get_path(env, 'lib', module_var)
# Setting up FFTW
wishes = ['main'] + components
# Components
lib_names = {'main': 'fftw3',
'thread': 'fftw3_threads',
'omp': 'fftw3_omp'}
inc_names = ['fftw3.h']
# Checking list of wishes
try:
libs = [lib_names[i] for i in wishes]
except KeyError:
raise SCons.Errors.StopError(
'Incompatible FFTW wishlist {0}'.format(wishes))
fftw_vars['LIBS'] = libs
conf_env = env.Clone(**fftw_vars)
conf = Configure(conf_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)))
conf_env = conf.Finish()
# Update modified variables
env.AppendUnique(**fftw_vars)
# ------------------------------------------------------------------------------
def FindBoost(env, headers=['boost/version.hpp'], module_var='BOOST_ROOT'):
"""Find Boost and set relevant environment variables"""
if not env.get('should_configure', True):
return
boost_vars = {}
boost_vars['CPPPATH'] = _get_path(env, 'include', module_var)
conf_env = env.Clone(**boost_vars)
conf = Configure(conf_env)
if not conf.CheckCXXHeader(headers):
raise SCons.Errors.StopError(
'Failed to find Boost headers {}'.format(headers))
conf_env = conf.Finish()
# Update modified variables
env.AppendUnique(**boost_vars)
# ------------------------------------------------------------------------------
def FindThrust(env, backend='omp', module_var='THRUST_ROOT'):
"""Find Thrust and set relevant environment variables"""
if not env.get('should_configure', True):
return
thrust_vars = {}
thrust_vars['CPPPATH'] = _get_path(env, 'include', module_var)
if basename(env['CXX']) == "clang++":
thrust_vars['CXXFLAGS'] = ["-Wno-unused-local-typedef"]
if backend == 'omp':
thrust_vars['CPPDEFINES'] = [
"THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_OMP"
]
elif backend == 'cuda':
thrust_vars['CPPDEFINES'] = [
"THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_CUDA",
"USE_CUDA"
]
else:
raise SCons.Errors.StopError(
'Unknown thrust backend "{}"'.format(backend))
conf_env = env.Clone(**thrust_vars)
conf = Configure(conf_env)
if not conf.CheckCXXHeader('thrust/version.h'):
raise SCons.Errors.StopError(
'Failed to find Thrust')
conf_env = conf.Finish()
# Update modified variables
env.AppendUnique(**thrust_vars)
# ------------------------------------------------------------------------------
def FindCuda(env, module_var="CUDA_ROOT"):
"""Detect cuda on clusters"""
if not env.get('should_configure', True):
return
if 'CUDA_ROOT' in env:
env['CUDA_TOOLKIT_PATH'] = _get_path(env, '', module_var)
else:
env['CUDA_TOOLKIT_PATH'] = '/opt/cuda'
env['CUDA_COMPONENTS'] = ['cufft']
env['CUDA_ARCH_FLAG'] = '-arch=sm_60'
colors = env['COLOR_DICT']
if not env['verbose']:
env['NVCCCOMSTR'] = env['SHCXXCOMSTR']
env['SHLINKCOMSTR'] = \
u'{0}[Linking (cuda)] {1}$TARGET{2}'.format(colors['purple'],
colors['blue'],
colors['end'])
env.AppendUnique(CXXFLAGS=[
"-expt-extended-lambda", # experimental lambda support
"-expt-relaxed-constexpr", # experimental constexpr
])
if env['build_type'] == 'debug':
env.AppendUnique(CXXFLAGS="-G")
env.Tool('nvcc')
# ------------------------------------------------------------------------------
def FindGTest(env):
"""A tool to configure GoogleTest"""
if not env.get('should_configure', True):
return
conf = Configure(env)
if not conf.CheckCXXHeader('gtest/gtest.h'):
raise SCons.Errors.StopError(
'Failed to find GoogleTest header\n' +
"Run 'git submodule update --init --recursive " +
"third-party/googletest'")
env = conf.Finish()
# ------------------------------------------------------------------------------
def FindExpolit(env):
"""A tool to configure Expolit"""
if not env.get('should_configure', True):
return
expolit_vars = {"CPPPATH": "#/third-party/expolit/include"}
conf_env = env.Clone(**expolit_vars)
conf = Configure(conf_env)
if not conf.CheckCXXHeader('expolit/expolit'):
raise SCons.Errors.StopError(
'Failed to find Expolit header\n' +
"Run 'git submodule update --init " +
"third-party/expolit'")
conf_env = conf.Finish()
env.AppendUnique(**expolit_vars)
diff --git a/site_scons/site_init.py b/site_scons/site_init.py
index 7dfc465..7f82851 100644
--- a/site_scons/site_init.py
+++ b/site_scons/site_init.py
@@ -1,74 +1,76 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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 .
import SCons
from SCons.Script import Dir, Configure
from subprocess import check_output
# ------------------------------------------------------------------------------
def pybind11(env):
"""A tool to configure pybind11"""
if env.GetOption('clean'):
return
# 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([env['py_exec'], "-c", versions_script],
universal_newlines=True).split('\n')
includes += [Dir(env['PYBIND11_ROOT'])]
# Extension of shared library for python
extension = check_output([env['py_exec'] + '-config', "--extension-suffix"],
universal_newlines=True).split('\n')[0]
env['SHLIBSUFFIX'] = extension
env.AppendUnique(CPPPATH=includes)
conf = Configure(env)
if not conf.CheckCXXHeader('pybind11/pybind11.h'):
raise SCons.Errors.StopError(
'Failed to find pybind11 header\n' +
"Run 'git submodule update --init --recursive " +
"third-party/pybind11'")
env = conf.Finish()
# ------------------------------------------------------------------------------
def gen_print(action_string, color_string, env):
"""Generic function for creating pretty compile output"""
if env['verbose']:
return None
def print_fun(command, target, source, env):
colors = env['COLOR_DICT']
print("{}[{}] {}{}".format(colors[color_string],
action_string,
colors['end'],
target[0]))
return print_fun
diff --git a/site_scons/site_tools/nvcc.py b/site_scons/site_tools/nvcc.py
index 06dce5d..a385268 100644
--- a/site_scons/site_tools/nvcc.py
+++ b/site_scons/site_tools/nvcc.py
@@ -1,203 +1,205 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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 .
"""
SCons tool to setup compilation environment for NVidia's NVCC
Based on version
https://github.com/thrust/thrust/blob/master/site_scons/site_tools/nvcc.py
"""
from __future__ import print_function
import SCons
from SCons.Errors import StopError
import platform
from os.path import join
known_components = {
'cufft': 'cufft.h',
'cufftw': 'cufftw.h'
}
def _find_cuda_paths(env):
"""Finding cuda paths"""
if 'CUDA_TOOLKIT_PATH' not in env:
raise StopError("CUDA_TOOLKIT_PATH variable not found in environment")
cuda_path = env['CUDA_TOOLKIT_PATH']
inc_path = join(cuda_path, 'include')
lib_path = join(cuda_path, 'lib')
bin_path = join(cuda_path, 'bin')
if platform.machine()[-2:] == '64':
lib_path += '64'
# add nvcc's location to PATH
env.PrependENVPath('PATH', bin_path)
# add the location of the cudart shared library to LD_LIBRARY_PATH as well
# this allows us to execute CUDA programs during the build
env.PrependENVPath('LD_LIBRARY_PATH', lib_path)
# add to include paths
env.AppendUnique(CPPPATH=[inc_path])
# add to library paths
env.AppendUnique(LIBPATH=[lib_path])
def _configure_cuda_components(env):
"""Finding required components in cuda"""
if env.GetOption('clean'):
return
if 'CUDA_COMPONENTS' in env:
for component in env['CUDA_COMPONENTS']:
if component not in known_components:
raise StopError("Unknown cuda component '{}'".format(component))
conf = SCons.Script.Configure(env)
if not conf.CheckLibWithHeader(component, known_components[component], 'c++'):
raise StopError("Failed to find library {} or header {}".format(
component, known_components[component]))
env = conf.Finish()
def _define_compile_cuda(env):
"""Define the compile string for files that need nvcc"""
env['NVCC'] = env.Detect('nvcc')
# Setting compilation command
env['NVCCCOM'] = \
'$NVCC $CUDA_ARCH_FLAG -o $TARGET -c $NVCC_CXXFLAGS $NVCC_CCFLAGS $_CCCOMCOM -x cu $SOURCES'
env['SHNVCCCOM'] = \
'$NVCC $CUDA_ARCH_FLAG -o $TARGET -dc -Xcompiler -fPIC $NVCC_CXXFLAGS $NVCC_CCFLAGS $_CCCOMCOM -x cu $SOURCES'
# Constructing proper cc flags
env['_NVCC_BARE_CCFLAGS'] = \
'${_concat("", CCFLAGS, "", __env__, _NVCC_BARE_FILTER)}'
env['_NVCC_PASSED_CCFLAGS'] = \
'${_concat("-Xcompiler ", CCFLAGS, "", __env__, _NVCC_COMPILER_PASSED_FILTER)}'
# Constructing proper cxx flags
env['_NVCC_BARE_CXXFLAGS'] = \
'${_concat("", CXXFLAGS, "", __env__, _NVCC_BARE_FILTER)}'
env['_NVCC_PASSED_CXXFLAGS'] = \
'${_concat("-Xcompiler ", CXXFLAGS, "", __env__, _NVCC_COMPILER_PASSED_FILTER)}'
# Putting all together
env['NVCC_CCFLAGS'] = '$_NVCC_BARE_CCFLAGS $_NVCC_PASSED_CCFLAGS'
env['NVCC_CXXFLAGS'] = '$_NVCC_BARE_CXXFLAGS $_NVCC_PASSED_CXXFLAGS'
def _define_link_cuda(env):
"""Define the link string"""
# Fixing rpaths
env['RPATHPREFIX'] = '-rpath='
env['__RPATH'] = '${_concat("-Xlinker ", _RPATH, "", __env__)}'
env['LINK'] = env['NVCC']
env['SHLINK'] = env['NVCC']
# Replacing old link command strings
env['LINKCOM'] = \
'$LINK $CUDA_ARCH_FLAG -o $TARGET $NVCC_LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
env['SHLINKCOM'] = \
'$SHLINK -shared $CUDA_ARCH_FLAG -o $TARGET $NVCC_SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
# Constructing proper static linker flags
env['_NVCC_BARE_LINKFLAGS'] = \
'${_concat("", LINKFLAGS, "", __env__, _NVCC_BARE_FILTER)}'
env['_NVCC_COMPILER_LINKFLAGS'] = \
'${_concat("-Xcompiler ", LINKFLAGS, "", __env__, _NVCC_COMPILER_FILTER)}'
env['_NVCC_PASSED_LINKFLAGS'] = \
'${_concat("-Xlinker ", LINKFLAGS, "", __env__, _NVCC_LINK_PASSED_FILTER)}'
# Constructing proper shared linker flags
env['_NVCC_BARE_SHLINKFLAGS'] = \
'${_concat("", SHLINKFLAGS, "", __env__, _NVCC_BARE_FILTER)}'
env['_NVCC_COMPILER_SHLINKFLAGS'] = \
'${_concat("-Xcompiler ", LINKFLAGS, "", __env__, _NVCC_COMPILER_FILTER)}'
env['_NVCC_PASSED_SHLINKFLAGS'] = \
'${_concat("-Xlinker ", SHLINKFLAGS, "", __env__, _NVCC_LINK_PASSED_FILTER)}'
# Putting all together
env['NVCC_LINKFLAGS'] = \
'$_NVCC_BARE_LINKFLAGS $_NVCC_COMPILER_LINKFLAGS $_NVCC_PASSED_LINKFLAGS'
env['NVCC_SHLINKFLAGS'] = env['NVCC_LINKFLAGS']
env['NVCC_SHLINKFLAGS'] += \
' $_NVCC_BARE_SHLINKFLAGS $_NVCC_COMPILER_SHLINKFLAGS $_NVCC_PASSED_SHLINKFLAGS'
def _define_commands(env):
"""Defining the command strings"""
# Flags allowed by nvcc
bare_flags = """-std=c++11 -O0 -O1 -O2 -O3 -g -pg -G -w""".split()
# Experimental flags
bare_flags += """-expt-extended-lambda -expt-relaxed-constexpr""".split()
# Flags that must be passed to compiler at link time
compiler_flags = """-fopenmp -qopenmp""".split()
# Pass flags bare to nvcc
env['_NVCC_BARE_FILTER'] = lambda flags: list(filter(lambda flag: flag in bare_flags, flags))
# Must prepend -Xcompiler, even at link time
env['_NVCC_COMPILER_FILTER'] = lambda flags: list(filter(
lambda flag: flag in compiler_flags, flags))
# Prepend -Xcompiler
env['_NVCC_COMPILER_PASSED_FILTER'] = lambda flags: list(filter(
lambda flag: flag not in set(bare_flags), flags))
# Prepend -Xlinker
env['_NVCC_LINKED_PASSED_FILTER'] = lambda flags: list(filter(
lambda flag: flag not in set(bare_flags) | set(compiler_flags), flags))
_define_compile_cuda(env)
_define_link_cuda(env)
def _add_actions_cuda(env):
"""Adding actions to .cu files to compile with nvcc. Other files are not affected."""
nvcc_action = SCons.Action.Action('$NVCCCOM', '$NVCCCOMSTR')
shnvcc_action = SCons.Action.Action('$SHNVCCCOM', '$NVCCCOMSTR')
static, shared = SCons.Tool.createObjBuilders(env)
# Compiling with nvcc action added to detected .cu files
static.add_action('.cu', nvcc_action)
shared.add_action('.cu', shnvcc_action)
# Emitter to qualify correctly object code type
static.add_emitter('.cu', SCons.Defaults.StaticObjectEmitter)
shared.add_emitter('.cu', SCons.Defaults.SharedObjectEmitter)
env['CXXCOM'] = '$NVCCCOM'
env['SHCXXCOM'] = '$SHNVCCCOM'
# Scanner for dependency calculations
SCons.Tool.SourceFileScanner.add_scanner('.cu', SCons.Scanner.C.CScanner())
def generate(env):
"""Setup environment for compiling .cu files with nvcc"""
_find_cuda_paths(env)
_add_actions_cuda(env)
_define_commands(env)
_configure_cuda_components(env)
def exists(env):
return env.Detect('nvcc')
diff --git a/site_scons/site_tools/tamaas.py b/site_scons/site_tools/tamaas.py
index 3cf3930..6c6065e 100644
--- a/site_scons/site_tools/tamaas.py
+++ b/site_scons/site_tools/tamaas.py
@@ -1,59 +1,61 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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 .
import os
from SCons.Errors import StopError
from SCons.Script import Configure
def generate(env):
"""Sets correct environement variables for Tamaas.
- 'TAMAAS_PATH' is the path to the build directory of Tamaas"""
if env.GetOption("clean"):
return
if 'TAMAAS_PATH' not in env:
raise StopError("Please set the 'TAMAAS_PATH' variable in your " +
"scons environment")
tm_path = env['TAMAAS_PATH']
include_dir = tm_path + '/src'
subdirs = "core model solvers bem surface percolation".split(" ")
include_dirs = [os.path.abspath(os.path.join(include_dir, x)) for x in subdirs]
lib_dir = include_dir
env.AppendUnique(CPPPATH=include_dirs)
env.AppendUnique(LIBPATH=[os.path.abspath(lib_dir)])
env.AppendUnique(RPATH=[os.path.abspath(lib_dir)])
env.AppendUnique(CXXFLAGS=["-std=c++14"])
env.AppendUnique(CPPDEFINES=["THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_OMP"])
env.AppendUnique(LIBS=['fftw3', 'fftw3_omp'])
conf = Configure(env)
if not conf.CheckLibWithHeader("Tamaas", "tamaas.hh", language="CXX"):
raise StopError(
"Tamaas not found in {}".format(os.path.abspath(tm_path)))
env['TAMAAS_PATH'] = os.path.abspath(tm_path)
env = conf.Finish()
def exists(env):
if 'TAMAAS_PATH' in env:
return True
return False
diff --git a/site_scons/version.py b/site_scons/version.py
index d96e110..a5704db 100644
--- a/site_scons/version.py
+++ b/site_scons/version.py
@@ -1,59 +1,61 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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 .
import subprocess
import base64
from functools import reduce
def git(*args):
return subprocess.check_output(["git"] + list(args))
def write_info_file(file_name, build_type="release"):
header = '#include "tamaas_info.hh"\n\n'
file_content = ''
branch = git("rev-parse", "--abbrev-ref", "HEAD")[:-1]
commit = git("rev-parse", branch)[:-1]
file_content += \
'std::string tamaas::TamaasInfo::build_type = "{}";\n'.format(build_type)
file_content += \
'std::string tamaas::TamaasInfo::branch = "{}";\n'.format(branch)
file_content += \
'std::string tamaas::TamaasInfo::commit = "{}";\n'.format(commit)
diff = git("diff")
diff += "|".encode('ascii') + git("diff", "--cached")
file_content += \
'std::string tamaas::TamaasInfo::diff = "{}";\n'.format(
base64.b64encode(diff))
remotes = git('remote', '-v')
remotes_strings = filter(lambda x: x != '', remotes.splitlines())
remotes_string = reduce(lambda x, y: x + '"{}\\n"\n'.format(y),
remotes_strings, "")
file_content += \
'std::string tamaas::TamaasInfo::remotes = {};\n'.format(remotes_string)
with open(file_name, 'w') as file:
file.write(header + file_content)
diff --git a/src/SConscript b/src/SConscript
index 0f0e96e..29be809 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -1,154 +1,156 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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 .
import os
Import('main_env')
def prepend(path, list):
return [os.path.join(path, x) for x in list]
env = main_env.Clone()
# Core
core_list = """
fft_plan_manager.cpp
fftransform.cpp
fftransform_fftw.cpp
grid.cpp
grid_hermitian.cpp
statistics.cpp
surface.cpp
tamaas.cpp
legacy_types.cpp
loop.cpp
""".split()
core_list = prepend('core', core_list)
core_list.append('tamaas_info.cpp')
# Lib roughcontact
generator_list = """
surface_generator.cpp
surface_generator_filter.cpp
surface_generator_filter_fft.cpp
surface_generator_random_phase.cpp
isopowerlaw.cpp
regularized_powerlaw.cpp
""".split()
generator_list = prepend('surface', generator_list)
# Lib PERCOLATION
percolation_list = """
flood_fill.cpp
""".split()
percolation_list = prepend('percolation', percolation_list)
# BEM PERCOLATION
bem_list = """
bem_kato.cpp
bem_polonski.cpp
bem_gigi.cpp
bem_gigipol.cpp
bem_penalty.cpp
bem_uzawa.cpp
bem_fft_base.cpp
bem_functional.cpp
bem_meta_functional.cpp
elastic_energy_functional.cpp
exponential_adhesion_functional.cpp
squared_exponential_adhesion_functional.cpp
maugis_adhesion_functional.cpp
complimentary_term_functional.cpp
bem_grid.cpp
bem_grid_polonski.cpp
bem_grid_kato.cpp
bem_grid_teboulle.cpp
bem_grid_condat.cpp
""".split()
bem_list = prepend('bem', bem_list)
# Model
model_list = """
model.cpp
model_factory.cpp
model_type.cpp
model_template.cpp
be_engine.cpp
westergaard.cpp
elastic_functional.cpp
meta_functional.cpp
adhesion_functional.cpp
volume_potential.cpp
kelvin.cpp
mindlin.cpp
boussinesq.cpp
elasto_plastic/isotropic_hardening.cpp
elasto_plastic/elasto_plastic_model.cpp
elasto_plastic/residual.cpp
integration/element.cpp
""".split()
model_list = prepend('model', model_list)
# Solvers
solvers_list = """
contact_solver.cpp
polonsky_keer_rey.cpp
kato.cpp
beck_teboulle.cpp
condat.cpp
polonsky_keer_tan.cpp
ep_solver.cpp
epic.cpp
""".split()
solvers_list = prepend('solvers', solvers_list)
# GPU API
gpu_list = """
fftransform_cufft.cpp
""".split()
gpu_list = prepend('gpu', gpu_list)
# Assembling total list
rough_contact_list = \
core_list + generator_list + percolation_list + model_list + solvers_list
# For some reason collapse OMP loops don't compile on intel
if env['CXX'] != 'icpc':
rough_contact_list += bem_list
# Adding GPU if needed
if env['backend'] == 'cuda':
rough_contact_list += gpu_list
# Generating dependency files
# env.AppendUnique(CXXFLAGS=['-MMD'])
# for file_name in rough_contact_list:
# obj = file_name.replace('.cpp', '.os')
# dep_file_name = file_name.replace('.cpp', '.d')
# env.SideEffect(dep_file_name, obj)
# env.ParseDepends(dep_file_name)
# Adding extra warnings for Tamaas base lib
env.AppendUnique(CXXFLAGS=['-Wextra'])
libTamaas = env.SharedLibrary('Tamaas', rough_contact_list)
Export('libTamaas')
diff --git a/tests/SConscript b/tests/SConscript
index f306aae..70fcc0f 100644
--- a/tests/SConscript
+++ b/tests/SConscript
@@ -1,159 +1,161 @@
+# -*- coding: utf-8 -*-
+
# @file
# @section LICENSE
#
# Copyright (©) 2016-19 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, abspath
from SCons.Script import Split, Copy, Dir, Import
from detect import FindGTest
# ------------------------------------------------------------------------------
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(
PRINT_CMD_LINE_FUNC=main_env['gen_print']("Copying", "red", main_env))
test_files = Split("""
run_tests.sh
test_hertz.py
test_westergaard.py
test_patch_westergaard.py
test_surface.py
test_autocorrelation.py
test_hertz_disp.py
test_hertz_kato.py
test_saturated_pressure.py
test_bem_grid.py
test_flood_fill.py
test_integral_operators.py
test_dumper.py
test_gtest.py
test_tangential.py
test_boussinesq_surface.py
test_voigt.py
fftfreq.py
conftest.py
""")
src_dir = "#/tests"
for file in test_files:
source = join(src_dir, file)
test_env.Command(file, source, Copy("$TARGET", "$SOURCE"))
test_env = env.Clone()
# Helper module for integral operators
test_env['SHLIBPREFIX'] = ''
register = test_env.SharedLibrary(target="register_integral_operators",
source=["register_integral_operators.cpp"],
LIBS=['Tamaas'],
RPATH=[abspath('../src')])
Import('libTamaas')
test_env.Depends(register, libTamaas)
# ------------------------------------------------------------------------------
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
""")
gtest_main = env.Object("tamaas_gtest_main.o", 'tamaas_gtest_main.cc')
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)
# ------------------------------------------------------------------------------
def make_bare_tests(env):
rough = env.Program("test_rough_surface.cpp")
Import('libTamaas')
env.Depends(rough, libTamaas)
# ------------------------------------------------------------------------------
Import('main_env')
# Setup of test environment
test_env = main_env.Clone()
test_env.AppendUnique(LIBS=['Tamaas'], LIBPATH=['.'])
copyComStr(test_env, main_env)
# Building tests that do not require any third party
make_bare_tests(test_env)
if test_env['build_python']:
test_env.Tool(pybind11)
test_env.ParseConfig("{}-config --ldflags".format(test_env['py_exec']))
test_env['CCFLAGS'] = []
make_python_tests(test_env)
# Building google tests
if test_env['use_googletest']:
make_google_tests(test_env)