diff --git a/SConstruct b/SConstruct
index a013aa27..52bd110f 100644
--- a/SConstruct
+++ b/SConstruct
@@ -1,430 +1,446 @@
 # -*- 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 <https://www.gnu.org/licenses/>.
 
 # ------------------------------------------------------------------------------
 # Imports
 # ------------------------------------------------------------------------------
 from __future__ import print_function
 
 import os
 
 import SCons
 
 from os.path import join, abspath
 
 # Import below not strictly necessary, but good for pep8
 from SCons.Script import (
     EnsurePythonVersion,
     EnsureSConsVersion,
     Help,
     Environment,
     Variables,
     EnumVariable,
     PathVariable,
     BoolVariable,
     Split,
     SConscript,
     Export,
     Dir
 )
 
 from version import get_git_subst
 from detect import (
     FindFFTW,
     FindBoost,
     FindThrust,
     FindCuda,
     FindExpolit,
     FindPybind11
 )
 
 # ------------------------------------------------------------------------------
 
 EnsurePythonVersion(2, 7)
 EnsureSConsVersion(2, 4)
 
 # ------------------------------------------------------------------------------
 
 tamaas_info = dict(
     version="2.1.1",
     authors=[
         u'Lucas Frérot',
         'Guillaume Anciaux',
         'Valentine Rey',
         'Son Pham-Ba',
         u'Jean-François Molinari'
     ],
     maintainer=u'Lucas Frérot',
     email='lucas.frerot@epfl.ch',
     copyright=u"Copyright (©) 2016-2020 EPFL "
     + u"(École Polytechnique Fédérale de Lausanne), "
     + u"Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)"
 )
 
 
 # ------------------------------------------------------------------------------
 def detect_dependencies(env):
     """Detect all dependencies"""
     FindFFTW(env, ['omp'], precision=env['real_type'])
     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)
 
     if env['build_python']:
         FindPybind11(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,
 )
 
 # Set tamaas information
 for k, v in tamaas_info.items():
     main_env[k] = v
 
 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', 'tbb'),
                  # 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'),
 
     # Compiler flags
     ('CXXFLAGS', 'C++ compiler flags', os.getenv('CXXFLAGS', "")),
 
     # Compile legacy code
     BoolVariable('legacy_bem', 'Compile legacy BEM code', False),
     # 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),
 
     # Strip binary of extra info
     BoolVariable('strip_info', 'Strip binary of added information', False),
 
     # Type variables
     EnumVariable('real_type', 'Type for real precision variables', 'double',
                  allowed_values=('double', 'long double')),
     EnumVariable('integer_type', 'Type for integer variables', 'int',
                  allowed_values=('int', 'long')),
 )
 
 # Set variables of environment
 vars.Update(main_env)
 help_text = vars.GenerateHelpText(main_env)
 help_text += """
 Commands:
     scons [build] [options]...                            Compile Tamaas (and additional modules/tests)
     scons install [prefix=/your/prefix] [options]...      Install Tamaas to prefix
     scons dev                                             Install symlink to Tamaas python module (useful to development purposes)
     scons test                                            Run tests with pytest
     scons doc                                             Compile documentation with Doxygen and Sphinx+Breathe
+    scons archive                                         Create a gzipped archive from source
 """  # noqa
 Help(help_text)
 
 # Save all options, not just those that differ from default
 with open('build-setup.conf', 'w') as setup:
     for option in vars.options:
         setup.write("# " + option.help + "\n")
         setup.write("{} = '{}'\n".format(option.key, main_env[option.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']
 main_env['build_dir'] = build_dir
 
 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['CXXCOMSTR'] = main_env['SHCXXCOMSTR'] = \
         u'{0}[Compiling ($SHCXX)] {1}$SOURCE'.format(colors['green'],
                                                      colors['end'])
     main_env['LINKCOMSTR'] = main_env['SHLINKCOMSTR'] = \
         u'{0}[Linking] {1}$TARGET'.format(colors['purple'],
                                           colors['end'])
     main_env['PRINT_CMD_LINE_FUNC'] = pretty_cmd_print
 
 # 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" and "g++" not in main_env['CXX']:
     raise SCons.Errors.StopError('GCC should be used when compiling with CUDA')
 
 # OpenMP flags - compiler dependent
 omp_flags = {
     "g++": ["-fopenmp"],
     "clang++": ["-fopenmp"],
     "icpc": ["-qopenmp"]
 }
 
 
 def cxx_alias(cxx):
     for k in omp_flags.keys():
         if k in cxx:
             return k
 
     raise SCons.Errors.StopError('Unsupported compiler: ' + cxx)
 
 
 cxx = cxx_alias(main_env['CXX'])
 main_env['CXXFLAGS'] = Split(main_env['CXXFLAGS'])
 main_env.AppendUnique(CXXFLAGS='-std=c++14 -Wall -Wextra -pedantic'.split())
 
 # Append openmp flags regardless of backend for fftw_omp
 main_env.AppendUnique(CXXFLAGS=omp_flags[cxx])
 main_env.AppendUnique(LINKFLAGS=omp_flags[cxx])
 
 # Correct bug in clang?
 if main_env['backend'] == 'omp' and cxx == "clang++":
     main_env.AppendUnique(LIBS=["atomic"])
 elif main_env['backend'] == 'tbb':
     main_env.AppendUnique(LIBS=['tbb'])
 
 # Force deactivate legacy when using intel
 # Intel does not like old openmp loops
 if cxx == 'icpc' and main_env['legacy_bem']:
     print("Using intel compiler => deactivating legacy code")
     main_env['legacy_bem'] = False
 
 # Flags and options
 if main_env['build_python']:
     main_env.AppendUnique(CPPDEFINES=['USE_PYTHON'])
 if main_env['legacy_bem']:
     main_env.AppendUnique(CPPDEFINES=['LEGACY_BEM'])
 if main_env['build_type'] == 'debug':
     main_env.AppendUnique(CPPDEFINES=['TAMAAS_DEBUG'])
 
 # Define the scalar types
 main_env.AppendUnique(CPPDEFINES={'REAL_TYPE': '${real_type}',
                                   'INT_TYPE': '${integer_type}'})
 
 if main_env['real_type'] == 'long double':
     main_env.AppendUnique(CPPDEFINES=['LONG_PRECISION'])
 
 # 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": [],
     "profiling": Split("-g -O3 -fno-omit-frame-pointer"),
     "release": []
 }
 
 if main_env['sanitizer'] != 'none':
     if main_env['backend'] == 'cuda':
         raise SCons.Errors.StopError(
             "Sanitizers with cuda are not yet supported!")
     cxxflags_dict[build_type].append('-fsanitize=${sanitizer}')
     shlinkflags_dict[build_type].append('-fsanitize=${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])
 
 # Adding
 main_env['LIBPATH'] = [abspath(join(build_dir, 'src')),
                        abspath(join(main_env['prefix'], 'lib'))]
 main_env['RPATH'] = "$LIBPATH"
 
 if main_env['should_configure']:
     detect_dependencies(main_env)
 
 # Writing information file
 main_env.Tool('textfile')
 main_env['SUBST_DICT'] = get_git_subst()
-main_env['SUBST_DICT'].update({
-    '@build_type@': '$build_type',
-    '@build_dir@': abspath(build_dir),
-})
 
+# Empty values if requested
 if main_env['strip_info']:
     for k in main_env['SUBST_DICT']:
         main_env['SUBST_DICT'][k] = ""
 
+# Substitution of environment file
+main_env['SUBST_DICT'].update({
+    '@build_type@': '$build_type',
+    '@build_dir@': abspath(build_dir),
+})
+
 # Environment file content
 env_content = """export PYTHONPATH=@build_dir@/python:$$PYTHONPATH
 export LD_LIBRARY_PATH=@build_dir@/src:$$LD_LIBRARY_PATH
 """
 
 # Writing environment file
 env_file = main_env.Textfile(join(build_dir, 'tamaas_environment.sh'),
                              env_content)
 
 
 # Building sub-directories
 def subdir(dir):
     return SConscript(join(dir, 'SConscript'),
                       variant_dir=join(build_dir, dir),
                       duplicate=True)
 
 
 # Building Tamaas library
 Export('main_env')
 subdir('src')
 
 build_targets = ['build-cpp', env_file]
 install_targets = ['install-lib']
 
 # Building Tamaas extra components
 for dir in ['python', 'tests']:
     if main_env['build_{}'.format(dir)] and not main_env.GetOption('help'):
         subdir(dir)
         build_targets.append('build-{}'.format(dir))
 
 # Building API + Sphinx documentation if requested
 if main_env['build_doc']:
     doc = main_env.Command('#.phony_doc', '',
                            'make -C {}'.format(Dir('#/doc')))
     main_env.Alias('doc', doc)
 else:
     dummy_command(main_env, 'doc', 'Command "doc" does not do anything'
                   ' without documentation activated ("build_doc=True")')
 
 # Define dummy dev command when python is deactivated
 if not main_env['build_python']:
     dummy_command(main_env, 'dev', 'Command "dev" does not do anything'
                   + ' without python activated ("build_python=True")')
 else:
     install_targets.append('install-python')
 
 # Define dummy test command when tests are deactivated
 if not main_env['build_tests']:
     dummy_command(main_env, 'test', 'Command "test" does not do anything'
                   + ' without tests activated ("build_tests=True")')
 
 # Definition of target aliases, a.k.a. sub-commands
 main_env.Alias('build', build_targets)
 
 # Define proper install targets
 main_env.Alias('install', install_targets)
 
 # Default target is to build stuff
 main_env.Default('build')
+
+# Building a tar archive
+archive = main_env.Command(
+    '../tamaas-{}.tar.gz'.format(main_env['version']),
+    '',
+    ('tar --exclude-vcs --exclude-vcs-ignores '
+     '--exclude=third-party/googletest '
+     '--exclude=third-party/pybind11 '
+     '--exclude=joss '
+     '-czf $TARGET .'),
+)
+main_env.Alias('archive', archive)