diff --git a/.clang-tidy b/.clang-tidy index 76c907d..15ca48f 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,3 @@ -Checks: 'modernize-use-*, performance-*, cppcoreguidelines-*' +Checks: '-*, modernize-use-*, -modernize-use-trailing-return-type*, performance-*, readability-*, -readability-braces-around-statements, -readability-implicit-bool-conversion, clang-analyzer-*, -clang-analyzer-core.NonNullParamChecker' AnalyzeTemporaryDtors: false -HeaderFilterRegex: 'src/.*' FormatStyle: file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e759779..eee41b3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,171 +1,251 @@ stages: - docker + - lint - build - test - deploy variables: IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG GIT_SUBMODULE_STRATEGY: recursive BUILD_DIR: build-release cache: key: "$CI_COMMIT_REF_SLUG" # ------------------------------------------------------------------------------ .docker_build: stage: docker image: docker:19.03.12 services: - docker:19.03.12-dind variables: # Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: "/certs" DEFAULT_IMAGE: $CI_REGISTRY_IMAGE:$CI_DEFAULT_BRANCH before_script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY script: - docker pull $DEFAULT_IMAGE-$IMAGE_NAME || true - docker build --cache-from $DEFAULT_IMAGE-$IMAGE_NAME -t $IMAGE_TAG-$IMAGE_NAME -f $DOCKERFILE . - docker push $IMAGE_TAG-$IMAGE_NAME docker build:debian: variables: IMAGE_NAME: debian-stable DOCKERFILE: tests/ci/docker/debian.mpi extends: .docker_build docker build:manylinux: variables: IMAGE_NAME: manylinux DOCKERFILE: tests/ci/docker/manylinux extends: .docker_build docker build:cuda: variables: IMAGE_NAME: cuda DOCKERFILE: tests/ci/docker/ubuntu_lts.cuda extends: .docker_build # ------------------------------------------------------------------------------ .debian_stable: - image: $IMAGE_TAG-debian-stable + variables: + output: ${CI_COMMIT_REF_SLUG}-debian-stable + image: ${IMAGE_TAG}-debian-stable .manylinux: - image: $IMAGE_TAG-manylinux + variables: + output: ${CI_COMMIT_REF_SLUG}-manylinux + image: ${IMAGE_TAG}-manylinux .cuda: - image: $IMAGE_TAG-cuda + variables: + output: ${CI_COMMIT_REF_SLUG}-cuda + image: ${IMAGE_TAG}-cuda # ------------------------------------------------------------------------------ +lint: + stage: lint + extends: + - .debian_stable + allow_failure: true + variables: + CLANG_PATCH: clang_format.patch + FLAKE_ERRORS: flake8_lint.txt + script: + - git remote remove upstream || true + - git remote add upstream "$CI_PROJECT_URL" + - git fetch upstream + - '[ -z "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" ] && HEAD="$CI_COMMIT_BEFORE_SHA" || HEAD="upstream/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"' + - echo "$HEAD" + - git-clang-format --diff "$HEAD" | tee $CLANG_PATCH + - grep "no modified files to format" $CLANG_PATCH || grep "clang-format did not modify any files" $CLANG_PATCH + - flake8 python/ | tee $FLAKE_ERRORS + - '[ ! -s $FLAKE_ERRORS ]' + artifacts: + when: on_failure + paths: + - $CLANG_PATCH + - $FLAKE_ERRORS + + .build: stage: build variables: COMPILE_LOG: compilation.log script: - scons 2>&1 | tee $COMPILE_LOG artifacts: - paths: + when: always + paths: &build_artifacts - build-setup.conf - $COMPILE_LOG - $BUILD_DIR - config.log + - "*.json" + +.ccache: + variables: + CCACHE_BASEDIR: $CI_PROJECT_DIR/$BUILD_DIR + CCACHE_DIR: $CI_PROJECT_DIR/.ccache + CCACHE_NOHASDIR: 1 + CCACHE_COMPILERCHECK: content + CXX: /usr/lib/ccache/g++ + OMPI_CXX: ${CXX} + cache: + key: ${output} + policy: pull-push + paths: + - .ccache + after_script: + - ccache --show-stats || true build:mpi: extends: - .build - .debian_stable + - .ccache before_script: - - scons build_tests=True - use_googletest=True - build_python=True - py_exec=python3 - use_mpi=True - backend=omp - fftw_threads=omp - verbose=True -h + - ccache --zero-stats || true + - bear scons build_tests=True + use_googletest=True + build_python=True + py_exec=python3 + use_mpi=True + backend=omp + fftw_threads=omp + verbose=True -h build:cuda: extends: - .build - .cuda before_script: - scons build_tests=True use_googletest=True build_python=True py_exec=python3 use_mpi=False backend=cuda fftw_threads=none verbose=True -h # ------------------------------------------------------------------------------ +clang-tidy: + stage: test + dependencies: + - build:mpi + allow_failure: true + extends: .debian_stable + variables: + CLANG_TIDY_FILE: clang_tidy.txt + script: + - run-clang-tidy -header-filter="build-release/.*" -quiet src tests python > $CLANG_TIDY_FILE || true + - '! grep "warning:" $CLANG_TIDY_FILE' + artifacts: + when: on_failure + paths: + - $CLANG_TIDY_FILE + test:mpi: stage: test dependencies: - build:mpi extends: .debian_stable variables: PYTHONPATH: $CI_PROJECT_DIR/$BUILD_DIR/python TESTS: $BUILD_DIR/tests JUNITXML: results.xml TESTS_LOG: tests.log script: - ls $PYTHONPATH - - python3 -c 'import sys; print(sys.path)' - - python3 -m pytest -vvv --durations=0 --junitxml=$JUNITXML + - python3 -c "import sys; print(sys.path)" + - python3 -m pytest -vvv --last-failed --durations=0 --junitxml=$JUNITXML $TESTS 2>&1 | tee $TESTS_LOG + after_script: + - python3 -m pytest --cache-show || true artifacts: + when: always paths: - $JUNITXML - $TESTS_LOG reports: junit: - $JUNITXML + cache: + key: ${output}-pytest + policy: pull-push + paths: + - .pytest_cache # ------------------------------------------------------------------------------ .protected_refs: extends: .manylinux rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_TAG =~ /^v.*/' wheels: stage: build - extends: .protected_refs + extends: + - .protected_refs + - .ccache + before_script: + - ccache --zero-stats || true script: - ./tests/ci/build_wheels.sh artifacts: paths: - dist/wheelhouse # ------------------------------------------------------------------------------ .deploy_wheels: stage: deploy extends: .protected_refs dependencies: - wheels script: python -m twine upload --verbose dist/wheelhouse/* package:gitlab: extends: .deploy_wheels variables: TWINE_USERNAME: gitlab-ci-token TWINE_PASSWORD: ${CI_JOB_TOKEN} TWINE_REPOSITORY_URL: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi package:pypi: extends: .deploy_wheels variables: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${PYPI_TOKEN} rules: - if: '$CI_COMMIT_TAG =~ /^v.*/' diff --git a/CHANGELOG.md b/CHANGELOG.md index ec6461b..909780e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,221 +1,237 @@ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) for final versions and [PEP440](https://www.python.org/dev/peps/pep-0440/) in case intermediate versions need to be released (e.g. development version `2.2.3.dev1` or release candidates `2.2.3rc1`), or individual commits are packaged. ## Unreleased +### Added + +- Now using `clang-format`, `clang-tidy` and `flake8` for linting + +### Changed + +- The root `Dockerfile` now compiles Tamaas, so using Tamaas in Docker is easier. +- The model attributes dumped to Numpy files are now written in a JSON-formatted + string to avoid unsafe loading/unpickling of objects. +- Removed the `build_doc` build option: now the doc targets are automatically + added if the dependencies are met, and built if `scons doc` is called. +- Removed the `use_googletest` build option: if tests are built and gtest is + present, the corresponding tests will be built + ## v2.3.0 -- 2021-06-15 ### Added - Added `read()` method to dumpers to create a model from a dump file - `getClusters()` can be called in MPI contact with partial contact maps - Added a JSON encoder class for models and a JSON dumper - CUDA compatibility is re-established, but has not been tested - Docstrings in the Python bindings for many classes/methods ### Changed - Tamaas version numbers are now managed by [versioneer](https://github.com/python-versioneer/python-versioneer). This means that Git tags prefixed with `v` (e.g. `v2.2.3`) carry meaning and determine the version. When no tag is set, versioneer uses the last tag, specifies the commit short hash and the distance to the last tag (e.g. `2.2.2+33.ge314b0e`). This version string is used in the compiled library, the `setup.py` script and the `__version__` variable in the python module. - Tamaas migrated to [GitLab](https://gitlab.com/tamaas/tamaas) - Continuous delivery has been implemented: - the `master` branch will now automatically build and publish Python wheels - to `https://gitlab.com/api/v4/projects/19913787/packages/pypi`. These + to `https://gitlab.com/api/v4/projects/19913787/packages/pypi/simple`. These "nightly" builds can be installed with: - pip install -i https://gitlab.com/api/v4/projects/19913787/packages/pypi tamaas + pip install \ + --extra-index-url https://gitlab.com/api/v4/projects/19913787/packages/pypi/simple \ + tamaas - version tags pushed to `master` will automatically publish the wheels to [PyPI](https://pypi.org/project/tamaas/) ### Deprecated - The `finalize()` function is now deprecated, since it is automatically called when the process terminates - Python versions 3.5 and below are not supported anymore ### Fixed - Fixed a host of dump read/write issues when model type was not `volume_*d`. Dumper tests are now streamlined and systematic. - Fixed a bug where `Model::solveDirichlet` would not compute correctly - Fixed a bug where `Statistics::contact` would not normalize by the global number of surface points ## v2.2.2 -- 2021-04-02 ### Added - Entry-point `tamaas` defines a grouped CLI for `examples/pipe_tools`. Try executing `tamaas surface -h` from the command-line! ### Changed - `CXXFLAGS` are now passed to the linker - Added this changelog - Using absolute paths for environmental variables when running `scons test` - Reorganized documentation layout - Gave the build system a facelift (docs are now generated directly with SCons instead of a Makefile) ### Deprecated - Python 2 support is discontinued. Version `v2.2.1` is the last PyPi build with a Python 2 wheel. - The scripts in `examples/pipe_tools` have been replaced by the `tamaas` command ### Fixed - `UVWDumper` no longer imports `mpi4py` in sequential - Compiling with different Thrust/FFTW backends ## v2.2.1 -- 2021-03-02 ### Added - Output registered fields and dumpers in `print(model)` - Added `operator[]` to the C++ model class (for fields) - Added `traction` and `displacement` properties to Python model bindings - Added `operators` property to Python model bindings, which provides a dict-like access to registered operators - Added `shape` and `spectrum` to properties to Python surface generator bindings - Surface generator constructor accepts surface global shape as argument - Choice of FFTW thread model ### Changed - Tests use `/tmp` for temporary files - Updated dependency versions (Thrust, Pybind11) ### Deprecated - Most `get___()` and `set___()` in Python bindings have been deprecated. They will generate a `DeprecationWarning`. ### Removed - All legacy code ## v2.2.0 -- 2020-12-31 ### Added - More accurate function for computation of contact area - Function to compute deviatoric of tensor fields - MPI implementation - Convenience `hdf5toVTK` function - Readonly properties `shape`, `global_shape`, `boundary_shape` on model to give shape information ### Changed - Preprocessor defined macros are prefixed with `TAMAAS_` - Moved `tamaas.to_voigt` to `tamaas.compute.to_voigt` ### Fixed - Warning about deprecated constructors with recent GCC versions - Wrong computation of grid strides - Wrong computation of grid sizes in views ## v2.1.4 -- 2020-08-07 ### Added - Possibility to generate a static `libTamaas` - C++ implementation of DFSANE solver - Allowing compilation without OpenMP ### Changed - NetCDF dumper writes frames to a single file ### Fixed - Compatibility with SCons+Python 3 ## v2.1.3 -- 2020-07-27 ### Added - Version number to `TamaasInfo` ### Changed - Prepending root directory when generating archive ## v2.1.2 -- 2020-07-24 This release changes some core internals related to discrete Fourier transforms for future MPI support. ### Added - Caching `CXXFLAGS` in SCons build - SCons shortcut to create code archive - Test of the elastic-plastic contact solver - Paraview data dumper (`.pvd` files) - Compression for UVW dumper - `__contains__` and `__iter__` Python bindings of model - Warning message of possible overflow in Kelvin ### Changed - Simplified `tamaas_info.cpp`, particularly the diff part - Using a new class `FFTEngine` to manage discrete Fourier transforms. Plans are re-used as much as possible with different data with the same shape. This is in view of future MPI developments - Redirecting I/O streams in solve functions so they can be used from Python (e.g. in Jupyter notebooks) - Calling `initialize()` and `finalize()` is no longer necessary ### Fixed - Convergence issue with non-linear solvers - Memory error in volume potentials ## v2.1.1 -- 2020-04-22 ### Added - SCons shortcut to run tests ### Fixed - Correct `RPATH` for shared libraries - Issues with SCons commands introduced in v2.1.0 - Tests with Python 2.7 ## v2.1.0 -- 2020-04-17 ### Added - SCons shortcuts to build/install Tamaas and its components - Selection of integration method for Kelvin operator - Compilation option to remove the legacy part of Tamaas - NetCDF dumper ### Fixed - Link bug with clang - NaNs in Kato saturated solver ## v2.0.0 -- 2019-11-11 First public release. Contains relatively mature elastic-plastic contact code. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1d0570c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,50 @@ +FROM debian:stable-slim + +MAINTAINER Lucas Frérot + +# Add contrib and non-free +RUN sed -i 's/main/main contrib non-free/' /etc/apt/sources.list + +# Install dependencies +RUN apt-get -qq update && apt-get install -qq -y \ + g++ \ + git \ + libboost-dev \ + libthrust-dev \ + libfftw3-dev \ + libfftw3-mpi-dev \ + python3-dev \ + python3-numpy \ + python3-pip \ + python3-scipy \ + python3-h5py \ + python3-netcdf4 \ + python3-mpi4py \ + python3-matplotlib \ + doxygen \ + scons \ + && rm -rf /var/lib/apt/lists/* + +RUN python3 -m pip install uvw + +# Basic setup +RUN mkdir -p /src/tamaas +COPY . /src/tamaas +RUN rm -f /src/tamaas/build-setup.conf + +# Build arguments +ARG BACKEND=cpp +ARG FFTW_THREADS=none +ARG USE_MPI=False + +# Compiling and installing +RUN cd /src/tamaas \ + && scons \ + backend=$BACKEND \ + fftw_threads=$FFTW_THREADS \ + use_mpi=$USE_MPI \ + verbose=true \ + py_exec=python3 \ + prefix=/usr/local \ + && scons install \ + && cd / && rm -rf /src/tamaas \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index 369ed3d..75a8de1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,131 +1,130 @@ pipeline { parameters {string(defaultValue: '', description: 'api-token', name: 'API_TOKEN') string(defaultValue: '', description: 'Token for readthedocs', name: 'RTD_TOKEN') string(defaultValue: '', description: 'buildable phid', name: 'BUILD_TARGET_PHID') string(defaultValue: '', description: 'Commit id', name: 'COMMIT_ID') string(defaultValue: '', description: 'Diff id', name: 'DIFF_ID') string(defaultValue: 'PHID-PROJ-gbo56hpf2y5bi7t5jusk', description: 'ID of the project', name: 'PROJECT_ID') } environment { PHABRICATOR_HOST = 'https://c4science.ch/api/' PYTHONPATH = sh returnStdout: true, script: 'echo ${WORKSPACE}/build-release/python:${WORKSPACE}/tests/ci/script/' } agent {dockerfile {additionalBuildArgs '--tag tamaas-environment'}} options {timeout(time: 1, unit: 'HOURS')} stages { stage('SCM Checkout') { steps { checkout scm: [ $class: 'GitSCM', branches: scm.branches, extensions: [[ $class: 'SubmoduleOption', recursiveSubmodules: true, ]], userRemoteConfigs: scm.userRemoteConfigs ] } } stage('Configure') { steps { writeFile(file: build-setup.conf, text:'''py_exec = \'python3\' build_python = \'true\' build_tests = \'true\' - use_googletest = \'true\' use_mpi = \'true\' verbose = \'true\'"''') } } stage('Compile') { steps { tee('compilation.log') { sh '''set -o pipefail rm -rf .sconf_temp .sconsign.dblite build-release scons''' } } post { always { uploadArtifact('config.log', 'Configure log') uploadArtifact('compilation.log', 'Compilation log') } } } stage('Run tests') { steps { tee('tests.log') { sh '''set -o pipefail python3 -m pytest -vvv --durations=0 --junitxml=results.xml build-release/tests''' } } post { always { uploadArtifact('tests.log', 'Tests log') createArtifact("results.xml") junit 'results.xml' step([$class: 'XUnitBuilder', thresholds: [ [$class: 'SkippedThreshold', failureThreshold: '0'], [$class: 'FailedThreshold', failureThreshold: '0']], tools: [[$class: 'GoogleTestType', pattern: 'build-release/tests/gtest_results.xml']]]) } } } } post { success { trigger_rtd() passed_hbm() } failure { failed_hbm() emailext( body: '''${SCRIPT, template="groovy-html.template"}''', mimeType: 'text/html', subject: "[Jenkins] ${currentBuild.fullDisplayName} Failed", recipientProviders: [[$class: 'CulpritsRecipientProvider']], to: 'lfrerot1@jhu.edu', attachLog: true, compressLog: true) } } } def createArtifact(filename) { sh "./tests/ci/scripts/hbm send-uri -k 'Jenkins URI' -u ${BUILD_URL} -l 'View Jenkins result'" sh "./tests/ci/scripts/hbm send-junit-results -f ${filename}" } def uploadArtifact(artifact, name) { archiveArtifacts(artifacts: "${artifact}", fingerprint: true) sh "./tests/ci/scripts/hbm upload-file -f ${artifact} -n \"${name}\" -v ${PROJECT_ID}" } def failed_hbm() { sh "./tests/ci/scripts/hbm failed" } def passed_hbm() { sh "./tests/ci/scripts/hbm passed" } def trigger_rtd() { def response = httpRequest(url: "https://readthedocs.org/api/v2/webhook/tamaas/106141/", httpMode: 'POST', requestBody: "token=${RTD_TOKEN}") println("Status: " + response.status) println("Content: " + response.content) } diff --git a/README.md b/README.md index b82a234..b245bbe 100644 --- a/README.md +++ b/README.md @@ -1,216 +1,222 @@ Tamaas --- A high-performance library for periodic rough surface contact ======================================================================== Tamaas is a C++/Python library that implements a number of numerical methods based on integral equations to efficiently solve contact problems with rough surfaces. The word تماس (tamaas) means "contact" in Arabic and Farsi. ## Quick Start If you have a Linux system, you can simply run `pip(3) install tamaas`. Note however that there due to compatibility reasons, this version of Tamaas is not built with parallel/multi-threading capabilities. So if you want parallelism, or encounter an issue with the [PyPI package](https://pypi.org/project/tamaas/), please compile from source. You can also use one of our [interactive tutorial notebooks](https://tamaas.readthedocs.io/en/latest/overview.html#tutorials) to get started. ## Dependencies Here is a list of dependencies to compile Tamaas: - a C++ compiler with full C++14 and OpenMP support - [SCons](https://scons.org/) (python build system) - [FFTW3](http://www.fftw.org/) - [boost](https://www.boost.org/) (preprocessor) - [thrust](https://github.com/thrust/thrust) (1.9.2+) - [python 3+](https://www.python.org/) with [numpy](https://numpy.org/) - [pybind11](https://github.com/pybind/pybind11) (included as submodule) Optional dependencies are: - an MPI implmentation - [FFTW3](http://www.fftw.org/) with MPI/pthreads/OpenMP support - [scipy](https://scipy.org) (for nonlinear solvers) - [uvw](https://pypi.org/project/uvw/), [netCDF4](https://unidata.github.io/netcdf4-python/netCDF4/index.html#section1), [h5py](https://www.h5py.org/) (for various dumpers) - [googletest](https://github.com/google/googletest) and [pytest](https://docs.pytest.org/en/latest/) (for tests) - [Doxygen](http://doxygen.nl/) and [Sphinx](https://www.sphinx-doc.org/en/stable/) (for documentation) Note that a Debian distribution should have the right packages for all these dependencies (they package the right version of thrust extracted from CUDA in `stretch-backports non-free` and `buster non-free`). ## Compiling ### Dependencies To simplify compilation, pybind11 and googletest can be setup via git submodules. We still recommend installing them via your package manager. git submodule update --init --recursive ### Build System The build system uses SCons. In order to compile Tamaas with the default options (i.e. optimized release build with python bindings + OpenMP): scons After compiling a first time (or running `scons -h` if you want to avoid compiling), you can edit the compilation options in the file `build-setup.conf`, or alternatively supply the options directly in the command line: scons option=value [...] To get a list of *all* build options and their possible values, you can run `scons -h`. You can run `scons -H` to see the SCons-specific options (among them `-j n` executes the build with `n` threads and `-c` cleans the build). Note that the build is aware of the `CXX` and `CXXFLAGS` environment variables. ## Installing Before you can import tamaas in python, you need to install the python package in some way. ### Using pip You have two choices to install tamaas: - An out-of-repository installation to a given prefix (e.g. `/usr/local`, or a python virtual environment) - A development installation to `~/.local` which links to the build directory The former is simply achieved with: scons prefix=/your/prefix install # Equivalent to (if you build in release) install build-release/src/libTamaas.so* /your/prefix/lib pip3 install --prefix /your/prefix build-release/python The compiled parts of the python module should automatically know where to find the Tamaas shared library, so no need to tinker with `LD_LIBRARY_PATH`. The second installation choice is equally simple: scons dev # Equivalent to pip3 install --user -e build-release/python You can check that everything is working fine with: python3 -c 'import tamaas; print(tamaas)' ### Using environment variables (not recommended) You can source (e.g. in your `~/.bashrc` file) the file `build-release/tamaas_environment.sh` to modify the `PYTHONPATH` and `LD_LIBRARY_PATH` environment variables. This is however not recommended because these variables may conflict in a python virtual environment (i.e. if you use `virtualenv` with tamaas). +### Using Docker image + +Tamaas provides a `Dockerfile` you can use to compile and deploy Tamaas in a +tested environment. Simply run `docker build -t tamaas .` in the root folder of +the repository. Check the documentation to see which build options can be +adjusted. + ## Tests To run tests, make sure to have [pytest](https://docs.pytest.org/en/latest/) installed and run `scons test` if you have compiled Tamaas with tests activated -(`scons build_tests=True use_googletest=True`). +(`scons build_tests=True`). ## Documentation The latest documentation is available on -[ReadTheDocs](https://tamaas.readthedocs.io/en/latest/)! Note however that due -to technical limitations, the Python API documentation reflects the latest -version of the [PyPI package](https://pypi.org/project/tamaas/), which may be -sligthly out of sync with the master branch. +[ReadTheDocs](https://tamaas.readthedocs.io/en/latest/)! It contains detailed +guides on the different components of Tamaas as well as a combined C++ and +Python API documentation. -To build the documentation locally, activate the `build_doc` option and run -`scons doc`. Make sure you have +You can also build the documentation locally: activate the `build_doc` option +and run `scons doc`. Make sure you have [sphinx-rtd-theme](https://pypi.org/project/sphinx-rtd-theme/) and -[breath](https://pypi.org/project/breathe/) installed. The compiled indexes for +[breathe](https://pypi.org/project/breathe/) installed. The compiled indexes for the doxygen C++ API and Sphinx documentation can be found in `doc/build/{doxygen,sphinx}/html/index.html`. Beware however that manually compiling documentation leads to a lot of warnings. ## Examples Example simulations can be found in the `examples/` directory. There is no guarantee that the examples in `examples/legacy/` all work however. - `rough_contact.py` shows a typical normal rough contact simulation - `adhesion.py` shows how you can derive some classes from Tamaas in python, here to implement a custom adhesion potential - `plasticity.py` computes an elastoplastic Hertz simulation and dumps the result in `examples/paraview/` in VTK format - `stresses.py` shows how you can compute stresses from a boundary traction distribution - the scripts in `pipe_tools` allow to execute elastic contact simulations without the need to code a custom script (see documentation for more details) ## Command-line Interface The Tamaas python module exposes a `tamaas` command with three subcommands: - `tamaas surface` generates rough surfaces - `tamaas contact` solves elastic contact - `tamaas plot` plots contact pressure and displacement See `tamaas --help` for command line arguments. ## Contributing Contributions to Tamaas are welcome! Please follow the guidelines below. ### Report an issue If you have an account on [gitlab](https://gitlab.com), you can [submit an issue](https://gitlab.com/tamaas/tamaas/-/issues/new). The full list of issues is available [here](https://gitlab.com/tamaas/tamaas/-/issues). ### Submit a patch / merge-request Follow [this guide](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html#new-merge-request-from-a-fork) to create a merge request on GitLab. Please target the repository's `master` branch. ## Citing Tamaas is the result of a science research project. To give proper credit to Tamaas and the researchers who have developed the numerical methods that it implements, please cite Tamaas as: Frérot , L., Anciaux, G., Rey, V., Pham-Ba, S., & Molinari, J.-F. Tamaas: a library for elastic-plastic contact of periodic rough surfaces. Journal of Open Source Software, 5(51), 2121 (2020). [doi:10.21105/joss.02121](https://doi.org/10.21105/joss.02121) If you use the elastic-plastic contact capabilities of Tamaas, please cite: Frérot, L., Bonnet, M., Molinari, J.-F. & Anciaux, G. A Fourier-accelerated volume integral method for elastoplastic contact. Computer Methods in Applied Mechanics and Engineering 351, 951–976 (2019) [doi:10.1016/j.cma.2019.04.006](https://doi.org/10.1016/j.cma.2019.04.006). If you use the adhesive contact capabilities of Tamaas, please cite: Rey, V., Anciaux, G. & Molinari, J.-F. Normal adhesive contact on rough surfaces: efficient algorithm for FFT-based BEM resolution. Comput Mech 1–13 (2017) [doi:10.1007/s00466-017-1392-5](https://doi.org/10.1007/s00466-017-1392-5). ## Changelog The changelog can be consulted [here](https://gitlab.com/tamaas/tamaas/-/blob/master/CHANGELOG.md). ## License Tamaas is distributed under the terms of the [GNU Affero General Public License v3.0](https://www.gnu.org/licenses/agpl.html). diff --git a/SConstruct b/SConstruct index e0ad13d..0497e37 100644 --- a/SConstruct +++ b/SConstruct @@ -1,481 +1,474 @@ # -*- mode:python; coding: utf-8 -*- # vim: set ft=python: # # Copyright (©) 2016-2021 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 sys import os from subprocess import check_output # Import below not strictly necessary, but good for pep8 from SCons.Script import ( EnsurePythonVersion, EnsureSConsVersion, Help, Environment, Variables, EnumVariable, PathVariable, BoolVariable, ListVariable, Split, Export, Dir, ) from SCons.Errors import StopError from SCons import __version__ as scons_version from version import get_git_subst from detect import ( FindFFTW, FindBoost, FindThrust, FindCuda, FindExpolit, FindPybind11 ) from INFOS import TAMAAS_INFOS # ------------------------------------------------------------------------------ EnsurePythonVersion(2, 7) EnsureSConsVersion(2, 4) # ------------------------------------------------------------------------------ def detect_dependencies(env): "Detect all dependencies" fftw_comp = { 'omp': ['omp'], 'threads': ['threads'], 'none': [], } fftw_components = fftw_comp[env['fftw_threads']] if main_env['use_mpi']: fftw_components.append('mpi') if main_env['use_fftw']: FindFFTW(env, fftw_components, precision=env['real_type']) if main_env['use_cuda']: FindCuda(env) FindBoost(env, ['boost/preprocessor/seq.hpp']) # Use thrust shipped with cuda if cuda is requested thrust_var = 'CUDA_ROOT' if env['use_cuda'] else 'THRUST_ROOT' FindThrust(env, env['backend'], thrust_var) if env['build_python']: FindPybind11(env) FindExpolit(env) def subdir(env, dir): "Building a sub-directory" return env.SConscript(env.File('SConscript', dir), variant_dir=env.Dir(dir, env['build_dir']), duplicate=True) def print_build_info(env): info = ("-- Tamaas ${version}\n" + "-- SCons {} (Python {}.{})\n".format(scons_version, sys.version_info.major, sys.version_info.minor) + "-- Build type: ${build_type}\n" + "-- Thrust backend: ${backend}\n" + ("-- FFTW threads: ${fftw_threads}\n" if env['use_fftw'] else '') + "-- MPI: ${use_mpi}\n" + "-- Build directory: ${build_dir}\n" + ("-- Python version (bindings): $py_version" if env['build_python'] else '')) print(env.subst(info)) # ------------------------------------------------------------------------------ # Main compilation # ------------------------------------------------------------------------------ # Compilation colors colors = { 'cyan': '\033[96m', 'purple': '\033[95m', 'blue': '\033[94m', 'green': '\033[92m', 'yellow': '\033[93m', 'gray': '\033[38;5;8m', 'orange': '\033[38;5;208m', '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_INFOS._asdict().items(): main_env[k] = v main_env['COLOR_DICT'] = colors main_env.AddMethod(subdir, 'SubDirectory') # 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=('cpp', 'omp', 'tbb', 'cuda'), ignorecase=2), EnumVariable('fftw_threads', 'Threads FFTW library preference', 'omp', allowed_values=('omp', 'threads', 'none'), 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', os.getenv('CXX', 'g++')), ('MPICXX', 'MPI Compiler wrapper', os.getenv('MPICXX', 'mpicxx')), ('py_exec', 'Python executable', 'python3'), # Compiler flags ('CXXFLAGS', 'C++ compiler flags', os.getenv('CXXFLAGS', "")), # 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), # Documentation ListVariable('doc_builders', 'Generated documentation formats', default='html', names=Split("html man")), # TODO include latex # Dependencies - BoolVariable('use_googletest', 'Build tests using GTest', False), BoolVariable('use_mpi', 'Builds multi-process parallelism', False), # Distribution options BoolVariable('strip_info', 'Strip binary of added information', False), BoolVariable('build_static_lib', "Build a static libTamaas", 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.replace('\n', '\n# ') + "\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-${build_type}' main_env['build_dir'] = main_env.Dir(build_dir) # Setting up the python name with version if main_env['build_python']: args = (main_env.subst("${py_exec} -c").split() + ["from distutils.sysconfig import get_python_version;" "print(get_python_version())"]) main_env['py_version'] = bytes(check_output(args)).decode() 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['ARCOMSTR'] = u'{}[Ar]{} $TARGET'.format(colors['purple'], colors['end']) main_env['RANLIBCOMSTR'] = \ u'{}[Randlib]{} $TARGET'.format(colors['purple'], colors['end']) main_env['PRINT_CMD_LINE_FUNC'] = pretty_cmd_print main_env['INSTALLSTR'] = \ u'{}[Installing] {}$SOURCE to $TARGET'.format(colors['blue'], colors['end']) # Include paths main_env.AppendUnique(CPPPATH=['#/src', '#/src/core', - '#/src/bem', '#/src/surface', '#/src/percolation', '#/src/model', '#/src/model/elasto_plastic', '#/src/solvers', '#/src/gpu', '#/python']) # Changing the shared object extension main_env['SHOBJSUFFIX'] = '.o' # Variables for clarity main_env['use_cuda'] = main_env['backend'] == "cuda" main_env['use_fftw'] = not main_env['use_cuda'] main_env['use_mpi'] = main_env['use_mpi'] and not main_env['use_cuda'] if not main_env['use_fftw']: main_env['fftw_threads'] = 'none' # Back to gcc if cuda is activated if main_env['use_cuda'] and "g++" not in main_env['CXX']: raise StopError('GCC should be used when compiling with CUDA') # Printing some build infos if main_env['should_configure']: print_build_info(main_env) # 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 StopError('Unsupported compiler: ' + cxx) cxx = cxx_alias(main_env['CXX']) # Setting main compilation flags main_env['CXXFLAGS'] = Split(main_env['CXXFLAGS']) main_env['LINKFLAGS'] = main_env['CXXFLAGS'] main_env.AppendUnique( CXXFLAGS=Split('-std=c++14 -Wall -Wextra'), CPPDEFINES={ 'TAMAAS_LOOP_BACKEND': 'TAMAAS_LOOP_BACKEND_${backend.upper()}', 'TAMAAS_FFTW_BACKEND': 'TAMAAS_FFTW_BACKEND_${fftw_threads.upper()}' }, ) if main_env['backend'] != 'cuda': main_env.AppendUnique(CXXFLAGS=['-pedantic']) # Adding OpenMP flags if main_env['backend'] == 'omp': main_env.AppendUnique(CXXFLAGS=omp_flags[cxx]) main_env.AppendUnique(LINKFLAGS=omp_flags[cxx]) else: main_env.AppendUnique(CXXFLAGS=['-Wno-unknown-pragmas']) # 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']) # Manage MPI compiler if main_env['use_mpi']: main_env['CXX'] = '$MPICXX' main_env.AppendUnique(CPPDEFINES=['TAMAAS_USE_MPI']) main_env.AppendUnique(CXXFLAGS=['-Wno-cast-function-type']) # Flags and options if main_env['build_type'] == 'debug': main_env.AppendUnique(CPPDEFINES=['TAMAAS_DEBUG']) # Define the scalar types main_env.AppendUnique(CPPDEFINES={'TAMAAS_REAL_TYPE': '${real_type}', 'TAMAAS_INT_TYPE': '${integer_type}'}) # Compilation flags cxxflags_dict = { "debug": Split("-g -O0"), "profiling": Split("-g -O3 -fno-omit-frame-pointer"), "release": Split("-O3") } if main_env['sanitizer'] != 'none': if main_env['backend'] == 'cuda': raise StopError( "Sanitizers with cuda are not yet supported!") cxxflags_dict[build_type].append('-fsanitize=${sanitizer}') main_env.AppendUnique(CXXFLAGS=cxxflags_dict[build_type]) main_env.AppendUnique(SHLINKFLAGS=cxxflags_dict[build_type]) main_env.AppendUnique(LINKFLAGS=cxxflags_dict[build_type]) if main_env['should_configure']: basic_checks(main_env) detect_dependencies(main_env) # Writing information file main_env.Tool('textfile') main_env['SUBST_DICT'] = get_git_subst() # 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@': '${build_dir.abspath}', '@build_version@': '$version', '@backend@': '$backend', }) # 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( main_env.File('tamaas_environment.sh', main_env['build_dir']), env_content) # Default targets build_targets = ['build-cpp', env_file] install_targets = ['install-lib'] if main_env._get_major_minor_revision(scons_version)[0] >= 4: main_env.Tool('compilation_db') main_env.CompilationDatabase(PRINT_CMD_LINE_FUNC=pretty_cmd_print) # Building Tamaas library Export('main_env') main_env.SubDirectory('src') # Building Tamaas extra components for dir in ['python', 'tests']: if main_env['build_{}'.format(dir)] and not main_env.GetOption('help'): main_env.SubDirectory(dir) build_targets.append('build-{}'.format(dir)) # Building API + Sphinx documentation if requested -if main_env['build_doc']: - main_env.SubDirectory('doc') - main_env.Alias('doc', 'build-doc') - install_targets.append('install-doc') -else: - dummy_command(main_env, 'doc', 'Command "doc" does not do anything' - ' without documentation activated ("build_doc=True")') +main_env.SubDirectory('doc') +main_env.Alias('doc', 'build-doc') +install_targets.append('install-doc') # 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-${version}.tar.gz', '', ('git archive ' '--format=tar.gz ' '--prefix=tamaas/ ' '-o $TARGET HEAD'), ) main_env.Alias('archive', archive) diff --git a/doc/SConscript b/doc/SConscript index 93dee47..8003c9d 100644 --- a/doc/SConscript +++ b/doc/SConscript @@ -1,94 +1,104 @@ # -*- mode:python; coding: utf-8 -*- # vim: set ft=python: # # Copyright (©) 2016-2021 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 SCons.Script import Import, Glob, Dir, File def add_sources(source_list, dirname, fnames): source_list += Dir(dirname).glob('*.hh') source_list += Dir(dirname).glob('*.cpp') Import('main_env') Import('libTamaas') doc_env = main_env.Clone() doc_env['man_section'] = 7 - -# Generating Doxyfile -doxygen_verbose = {True: "NO", False: "YES"} -doxyfile_target = doc_env.Substfile('doxygen/Doxyfile', 'doxygen/Doxyfile.in', - SUBST_DICT={ - '@version@': '$version', - '@build_dir@': Dir('doxygen').path, - '@logo@': File('icon.svg').path, - '@src_dir@': Dir('#src').abspath, - '@verbose@': - doxygen_verbose[doc_env["verbose"]], - }) +doc_targets = [] # Generate Doxygen API documentation doc_env.Tool('doxygen') -doxygen_target = doc_env.Doxygen('doxygen/xml/index.xml', - [doxyfile_target, 'icon.svg']) -# Adding all source files as dependencies -sources = [] -Dir('#src').walk(add_sources, sources) -doc_env.Depends(doxygen_target, sources) +if doc_env['has_doxygen']: + # Generating Doxyfile + doxygen_verbose = {True: "NO", False: "YES"} + doxyfile_target = doc_env.Substfile('doxygen/Doxyfile', + 'doxygen/Doxyfile.in', + SUBST_DICT={ + '@version@': '$version', + '@build_dir@': Dir('doxygen').path, + '@logo@': File('icon.svg').path, + '@src_dir@': Dir('#src').abspath, + '@verbose@': + doxygen_verbose[doc_env["verbose"]], + }) + + doxygen_target = doc_env.Doxygen('doxygen/xml/index.xml', + [doxyfile_target, 'icon.svg']) + + # Adding all source files as dependencies + sources = [] + Dir('#src').walk(add_sources, sources) + doc_env.Depends(doxygen_target, sources) + doc_targets.append(doxygen_target) # Generate Sphinx User documentation sphinx_targets_map = { "html": "sphinx/html/index.html", "man": "sphinx/tamaas.${man_section}", "latex": "sphinx/latex/Tamaas.tex" } doc_env.Tool('sphinx') -doc_env['IMPLICIT_COMMAND_DEPENDENCIES'] = 0 -sphinx_sources = [Glob('sphinx/source/*'), Glob('sphinx/source/figures/*')] -sphinx_targets = { - builder: doc_env.Sphinx(sphinx_targets_map[builder], sphinx_sources) - for builder in doc_env['doc_builders'] -} +sphinx_targets = None +if doc_env['has_sphinx'] and len(doc_targets) != 0: + doc_env['IMPLICIT_COMMAND_DEPENDENCIES'] = 0 + sphinx_sources = [Glob('sphinx/source/*'), Glob('sphinx/source/figures/*')] + sphinx_targets = { + builder: doc_env.Sphinx(sphinx_targets_map[builder], sphinx_sources) + for builder in doc_env['doc_builders'] + } + + for target in sphinx_targets.values(): + doc_env.Depends(target, doxygen_target) -for target in sphinx_targets.values(): - doc_env.Depends(target, doxygen_target) + doc_targets += list(sphinx_targets.values()) # Alias for both docs -doc_targets = [doxygen_target] + list(sphinx_targets.values()) main_env.Alias('build-doc', doc_targets) # Install target for documentation share = Dir('share', doc_env['prefix']) doc_install = [] sphinx_prefix_map = {} -if "html" in doc_env['doc_builders']: - doc_install.append(doc_env.Install(target=share.Dir('doc').Dir('tamaas'), - source=sphinx_targets['html'][0].dir)) -if "man" in doc_env['doc_builders']: - doc_install.append(doc_env.Install( - target=share.Dir('man') .Dir(doc_env.subst('man${man_section}')), - source=sphinx_targets['man'])) +if sphinx_targets is not None: + if "html" in doc_env['doc_builders']: + doc_install.append(doc_env.Install( + target=share.Dir('doc').Dir('tamaas'), + source=sphinx_targets['html'][0].dir)) + if "man" in doc_env['doc_builders']: + doc_install.append(doc_env.Install( + target=share.Dir('man') .Dir(doc_env.subst('man${man_section}')), + source=sphinx_targets['man'])) main_env.Alias('install-doc', doc_install) diff --git a/doc/requirements.txt b/doc/requirements.txt index 7583e70..3b88c22 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,6 +1,11 @@ -breathe +--extra-index-url https://gitlab.com/api/v4/projects/19913787/packages/pypi/simple + +breathe==4.24.1 +sphinx==3.3.1 +sphinx-rtd-theme==0.5.2 + uvw h5py netCDF4 scipy tamaas diff --git a/doc/sphinx/source/overview.rst b/doc/sphinx/source/overview.rst index 5bf0ce1..2c842c8 100644 --- a/doc/sphinx/source/overview.rst +++ b/doc/sphinx/source/overview.rst @@ -1,72 +1,69 @@ Library overview ================ Tamaas is mainly composed of three components: - Random surface generation procedures - - Model state objects and operators - - Contact solving procedures These parts are meant to be independent as well as inter-operable. The first one provides an interface to several stochastic surface generation algorithms described in :doc:`rough_surfaces`. The second provides an interface to a state object :py:class:`Model ` (and C++ class :cpp:class:`tamaas::Model`) as well as integral operators based on the state object (see :doc:`model`). Finally, the third part provides contact solving routines that make use of the integral operators for performance (see :doc:`contact`). .. _sect-tutorials: Tutorials --------- The following tutorial notebooks can also be used to learn about Tamaas: - Elastic Contact (`live notebook `_, `notebook viewer `_) - - Rough Surfaces (`live notebook `_, `notebook viewer `_) Changelog --------- The changelog can be consulted `here `_. Seeking help ------------ You can ask your questions on `gitlab `_ using this `form `_. If you do not have an account, you can create one `on this page `_. Contribution ------------ Contributions to Tamaas are welcome, whether they are code, bug, or documentation related. Code .... Please `fork `_ Tamaas and `submit your patch as a merge request `_. Bug reports ........... You can also contribute to Tamaas by reporting any bugs you find `here `_ if you have an account on `gitlab `_. diff --git a/doc/sphinx/source/quickstart.rst b/doc/sphinx/source/quickstart.rst index 9d52be5..e6baa65 100644 --- a/doc/sphinx/source/quickstart.rst +++ b/doc/sphinx/source/quickstart.rst @@ -1,198 +1,215 @@ Quickstart ---------- Here is a quick introduction to get you started with Tamaas. Installation from PyPI ^^^^^^^^^^^^^^^^^^^^^^ -If you have a Linux system, you can simply run ``pip(3) install tamaas``. Note -however that there may be due to compatibility reasons, this version of Tamaas -is not built with parallel capabilities. So if you want parallelism, or -encounter an issue with the `PyPI package `_, -please compile from source. +If you have a Linux system, you can simply run ``python3 -m pip install +tamaas``. This installs the latest stable version of Tamaas from `PyPI +`_. You can get the latest cutting-edge build +of Tamaas with: + +.. code-block:: bash + + python3 -m pip install \ + --extra-index-url https://gitlab.com/api/v4/projects/19913787/packages/pypi/simple \ + tamaas + + +.. note:: + To limit the number of statically linked dependencies in the wheel package, + the builds that can be installed via PyPI or the GitLab package registery do + not include parallelism or architecture specific optimizations. If you want + to execute Tamaas in parallel, or want to improve performance, it is highly + recommanded that you compile from source with the instructions below. + + +Docker image +^^^^^^^^^^^^ + +The ``Dockerfile`` at the root of the Tamaas repository can be used to build an +image containing Tamaas with a full set of dependencies. Simply run: + +.. code-block:: bash + + docker build -t tamaas . + +.. tip:: + The following arguments can be passed to docker to customize the Tamaas + build (with the ``--build-arg`` flag for ``docker build``): + + - ``BACKEND``: parallelism model for loops + - ``FFTW_THREADS``: parallelism model for FFTW3 + - ``USE_MPI``: compile an MPI-parallel version of Tamaas + + See below for explanations. + + Installation from source ^^^^^^^^^^^^^^^^^^^^^^^^ First make sure the following dependencies are installed for Tamaas: - a **C++ compiler** with full **C++14** and **OpenMP** support - - **SCons** (python build system) - - **FFTW3** - - **thrust** (1.9.2+) - - **boost** (pre-processor) - - **python 3+** with **numpy** - - **pybind11** (included as submodule) - - **expolit** (included as submodule) Optional dependencies are: - an MPI implementation - - **FFTW3** with MPI/threads/OpenMP (your pick) support - - **scipy** (for nonlinear solvers) - - **uvw**, **h5py**, **netCDF4** (for dumpers) - - **googletest** and **pytest** (for tests) - - **Doxygen** and **Sphinx** (for documentation) .. tip:: On a HPC environment, use the following variables to specify where the dependencies are located: - ``FFTW_ROOT`` - ``THRUST_ROOT`` - ``BOOST_ROOT`` - ``PYBIND11_ROOT`` - ``GTEST_ROOT`` .. note:: You can use the provided Dockerfile to build an image with the external dependencies installed. You should first clone the git repository with the submodules that are dependencies to tamaas (`pybind11 `_ and `googletest `_):: git clone --recursive https://gitlab.com/tamaas/tamaas.git The build system uses `SCons `_. In order to compile Tamaas with the default options:: scons After compiling a first time, you can edit the compilation options in the file ``build-setup.conf``, or alternatively supply the options directly in the command line: .. code-block:: bash scons option=value [...] To get a list of **all** build options and their possible values, you can run ``scons -h``. You can run ``scons -H`` to see the SCons-specific options (among them ``-j n`` executes the build with ``n`` threads and ``-c`` cleans the build). Note that the build is aware of the ``CXX`` and ``CXXFLAGS`` environment variables. Once compiled, you can install the python module with: .. code-block:: bash scons install prefix=/your/prefix The above command automatically calls ``pip(3)`` if it is installed, and falls back on ``setuptools`` otherwise. If you do not want to install Tamaas, you can use the following command:: scons dev This creates a symlink in ``~/.local/lib//site-packages`` and is equivalent to ``pip(3) install -e`` or ``./setup.py develop``. You can check that everything is working fine with: .. code-block:: bash python3 -c 'import tamaas; print(tamaas)' -Using Docker -............ - -The Tamaas repository provides a `Dockerfile` that describes an appropriate -build environment. You can use it in the following way: - -.. code-block:: bash - - # Build the image, run it and mount the tamaas repository - docker build -t tamaas_build . - docker run -v $PWD:/app/tamaas -it tamaas_build bash - - # Once in the image shell: compile and install - cd /app/tamaas - scons - scons dev - -The image also has some of the dependencies required to run the examples below -(matplotlib, uvw). - Important build options ....................... Here are a selected few important compilation options: ``build_type`` Controls the type of build, which essentially changes the optimisation level (``-O0`` for ``debug``, ``-O2`` for ``profiling`` and ``-O3`` for - ``release``) and the amount of debug information. Default type is ``release``. + ``release``) and the amount of debug information. Default type is + ``release``. Accepted values are: + + - ``release`` + - ``debug`` + - ``profiling`` ``CXX`` Compiler (uses the environment variable ``CXX`` by default). ``CXXFLAGS`` Compiler flags (uses the environment variable ``CXXFLAGS`` by default). Useful to add tuning flags (e.g. ``-march=native -mtune=native`` for GCC or ``-xHOST`` for Intel), or additional optimisation flags (e.g. ``-flto`` for link-time optimization). ``backend`` Controls the Thrust parallelism backend. Defaults to ``omp`` for OpenMP. + Accepted values are: + + - ``omp``: OpenMP + - ``cpp``: Sequential + - ``tbb`` (unsupported): Threading Building Blocks ``fftw_threads`` - Controls the FFTW thread model. Defaults to ``omp`` for OpenMP. + Controls the FFTW thread model. Defaults to ``omp`` for OpenMP. Accepted + values are: + + - ``omp``: OpenMP + - ``threads``: PThreads + - ``none``: Sequential ``use_mpi`` - Activates MPI-parallelism. + Activates MPI-parallelism (boolean value). More details on the above options can be found in :doc:`performance`. Building the docs ^^^^^^^^^^^^^^^^^ Documentation is built using ``scons doc``. Make sure to have the correct dependencies installed (they are already provided in the Docker image). Running the contact pipe tools ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Once installed, the python package provides a `tamaas` command, which defines three subcommands that can be used to explore the mechanics of elastic rough contact: - ``surface`` generates randomly rough surfaces (see :doc:`rough_surfaces`) - - ``contact`` solves an elastic contact problem with a surface read from ``stdin`` (see :doc:`contact`) - - ``plot`` plots the surface tractions and displacements read from ``stdin`` Here's a sample command line for you to try out: .. code-block:: bash tamaas surface --cutoffs 2 4 64 --size 512 512 --hurst 0.8 | tamaas contact 1 | tamaas plot Check out the help of each command (e.g. ``tamaas surface -h``) for a description of the arguments. Running the tests ^^^^^^^^^^^^^^^^^ You need to activate the ``build_tests`` option to compile the tests: .. code-block:: bash scons build_tests=True Tests can then be run with the ``scons test`` command. Make sure you have `pytest `_ installed. diff --git a/doc/sphinx/source/rough_surfaces.rst b/doc/sphinx/source/rough_surfaces.rst index a5d42b3..cd49948 100644 --- a/doc/sphinx/source/rough_surfaces.rst +++ b/doc/sphinx/source/rough_surfaces.rst @@ -1,119 +1,117 @@ Random rough surfaces ===================== The generation of stochatsticly rough surfaces is controlled in Tamaas by two abstract classes: :cpp:class:`tamaas::SurfaceGenerator` and :cpp:class:`tamaas::Filter`. The former provides access lets the user set the surface sizes and random seed, while the latter encodes the information of the spectrum of the surface. Two surface generation methods are provided: - :cpp:class:`tamaas::SurfaceGeneratorFilter` implements a Fourier filtering algorithm (see `Hu & Tonder `_), - - :cpp:class:`tamaas::SurfaceGeneratorRandomPhase` implements a random phase filter. Both of these rely on a :cpp:class:`tamaas::Filter` object to provided the filtering information (usually power spectrum density coefficients). Tamaas provides two such classes and allows for Python subclassing: - :cpp:class:`tamaas::Isopowerlaw` provides a roll-off powerlaw, - - :cpp:class:`tamaas::RegularizedPowerlaw` provides a powerlaw with a regularized rolloff. Tamaas also provided routines for surface statistics. Generating a surface -------------------- Let us now see how to generate a surface. Frist create a filter object and set the surface sizes:: import tamaas as tm # Create spectrum object spectrum = tm.Isopowerlaw2D() # Set spectrum parameters spectrum.q0 = 4 spectrum.q1 = 4 spectrum.q2 = 32 spectrum.hurst = 0.8 The ``spectrum`` object can be queried for information, such as the root-mean-square of heights, the various statistical moments, the spectrum bandwidth, etc. Then we create a generator object and build the random surface:: generator = tm.SurfaceGeneratorFilter2D([128, 128]) generator.spectrum = spectrum generator.random_seed = 0 surface = generator.buildSurface() .. important:: The ``surface`` object is a :py:class:`numpy.ndarray` wrapped around internal memory in the ``generator`` object, so a subsequent call to :py:func:`buildSurface ` may change its content. Note that if ``generator`` goes out of scope its memory will not be freed if there is still a live reference to the surface data. .. important:: If ran in an MPI context, the constructor of :py:class:`SurfaceGeneratorFilter2D ` (and others) expects the *global* shape of the surface. The shape can also be changed with ``generator.shape = [64, 64]``. Custom filter ------------- Tamaas provides several classes that can be derived directly with Python classes, and :cpp:class:`tamaas::Filter` is one of them. Since it provides a single pure virtual method :cpp:func:`computeFilter `, it is easy to write a sub-class. Here we implement a class that takes a user-defined auto-correlation function and implements the :cpp:func:`computeFilter ` virtual function:: import numpy class AutocorrelationFilter(tm.Filter2D): def __init__(self, autocorrelation): tm.Filter2D.__init__(self) self.autocorrelation = autocorrelation.copy() def computeFilter(self, filter_coefficients): shifted_ac = numpy.fft.ifftshift(self.autocorrelation) # Fill in the PSD coefficients filter_coefficients[...] = numpy.sqrt(np.fft.rfft2(shifted_ac)) # Normalize filter_coefficients[...] *= 1 / numpy.sqrt(self.autocorrelation.size) Here ``filter_coefficients`` is also a :py:class:`numpy.ndarray` and is therefore easily manipulated. The creation of the surface then follows the same pattern as previously:: # Create spectrum object autocorrelation = ... # set your desired autocorrelation spectrum = AutocorrelationFilter(autocorrelation) generator = tm.SurfaceGenerator2D() generator.shape = autocorrelation.shape generator.spectrum = spectrum surface = generator.buildSurface() The lifetime of the ``spectrum`` object is associated to the ``generator`` when :py:func:`setSpectrum ` is called. Surface Satistics ----------------- Tamaas provides the C++ class :cpp:class:`tamaas::Statistics` and its wrapper :py:class:`Statistics2D ` to compute statistics on surfaces, including: - power spectrum density - autocorrelation - spectrum moments - root-mean-square of heights :math:`\sqrt{\langle h^2 \rangle}` - root-mean-square of slopes (computed in Fourier domain) :math:`\sqrt{\langle |\nabla h|^2\rangle}` All these quantities are computed in a discretization-independent manner: increasing the number of points in the surface should not drastically change the computed values (for a given spectrum). This allows to refine the discretization as much as possible to approximate a continuum. Note that the autocorrelation and PSD are fft-shifted. Here is a sample code plotting the PSD and autocorrelation:: psd = tm.Statistics2D.computePowerSpectrum(surface) psd = numpy.fft.fftshift(psd, axes=0) # Shifting only once axis because of R2C transform import matplotlib.pyplot as plt from matplotlib.colors import LogNorm plt.imshow(psd.real, norm=LogNorm()) acf = tm.Statistics2D.computeAutocorrelation(surface) acf = numpy.fft.fftshift(acf) plt.figure() plt.imshow(acf) plt.show() See ``examples/statistics.py`` for more usage examples of statistics. diff --git a/python/SConscript b/python/SConscript index c3ec2e0..beca60a 100644 --- a/python/SConscript +++ b/python/SConscript @@ -1,161 +1,161 @@ # -*- mode:python; coding: utf-8 -*- # vim: set ft=python: # # Copyright (©) 2016-2021 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 SCons.Script import Import, Split, Copy, Dir Import('main_env') # Pybind11 wrapper env_pybind = main_env.Clone(SHLIBPREFIX='') # Remove pedantic warnings cxx_flags = env_pybind['CXXFLAGS'] try: del cxx_flags[cxx_flags.index('-pedantic')] except ValueError: pass 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/compute.cpp wrap/mpi.cpp wrap/test_features.cpp """) # Setting paths to find libTamaas env_pybind.AppendUnique(LIBPATH=['../src']) # Link against a static libTamaas if env_pybind['build_static_lib']: env_pybind.PrependUnique(LIBS=['Tamaas']) # keep other libs for link env_pybind['RPATH'] = "" # no need for rpath w/ static lib # Link against a dynamic libTamaas else: env_pybind.AppendUnique(RPATH=[ "'$$$$ORIGIN/../../src'", # path to lib in build_dir "'$$$$ORIGIN/../../..'", # path to lib in install prefix ]) env_pybind['LIBS'] = ['Tamaas'] # discard other libs for link # Building the pybind library tamaas_wrap = env_pybind.Pybind11Module( target='tamaas/_tamaas', source=pybind_sources, ) # 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() # Copying additional python files python_files = """ __main__.py compute.py dumpers/__init__.py dumpers/_helper.py nonlinear_solvers/__init__.py """.split() targets = [tamaas_wrap] targets += [ copy_env.Command(copy_env.File(f, 'tamaas'), copy_env.File(f, '#python/tamaas'), Copy("$TARGET", "$SOURCE")) for f in python_files ] targets.append(copy_env.Command('MANIFEST.in', '#python/MANIFEST.in', Copy("$TARGET", "$SOURCE"))) subst_env = env_pybind.Clone( SUBST_DICT={ '@version@': '$version', '@authors@': str(copy_env['authors']), '@email@': '$email', - '@description@': '$email', + '@description@': '$description', # TODO change when issue with unicode fixed # '@copyright@': '$copyright', # '@maintainer@': '$maintainer', } ) subst_env.Tool('textfile') targets.append(subst_env.Substfile('setup.py.in')) targets.append(subst_env.Substfile('tamaas/__init__.py.in')) # Defining alias for python builds main_env.Alias('build-python', targets) # Checking if we can use pip to install (more convenient for end-user) install_env = main_env.Clone() conf = Configure(install_env, custom_tests={'CheckPythonModule': CheckPythonModule}) has_pip = conf.CheckPythonModule('pip') install_env = conf.Finish() # Current build directory install_env['PYDIR'] = Dir('.') # Setting command line for installation if has_pip: install_env['PYINSTALLCOM'] = '${py_exec} -m pip install -U $PYOPTIONS .' install_env['PYDEVELOPCOM'] = \ '${py_exec} -m pip install $PYOPTIONS -e .[all]' else: install_env['PYINSTALLCOM'] = '${py_exec} setup.py install $PYOPTIONS' install_env['PYDEVELOPCOM'] = '${py_exec} setup.py develop $PYOPTIONS' install_env['py_version'] = get_python_version(install_env) install_env.PrependENVPath( 'PYTHONPATH', install_env.subst('${prefix}/lib/python${py_version}/site-packages')) # Specify install target PYOPTIONS = ['${"" if verbose else "-q"}'] python_install = install_env.Command( '.python_install_phony', targets, install_env['PYINSTALLCOM'], PYOPTIONS=['--prefix', '${prefix}'] + PYOPTIONS, chdir=install_env['PYDIR']) python_install_dev = install_env.Command( '.python_install_local_phony', targets, install_env['PYDEVELOPCOM'], PYOPTIONS=['--user'] + PYOPTIONS, chdir=install_env['PYDIR']) # Defining aliases main_env.Alias('install-python', python_install) main_env.Alias('dev', python_install_dev) diff --git a/python/cast.hh b/python/cast.hh index 4f7e2c1..e8b608c 100644 --- a/python/cast.hh +++ b/python/cast.hh @@ -1,181 +1,183 @@ /* * SPDX-License-Indentifier: AGPL-3.0-or-later * * Copyright (©) 2016-2021 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 CAST_HH #define CAST_HH /* -------------------------------------------------------------------------- */ #include "grid_base.hh" #include "grid.hh" #include "numpy.hh" /* -------------------------------------------------------------------------- */ #include #include /* -------------------------------------------------------------------------- */ namespace pybind11 { // Format descriptor necessary for correct wrap of tamaas complex type template struct format_descriptor< tamaas::complex, detail::enable_if_t::value>> { static constexpr const char c = format_descriptor::c; static constexpr const char value[3] = {'Z', c, '\0'}; static std::string format() { return std::string(value); } }; #ifndef PYBIND11_CPP17 template constexpr const char format_descriptor< tamaas::complex, detail::enable_if_t::value>>::value[3]; #endif namespace detail { // declare tamaas complex as a complex type for pybind11 template struct is_complex> : std::true_type {}; template struct is_fmt_numeric, detail::enable_if_t::value>> : std::true_type { static constexpr int index = is_fmt_numeric::index + 3; }; static inline handle policy_switch(return_value_policy policy, handle parent) { switch (policy) { case return_value_policy::copy: case return_value_policy::move: return handle(); case return_value_policy::automatic_reference: // happens in python-derived // classes case return_value_policy::reference: return none(); case return_value_policy::reference_internal: return parent; default: TAMAAS_EXCEPTION("Policy is not handled"); } } template handle grid_to_python(const tamaas::Grid& grid, return_value_policy policy, handle parent) { parent = policy_switch(policy, parent); // reusing variable std::vector sizes(dim); std::copy(grid.sizes().begin(), grid.sizes().end(), sizes.begin()); if (grid.getNbComponents() != 1) sizes.push_back(grid.getNbComponents()); return array(sizes, grid.getInternalData(), parent).release(); } /** * Type caster for grid classes * inspired by https://tinyurl.com/y8m47qh3 from T. De Geus * and pybind11/eigen.h */ template