diff --git a/CHANGELOG.md b/CHANGELOG.md index 79891eb..b45d1d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,167 +1,172 @@ # 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). ## Unreleased +### 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. ## 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/python/SConscript b/python/SConscript index 727cd67..d31d294 100644 --- a/python/SConscript +++ b/python/SConscript @@ -1,159 +1,160 @@ # -*- mode:python; coding: utf-8 -*- # vim: set ft=python: # @file # LICENSE # # 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', # 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 python_install = install_env.Command( '.python_install_phony', targets, install_env['PYINSTALLCOM'], PYOPTIONS='--prefix ${prefix}', chdir=install_env['PYDIR']) python_install_dev = install_env.Command( '.python_install_local_phony', targets, install_env['PYDEVELOPCOM'], PYOPTIONS='--user', chdir=install_env['PYDIR']) # Defining aliases main_env.Alias('install-python', python_install) main_env.Alias('dev', python_install_dev) diff --git a/python/setup.py.in b/python/setup.py.in index 5abb1d0..2c4ab58 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -1,108 +1,111 @@ # -*- mode:python; coding: utf-8 -*- import setuptools import shutil import sysconfig import os long_description = """ # Tamaas - A high-performance library for periodic rough surface contact [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3479236.svg)](https://doi.org/10.5281/zenodo.3479236) [![status](https://joss.theoj.org/papers/86903c51f3c66964eef7776d8aeaf17d/status.svg)](https://joss.theoj.org/papers/86903c51f3c66964eef7776d8aeaf17d) [![Documentation Status](https://readthedocs.org/projects/tamaas/badge/?version=latest)](https://tamaas.readthedocs.io/en/latest/?badge=latest) [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/git/https%3A%2F%2Fc4science.ch%2Fsource%2Ftamaas_tutorials.git/HEAD?filepath=elastic_contact.ipynb) Tamaas (from تماس meaning “contact” in Arabic and Farsi) is a high-performance rough-surface periodic contact code based on boundary and volume integral equations. The clever mathematical formulation of the underlying numerical methods allows the use of the fast-Fourier Transform, a great help in achieving peak performance: Tamaas is consistently two orders of magnitude faster (and lighter) than traditional FEM! Tamaas is aimed at researchers and practitioners wishing to compute realistic contact solutions for the study of interface phenomena. ## Disclaimer This package is intended for ease of installation for Linux platforms, but comes with NO WARRANTY of compatibility (although it is manylinux2010_x86_64 compliant). If you experience any issue, please install Tamaas from [source](https://c4science.ch/source/tamaas/). We provide a Docker image for non-Linux systems. Note that to satisfy the manylinux2010 requirements, the version of Tamaas provided by this package is not parallel. This version of Tamaas is statically linked to [FFTW3](http://fftw.org/) developed by Matteo Frigo and Steven G. Johnson, released under the terms of the GPL public license ([source code](https://github.com/FFTW/fftw3)). 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 the [JOSS paper](https://joss.theoj.org/papers/86903c51f3c66964eef7776d8aeaf17d) and the appropriate references therein. ## Dependencies Essentials: - Numpy Optional: - Scipy (for non-linear solvers) - UVW (for dumpers) - h5py (for dumpers) - netCDF4 (for dumpers) To install with all dependencies, run ``pip install tamaas[solvers,dumpers]``. ## Documentation Documentation can be found on [tamaas.readthedocs.io](https://tamaas.readthedocs.io/en/latest/). ## Changelog The changelog can be consulted [here](https://c4science.ch/source/tamaas/browse/master/CHANGELOG.md?as=remarkup). """ try: shutil.copyfile('../../README.md', 'README.md') except FileNotFoundError: pass setuptools.setup( name="tamaas", version="@version@", packages=setuptools.find_packages(), include_package_data=True, author=', '.join(@authors@), author_email="@email@", description='A high-performance library for periodic rough surface contact', long_description=long_description, long_description_content_type="text/markdown", url="https://c4science.ch/tag/tamaas/", project_urls={ "Bug Tracker": "https://c4science.ch/maniphest/query/1jDBkIDDxCAP/", "Documentation": "https://tamaas.readthedocs.io/en/latest/", "Source Code": "https://c4science.ch/source/tamaas/", }, platforms=['Linux'], install_requires=['numpy'], + entry_points={ + "console_scripts": ['tamaas = tamaas.__main__:main'], + }, extras_require={ "all": ['uvw', 'h5py', 'netCDF4', 'scipy', 'mpi4py'], "dumpers": ['uvw', 'h5py', 'netCDF4'], "solvers": ['scipy'], "mpi": ['mpi4py'], }, classifiers=[ "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU Affero General Public License v3", "Programming Language :: C++", "Programming Language :: Python :: 3", "Topic :: Scientific/Engineering", "Intended Audience :: Science/Research", "Operating System :: POSIX :: Linux", ] ) diff --git a/python/tamaas/__main__.py b/python/tamaas/__main__.py new file mode 100755 index 0000000..29ae7d6 --- /dev/null +++ b/python/tamaas/__main__.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8 -*- +# vim: set ft=python: + +import sys +import io +import time +import argparse +import tamaas as tm +import numpy as np + + +__author__ = "Lucas Frérot" +__copyright__ = ( + "Copyright (©) 2019-2021, EPFL (École Polytechnique Fédérale de Lausanne)," + "\nLaboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)" +) +__license__ = "AGPL" +__email__ = "lucas.frerot@gmail.com" + + +def load_stream(stream): + """ + Load numpy from binary stream (allows piping) + + Code from + https://gist.github.com/CMCDragonkai/3c99fd4aabc8278b9e17f50494fcc30a + """ + np_magic = stream.read(6) + # use the sys.stdin.buffer to read binary data + np_data = stream.read() + # read it all into an io.BytesIO object + return io.BytesIO(np_magic + np_data) + + +def surface(args): + if args.generator == 'random_phase': + generator = tm.SurfaceGeneratorRandomPhase2D(args.sizes) + elif args.generator == 'filter': + generator = tm.SurfaceGeneratorFilter2D(args.sizes) + else: + raise ValueError('Unknown generator method {}'.format(args.generator)) + + generator.spectrum = tm.Isopowerlaw2D() + generator.spectrum.q0 = args.cutoffs[0] + generator.spectrum.q1 = args.cutoffs[1] + generator.spectrum.q2 = args.cutoffs[2] + generator.spectrum.hurst = args.hurst + generator.random_seed = args.seed + + surface = generator.buildSurface() / generator.spectrum.rmsSlopes() \ + * args.rms + + output = args.output if args.output is not None else sys.stdout + + np.savetxt(output, surface) + + +def contact(args): + from tamaas.dumpers import NumpyDumper + + tm.set_log_level(tm.LogLevel.error) + + if not args.input: + input = sys.stdin + else: + input = args.input + + surface = np.loadtxt(input) + + discretization = surface.shape + system_size = [1., 1.] + + model = tm.ModelFactory.createModel(tm.model_type.basic_2d, + system_size, discretization) + solver = tm.PolonskyKeerRey(model, surface, args.tol) + + solver.solve(args.load) + + dumper = NumpyDumper('numpy', 'traction', 'displacement') + dumper.dump_to_file(sys.stdout.buffer, model) + + +def plot(args): + import matplotlib.pyplot as plt + + fig, (ax_traction, ax_displacement) = plt.subplots(1, 2) + + ax_traction.set_title('Traction') + ax_displacement.set_title('Displacement') + + with load_stream(sys.stdin.buffer) as f_np: + data = np.load(f_np) + ax_traction.imshow(data['traction']) + ax_displacement.imshow(data['displacement']) + + fig.set_size_inches(10, 6) + fig.tight_layout() + + plt.show() + + +def main(): + parser = argparse.ArgumentParser( + prog='tamaas', + description=("tm.py is a simple utility script for surface generation and," + " elastic contact computation and plotting of contact" + " solutions"), + ) + + subs = parser.add_subparsers(title='commands', + description='utility commands') + + # Arguments for surface command + parser_surface = subs.add_parser( + 'surface', description='Generate a self-affine rough surface') + parser_surface.add_argument("--cutoffs", "-K", + nargs=3, + type=int, + help="Long, rolloff and short wavelength cutoffs", + metavar=('k_l', 'k_r', 'k_s'), + required=True) + parser_surface.add_argument("--sizes", + nargs=2, + type=int, + help="Number of points", + metavar=('nx', 'ny'), + required=True) + parser_surface.add_argument("--hurst", "-H", + type=float, + help="Hurst exponent", + required=True) + parser_surface.add_argument("--rms", + type=float, + help="Root-mean-square of slopes", + default=1.) + parser_surface.add_argument("--seed", + type=int, + help="Random seed", + default=int(time.time())) + parser_surface.add_argument("--generator", + help="Generation method", + choices=('random_phase', 'filter'), + default='random_phase') + parser_surface.add_argument("--output", "-o", + help="Output file name (compressed if .gz)") + parser_surface.set_defaults(func=surface) + + + # Arguments for contact command + parser_contact = subs.add_parser( + 'contact', + description="Compute the elastic contact solution with a given surface") + + parser_contact.add_argument("--input", "-i", + help="Rough surface file (default read from stdin)") + parser_contact.add_argument("--tol", + type=float, + default=1e-12, + help="Solver tolerance") + parser_contact.add_argument("load", + type=float, + help="Applied average pressure") + parser_contact.set_defaults(func=contact) + + + # Arguments for plot command + parser_plot = subs.add_parser( + 'plot', description='Plot contact solution') + parser_plot.set_defaults(func=plot) + + args = parser.parse_args() + + try: + args.func(args) + except AttributeError: + parser.print_usage() + +if __name__ == '__main__': + main()