diff --git a/python/tamaas/nonlinear_solvers/__init__.py b/python/tamaas/nonlinear_solvers/__init__.py index b10ba88..d30b39a 100644 --- a/python/tamaas/nonlinear_solvers/__init__.py +++ b/python/tamaas/nonlinear_solvers/__init__.py @@ -1,167 +1,171 @@ # -*- mode:python; coding: utf-8 -*- # # Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # Copyright (©) 2020-2022 Lucas Frérot # # 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 . """Nonlinear solvers for plasticity problems. Solvers in this module use :py:mod:`scipy.optimize` to solve the implicit non-linear equation for plastic deformations with fixed contact pressures. """ from functools import wraps from scipy.optimize import newton_krylov, root from scipy.optimize.nonlin import NoConvergence from .. import EPSolver, Logger, LogLevel, mpi from .._tamaas import _tolerance_manager from .._tamaas import _DFSANESolver as DFSANECXXSolver __all__ = ['NLNoConvergence', 'DFSANESolver', 'DFSANECXXSolver', 'NewtonKrylovSolver', 'ToleranceManager'] class NLNoConvergence(Exception): """Convergence not reached exception.""" class ScipySolver(EPSolver): """Base class for solvers wrapping SciPy routines.""" def __init__(self, residual, model=None, callback=None): """Construct nonlinear solver with residual. :param residual: plasticity residual object :param model: Deprecated :param callback: passed on to the Scipy solver """ super(ScipySolver, self).__init__(residual) if mpi.size() > 1: raise RuntimeError("Scipy solvers cannot be used with MPI; " "DFSANECXXSolver can be used instead") self.callback = callback self._x = self.getStrainIncrement() self._residual = self.getResidual() self.options = {'ftol': 0, 'fatol': 1e-9} def solve(self): """Solve R(δε) = 0 using a Scipy function.""" # For initial guess, compute the strain due to boundary tractions # self._residual.computeResidual(self._x) # self._x[...] = self._residual.getVector() EPSolver.beforeSolve(self) # Scipy root callback def compute_residual(vec): self._residual.computeResidual(vec) return self._residual.getVector().copy() # Solve + Logger().get(LogLevel.debug) << \ + "Entering non-linear solve\n" self._x[...] = self._scipy_solve(compute_residual) + Logger().get(LogLevel.debug) << \ + "Non-linear solve returned" # Computing displacements self._residual.computeResidualDisplacement(self._x) def reset(self): """Set solution vector to zero.""" self._x[...] = 0 def _scipy_solve(self, compute_residual): """Actually call the scipy solver. :param compute_residual: function returning residual for given variable """ raise NotImplementedError() class NewtonKrylovSolver(ScipySolver): """Solve using a finite-difference Newton-Krylov method.""" def _scipy_solve(self, compute_residual): try: return newton_krylov(compute_residual, self._x, f_tol=self.tolerance, verbose=True, callback=self.callback) except NoConvergence: raise NLNoConvergence("Newton-Krylov did not converge") class DFSANESolver(ScipySolver): """Solve using a spectral residual jacobianless method. See :doi:`10.1090/S0025-5718-06-01840-0` for details on method and the relevant Scipy `documentation `_ for details on parameters. """ def _scipy_solve(self, compute_residual): solution = root(compute_residual, self._x, method='df-sane', options={'ftol': 0, 'fatol': self.tolerance}, callback=self.callback) Logger().get(LogLevel.info) << \ "DF-SANE/Scipy: {} ({} iterations, {})".format( solution.message, solution.nit, self.tolerance) if not solution.success: raise NLNoConvergence("DF-SANE/Scipy did not converge") return solution.x.copy() def ToleranceManager(start, end, rate): """Decorate solver to manage tolerance of non-linear solve step.""" def actual_decorator(cls): orig_init = cls.__init__ orig_solve = cls.solve orig_update_state = cls.updateState @wraps(cls.__init__) def __init__(obj, *args, **kwargs): orig_init(obj, *args, **kwargs) obj.setToleranceManager(_tolerance_manager(start, end, rate)) @wraps(cls.solve) def new_solve(obj, *args, **kwargs): ftol = obj.tolerance ftol *= rate obj.tolerance = max(ftol, end) return orig_solve(obj, *args, **kwargs) @wraps(cls.updateState) def updateState(obj, *args, **kwargs): obj.tolerance = start return orig_update_state(obj, *args, **kwargs) cls.__init__ = __init__ # cls.solve = new_solve # cls.updateState = updateState return cls return actual_decorator diff --git a/src/core/array.hh b/src/core/array.hh index 5bc3124..c145d01 100644 --- a/src/core/array.hh +++ b/src/core/array.hh @@ -1,168 +1,179 @@ /* * SPDX-License-Indentifier: AGPL-3.0-or-later * * Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * Copyright (©) 2020-2022 Lucas Frérot * * 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 ARRAY_HH #define ARRAY_HH /* -------------------------------------------------------------------------- */ #include "logger.hh" +#include "span.hh" #include "tamaas.hh" #include #include #include #include /* -------------------------------------------------------------------------- */ namespace tamaas { /// Generic storage class with wrapping capacities template struct Array final { /// Default Array() = default; + /// Empty array of given size - Array(UInt size) : Array() { this->resize(size); } + Array(UInt size) : Array() { resize(size); } + /// Copy constructor (deep) Array(const Array& v) : Array() { - this->resize(v._size); - thrust::copy_n(v._data, v._size, this->_data); + resize(v.size()); + thrust::copy(v.view_.begin(), v.view_.end(), view_.begin()); } + /// Move constructor (transfers data ownership) Array(Array&& v) noexcept : Array() { - _data = std::exchange(v._data, nullptr); - _size = std::exchange(v._size, 0); - _reserved = std::exchange(v._reserved, 0); - wrapped = std::exchange(v.wrapped, false); + view_ = std::exchange(v.view_, span{}); + reserved_ = std::exchange(v.reserved_, 0); + wrapped_ = std::exchange(v.wrapped_, false); } + /// Wrap array on data - Array(T* data, UInt size) : Array() { this->wrap(data, size); } + Array(T* data, UInt size) noexcept : Array() { wrap(data, size); } + + /// Wrap on span + Array(span view) noexcept : Array() { wrap(view); } + /// Destructor ~Array() { - if (not wrapped) - _alloc.deallocate(_data, _reserved); + if (not wrapped_) + alloc_.deallocate(view_); } /// Copy operator Array& operator=(const Array& v) { - this->wrapped = false; - if (v.size() != this->size()) - this->resize(v.size()); - thrust::copy_n(v._data, _size, _data); + wrapped_ = false; + resize(v.size()); + thrust::copy(v.view_.begin(), v.view_.end(), view_.begin()); return *this; } /// Move operator Array& operator=(Array&& v) noexcept { - if (this != &v) { - if (not wrapped) - _alloc.deallocate(_data, _reserved); - _data = std::exchange(v._data, nullptr); - _size = std::exchange(v._size, 0); - _reserved = std::exchange(v._reserved, 0); - wrapped = std::exchange(v.wrapped, false); - } + if (this == &v) + return *this; + + if (not wrapped_) + alloc_.deallocate(view_); + view_ = std::exchange(v.view_, span{}); + reserved_ = std::exchange(v.reserved_, 0); + wrapped_ = std::exchange(v.wrapped_, false); return *this; } - void wrap(const Array& other) { this->wrap(other._data, other._size); } + /// Wrap on view + Array& operator=(span v) noexcept { wrap(v); } - /// Wrap a memory pointer - void wrap(T* data, UInt size) { - _data = data; - _size = size; - wrapped = true; - _reserved = 0; + /// Wrap array + void wrap(const Array& other) noexcept { wrap(other.view_); } + + /// Wrap view + void wrap(span view) noexcept { + view_ = view; + wrapped_ = true; + reserved_ = 0; } + /// Wrap a memory pointer + void wrap(T* data, UInt size) noexcept { wrap(span{data, size}); } + /// Data pointer access (const) - const T* data() const { return _data; } + const T* data() const { return view_.data(); } + /// Data pointer access (non-const) - T* data() { return _data; } + T* data() { return view_.data(); } /// Resize array - void resize(UInt size, const T& value = T()) { - if (wrapped) + void resize(UInt new_size, const T& value = T()) { + if (wrapped_) TAMAAS_EXCEPTION("cannot resize wrapped array"); // Erase array - if (size == 0) { - _alloc.deallocate(_data, _reserved); - _data = nullptr; - _size = 0; - _reserved = 0; + if (new_size == 0) { + alloc_.deallocate(view_); + view_ = span{}; + reserved_ = 0; return; } // Do nothing - if (size == this->_size) + if (new_size == size()) return; // Allocate new data - _alloc.deallocate(_data, _reserved); + alloc_.deallocate(view_); - _data = _alloc.allocate(size); - _size = size; - _reserved = size; + view_ = alloc_.allocate(new_size); + reserved_ = new_size; - if (not wrapped) - thrust::fill(_data, _data + size, value); + if (not wrapped_) + thrust::fill(view_.begin(), view_.end(), value); } /// Reserve storage space void reserve(UInt size) { - if (_reserved >= size) + if (reserved_ >= size) return; - auto new_data = _alloc.allocate(size); - if (new_data != _data) { - thrust::copy_n(_data, _size, new_data); - _alloc.deallocate(_data, _reserved); - _data = new_data; - _reserved = size; + auto new_view = alloc_.allocate(size); + + if (new_view.data() != view_.data()) { + thrust::copy(view_.begin(), view_.end(), new_view.begin()); + + alloc_.deallocate(view_); + view_ = {new_view.data(), view_.size()}; + reserved_ = view_.size(); + } else { + reserved_ = size; } } /// Access operator - inline T& operator[](UInt i) { - TAMAAS_ASSERT(i < _size, "memory overflow"); - return _data[i]; - } + inline T& operator[](UInt i) { return view_[i]; } /// Access operator (const) - inline const T& operator[](UInt i) const { - TAMAAS_ASSERT(i < _size, "memory overflow"); - return _data[i]; - } + inline const T& operator[](UInt i) const { return view_[i]; } /// Get size of array - inline UInt size() const { return _size; } + inline UInt size() const { return view_.size(); } + + span view() const { return view_; } private: - T* _data = nullptr; - UInt _size = 0; - std::size_t _reserved = 0; - bool wrapped = false; - Allocator _alloc; + span view_; + typename span::size_type reserved_ = 0; + bool wrapped_ = false; + Allocator alloc_; }; } // namespace tamaas /* -------------------------------------------------------------------------- */ #endif /* ARRAY_HH */ diff --git a/src/core/cuda/unified_allocator.hh b/src/core/cuda/unified_allocator.hh index 7630e9d..97f4983 100644 --- a/src/core/cuda/unified_allocator.hh +++ b/src/core/cuda/unified_allocator.hh @@ -1,51 +1,52 @@ /* * SPDX-License-Indentifier: AGPL-3.0-or-later * * Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * Copyright (©) 2020-2022 Lucas Frérot * * 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 UNIFIED_ALLOCATOR_HH #define UNIFIED_ALLOCATOR_HH /* -------------------------------------------------------------------------- */ +#include "span.hh" #include #include /* -------------------------------------------------------------------------- */ namespace tamaas { /// Class allocating [unified /// memory](http://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__MEMORY.html#group__CUDART__MEMORY_1gd228014f19cc0975ebe3e0dd2af6dd1b) template struct UnifiedAllocator { /// Allocate memory - static T* allocate(std::size_t n) { + static span allocate(typename span::size_type n) noexcept { T* p = nullptr; cudaMallocManaged(&p, n * sizeof(T)); return p; } /// Free memory static void deallocate(T* p, __attribute__((unused)) std::size_t n) { cudaFree(p); } }; } // namespace tamaas #endif // UNIFIED_ALLOCATOR_HH diff --git a/src/core/fftw/fftw_allocator.hh b/src/core/fftw/fftw_allocator.hh index f0eab15..5d6f793 100644 --- a/src/core/fftw/fftw_allocator.hh +++ b/src/core/fftw/fftw_allocator.hh @@ -1,52 +1,52 @@ /* * SPDX-License-Indentifier: AGPL-3.0-or-later * * Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * Copyright (©) 2020-2022 Lucas Frérot * * 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 FFTW_ALLOCATOR_HH #define FFTW_ALLOCATOR_HH /* -------------------------------------------------------------------------- */ +#include "span.hh" #include #include /* -------------------------------------------------------------------------- */ namespace tamaas { /// Class allocating /// [SIMD](http://www.fftw.org/fftw3_doc/SIMD-alignment-and-fftw_005fmalloc.html#SIMD-alignment-and-fftw_005fmalloc) /// aligned memory template struct FFTWAllocator { /// Allocate memory - static T* allocate(std::size_t n) noexcept { + static span allocate(typename span::size_type n) noexcept { T* p = nullptr; - p = (T*)fftw_malloc(sizeof(T) * n); - return p; + p = static_cast(fftw_malloc(sizeof(T) * n)); + span view{p, n}; + return view; } /// Free memory - static void deallocate(T* p, __attribute__((unused)) std::size_t n) noexcept { - fftw_free(p); - } + static void deallocate(span view) noexcept { fftw_free(view.data()); } }; } // namespace tamaas #endif // FFTW_ALLOCATOR_HH diff --git a/src/core/grid.cpp b/src/core/grid.cpp index 55f8204..8086e65 100644 --- a/src/core/grid.cpp +++ b/src/core/grid.cpp @@ -1,122 +1,100 @@ /* * SPDX-License-Indentifier: AGPL-3.0-or-later * * Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * Copyright (©) 2020-2022 Lucas Frérot * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "grid.hh" #include "tamaas.hh" #include #include #include /* -------------------------------------------------------------------------- */ namespace tamaas { /* -------------------------------------------------------------------------- */ template Grid::Grid() : GridBase() { this->n.fill(0); this->strides.fill(1); this->nb_components = 1; } /* -------------------------------------------------------------------------- */ -template -Grid::Grid(const std::array& n, UInt nb_components, - value_type* data) - : GridBase() { - this->n = n; - this->nb_components = nb_components; - UInt size = this->computeSize(); - this->data.wrap(data, size); - this->computeStrides(); -} - -template -Grid::Grid(const Grid& o) - : GridBase(o), n(o.n), strides(o.strides) {} - -template -Grid::Grid(Grid&& o) noexcept - : GridBase(std::move(o)), n(std::move(o.n)), - strides(std::move(o.strides)) {} - -/* -------------------------------------------------------------------------- */ - template void Grid::resize(const std::array& n) { this->resize(n.begin(), n.end()); } /* -------------------------------------------------------------------------- */ template void Grid::resize(const std::vector& n) { TAMAAS_ASSERT(n.size() == dim, "Shape vector not matching grid dimensions"); this->resize(n.begin(), n.end()); } /* -------------------------------------------------------------------------- */ template void Grid::resize(std::initializer_list n) { TAMAAS_ASSERT(n.size() == dim, "Shape initializer list not matching grid dimensions"); this->resize(std::begin(n), std::end(n)); } /* -------------------------------------------------------------------------- */ template void Grid::computeStrides() { std::copy(n.begin() + 1, n.end(), strides.begin()); strides[dim] = 1; strides[dim - 1] = this->nb_components; std::partial_sum(strides.rbegin(), strides.rend(), strides.rbegin(), std::multiplies()); } /* -------------------------------------------------------------------------- */ template void Grid::printself(std::ostream& str) const { str << "Grid(" << dim << ", " << this->nb_components << ") {"; for (auto& val : *this) { str << val << ", "; } str << "\b\b}"; } /* -------------------------------------------------------------------------- */ /// Class instanciation #define GRID_INSTANCIATE_TYPE(type) \ template class Grid; \ template class Grid; \ template class Grid GRID_INSTANCIATE_TYPE(Real); GRID_INSTANCIATE_TYPE(UInt); GRID_INSTANCIATE_TYPE(Complex); GRID_INSTANCIATE_TYPE(Int); GRID_INSTANCIATE_TYPE(bool); #undef GRID_INSTANCIATE_TYPE } // namespace tamaas diff --git a/src/core/grid.hh b/src/core/grid.hh index 333fd01..4229715 100644 --- a/src/core/grid.hh +++ b/src/core/grid.hh @@ -1,233 +1,234 @@ /* * SPDX-License-Indentifier: AGPL-3.0-or-later * * Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * Copyright (©) 2020-2022 Lucas Frérot * * 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 GRID_HH #define GRID_HH /* -------------------------------------------------------------------------- */ #include "grid_base.hh" #include "tamaas.hh" #include #include #include #include /* -------------------------------------------------------------------------- */ namespace tamaas { /** * @brief Multi-dimensional & multi-component array class * * This class is a container for multi-component data stored on a multi- * dimensional grid. * * The access function is the parenthesis operator. For a grid of dimension d, * the operator takes d+1 arguments: the first d arguments are the position on * the grid and the last one is the component of the stored data. * * It is also possible to use the access operator with only one argument, it is * then considering the grid as a flat array, accessing the given cell of the * array. */ template class Grid : public GridBase { template using is_valid_index = fold_trait; public: /* ------------------------------------------------------------------------ */ /* Types */ /* ------------------------------------------------------------------------ */ using value_type = T; using reference = value_type&; static constexpr UInt dimension = dim; /* ------------------------------------------------------------------------ */ /* Constructors */ /* ------------------------------------------------------------------------ */ public: /// Constructor by default (empty array) Grid(); - /// Constructor from iterators + /// Constructor with shape from iterators template Grid(RandomAccessIterator begin, RandomAccessIterator end, UInt nb_components); + /// Construct with shape from iterators on data view template - Grid(RandomAccessIterator begin, RandomAccessIterator end, - UInt nb_components, value_type* data); + Grid(RandomAccessIterator begin, RandomAccessIterator end, UInt nb_components, + span data); /// Constructor Grid(const std::array& n, UInt nb_components) : Grid(std::begin(n), std::end(n), nb_components) {} /// Wrap on given data - Grid(const std::array& n, UInt nb_components, value_type* data); + Grid(const std::array& n, UInt nb_components, + span data) + : Grid(std::begin(n), std::end(n), nb_components, data) {} /// Constructor with vectors Grid(const std::vector& n, UInt nb_components) : Grid(std::begin(n), std::end(n), nb_components) {} /// Constructor with initializer list Grid(const std::initializer_list& n, UInt nb_components) : Grid(std::begin(n), std::end(n), nb_components) {} /// Copy constructor - Grid(const Grid& o); + Grid(const Grid& o) : GridBase(), n(o.n), strides(o.strides) {} /// Move constructor (transfers data ownership) - Grid(Grid&& o) noexcept; + Grid(Grid&& o) noexcept + : GridBase(std::move(o)), n(std::move(o.n)), + strides(std::move(o.strides)) {} /// Destructor ~Grid() override = default; private: /// Init from standard container template void init(const Container& n, UInt nb_components); public: /* ------------------------------------------------------------------------ */ /* Common operations */ /* ------------------------------------------------------------------------ */ /// Resize array void resize(const std::array& n); /// Resize array (from std::vector) void resize(const std::vector& n); /// Resize array (from initializer list) void resize(std::initializer_list n); /// Resize array (from iterators) template void resize(ForwardIt begin, ForwardIt end); /// Compute size inline UInt computeSize() const; /// Get grid dimension inline UInt getDimension() const override { return dim; } /// Compute strides void computeStrides(); /// Print virtual void printself(std::ostream& str) const; /// Get sizes const std::array& sizes() const { return n; } /// Get strides const std::array& getStrides() const { return this->strides; } /* ------------------------------------------------------------------------ */ /* Access operators (these cannot be moved outside the class) */ /* ------------------------------------------------------------------------ */ /// Variadic access operator (non-const) template inline std::enable_if_t::value, T&> operator()(T1... args) { /// Checking access integrity constexpr UInt nargs = sizeof...(T1); static_assert(nargs == dim + 1 || nargs == 1 || nargs == dim, "number of arguments in operator() does not match dimension"); constexpr UInt start = (nargs == 1) ? dim : 0; UInt offset = unpackOffset(0, start, args...); return this->data[offset]; } /// Variadic access operator template inline std::enable_if_t::value, const T&> operator()(T1... args) const { /// Checking access integrity constexpr UInt nargs = sizeof...(T1); static_assert(nargs == dim + 1 || nargs == 1 || nargs == dim, "number of arguments in operator() does not match dimension"); constexpr UInt start = (nargs == 1) ? dim : 0; UInt offset = unpackOffset(0, start, args...); return this->data[offset]; } /// Tuple index access operator template inline T& operator()(std::array tuple); template inline const T& operator()(std::array tuple) const; private: /// Unpacking the arguments of variadic () template inline UInt unpackOffset(UInt offset, UInt index_pos, UInt index, T1... rest) const; /// End case for recursion template inline UInt unpackOffset(UInt offset, UInt index_pos, UInt index) const; /// Computing offset for a tuple index template inline UInt computeOffset(std::array tuple) const; /* ------------------------------------------------------------------------ */ /* Move/Copy operators */ /* ------------------------------------------------------------------------ */ public: using GridBase::operator=; // = operator Grid& operator=(const Grid& other); // = operator (move) Grid& operator=(Grid&& other) noexcept; // Copy data from another grid template void copy(const Grid& other); // Move data from another grid template void move(Grid&& other) noexcept; template - void wrap(GridBase & other, Container&& n) { + void wrap(GridBase& other, Container&& n) { GridBase::wrap(other); std::copy(n.begin(), n.end(), this->n.begin()); this->computeStrides(); } template - void wrap(const GridBase & other, Container&& n) { + void wrap(const GridBase& other, Container&& n) { GridBase::wrap(other); std::copy(n.begin(), n.end(), this->n.begin()); this->computeStrides(); } // Wrap memory (non-const) - void wrap(Grid& other) { - wrap(other, other.n); - } + void wrap(Grid& other) { wrap(other, other.n); } // Wrap memory - void wrap(const Grid& other) { - wrap(other, other.n); - } + void wrap(const Grid& other) { wrap(other, other.n); } /* ------------------------------------------------------------------------ */ /* Member variables */ /* ------------------------------------------------------------------------ */ protected: std::array n; ///< shape of grid: size per dimension std::array strides; ///< strides for access }; } // namespace tamaas /* -------------------------------------------------------------------------- */ /* Inline/template function definitions */ /* -------------------------------------------------------------------------- */ #include "grid_tmpl.hh" /* -------------------------------------------------------------------------- */ #endif // GRID_HH diff --git a/src/core/grid_base.hh b/src/core/grid_base.hh index 5a7e2ac..fc96e29 100644 --- a/src/core/grid_base.hh +++ b/src/core/grid_base.hh @@ -1,342 +1,344 @@ /* * SPDX-License-Indentifier: AGPL-3.0-or-later * * Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * Copyright (©) 2020-2022 Lucas Frérot * * 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 GRID_BASE_HH #define GRID_BASE_HH /* -------------------------------------------------------------------------- */ #include "array.hh" #include "iterator.hh" #include "loop.hh" #include "mpi_interface.hh" #include "static_types.hh" #include "tamaas.hh" #include #include #include #include /* -------------------------------------------------------------------------- */ namespace tamaas { /** * @brief Dimension agnostic grid with components stored per points */ template class GridBase { public: /// Constructor by default GridBase() = default; /// Copy constructor GridBase(const GridBase& o) { this->copy(o); } /// Move constructor (transfers data ownership) GridBase(GridBase&& o) noexcept : data(std::move(o.data)), nb_components(std::exchange(o.nb_components, 1)) {} /// Initialize with size explicit GridBase(UInt nb_points, UInt nb_components = 1) { setNbComponents(nb_components); this->resize(nb_points * nb_components); } /// Destructor virtual ~GridBase() = default; /* ------------------------------------------------------------------------ */ /* Iterator class */ /* ------------------------------------------------------------------------ */ using iterator = iterator_::iterator; using const_iterator = iterator_::iterator; using value_type = T; using reference = value_type&; /// Get grid dimension virtual UInt getDimension() const { return 1; } /// Get internal data pointer (const) const T* getInternalData() const { return this->data.data(); } /// Get internal data pointer (non-const) T* getInternalData() { return this->data.data(); } /// Get number of components UInt getNbComponents() const { return nb_components; } /// Set number of components void setNbComponents(UInt n) { nb_components = n; } /// Get total size virtual UInt dataSize() const { return this->data.size(); } /// Get global data size UInt globalDataSize() const { return mpi::allreduce(dataSize()); } /// Get number of points UInt getNbPoints() const { return this->dataSize() / this->getNbComponents(); } /// Get global number of points UInt getGlobalNbPoints() const { return this->globalDataSize() / this->getNbComponents(); } /// Set components void uniformSetComponents(const GridBase& vec); /// Resize void resize(UInt size) { this->data.resize(size); } /// Reserve storage w/o changing data logic void reserve(UInt size) { this->data.reserve(size); } /* ------------------------------------------------------------------------ */ /* Iterator methods */ /* ------------------------------------------------------------------------ */ virtual iterator begin(UInt n = 1) { return iterator(this->getInternalData(), n); } virtual iterator end(UInt n = 1) { return iterator(this->getInternalData() + this->dataSize(), n); } virtual const_iterator begin(UInt n = 1) const { return const_iterator(this->getInternalData(), n); } virtual const_iterator end(UInt n = 1) const { return const_iterator(this->getInternalData() + this->dataSize(), n); } + decltype(auto) view() const { return data.view(); } + /* ------------------------------------------------------------------------ */ /* Operators */ /* ------------------------------------------------------------------------ */ inline T& operator()(UInt i) { return this->data[i]; } inline const T& operator()(UInt i) const { return this->data[i]; } #define VEC_OPERATOR(op) \ template \ GridBase& operator op(const GridBase& other) VEC_OPERATOR(+=); VEC_OPERATOR(*=); VEC_OPERATOR(-=); VEC_OPERATOR(/=); #undef VEC_OPERATOR template T dot(const GridBase& other) const; #define SCALAR_OPERATOR(op) GridBase& operator op(const T& e) SCALAR_OPERATOR(+=); SCALAR_OPERATOR(*=); SCALAR_OPERATOR(-=); SCALAR_OPERATOR(/=); SCALAR_OPERATOR(=); #undef SCALAR_OPERATOR /// Broadcast operators #define BROADCAST_OPERATOR(op) \ template \ GridBase& operator op(const StaticArray& b) BROADCAST_OPERATOR(+=); BROADCAST_OPERATOR(-=); BROADCAST_OPERATOR(*=); BROADCAST_OPERATOR(/=); BROADCAST_OPERATOR(=); #undef BROADCAST_OPERATOR /* ------------------------------------------------------------------------ */ /* Min/max */ /* ------------------------------------------------------------------------ */ T min() const; T max() const; T sum() const; T mean() const { return sum() / static_cast(globalDataSize()); } T var() const; T l2norm() const { return std::sqrt(dot(*this)); } /* ------------------------------------------------------------------------ */ /* Move/Copy */ /* ------------------------------------------------------------------------ */ GridBase& operator=(const GridBase& o) { this->copy(o); return *this; } GridBase& operator=(GridBase& o) { this->copy(o); return *this; } GridBase& operator=(GridBase&& o) noexcept { this->move(std::move(o)); return *this; } template void copy(const GridBase& other) { if (other.dataSize() != this->dataSize()) this->resize(other.dataSize()); thrust::copy(other.begin(), other.end(), this->begin()); nb_components = other.nb_components; } template void move(GridBase&& other) noexcept { data = std::move(other.data); nb_components = std::exchange(other.nb_components, 1); } GridBase& wrap(GridBase& o) { data.wrap(o.data); nb_components = o.nb_components; return *this; } GridBase& wrap(const GridBase& o) { data.wrap(o.data); nb_components = o.nb_components; return *this; } protected: Array data; UInt nb_components = 1; }; /* -------------------------------------------------------------------------- */ template void GridBase::uniformSetComponents(const GridBase& vec) { if (vec.dataSize() != this->nb_components) TAMAAS_EXCEPTION("Cannot set grid field with values of vector"); auto begin_it{begin()}; auto end_it{end()}; // silencing nvcc warnings for (auto it = begin(); it < end_it; ++it) { UInt i = it - begin_it; *it = vec(i % this->nb_components); } } /* -------------------------------------------------------------------------- */ #define SCALAR_OPERATOR_IMPL(op) \ template \ inline GridBase& GridBase::operator op(const T& e) { \ Loop::loop([e] CUDA_LAMBDA(T& val) { val op e; }, *this); \ return *this; \ } SCALAR_OPERATOR_IMPL(+=) SCALAR_OPERATOR_IMPL(*=) SCALAR_OPERATOR_IMPL(-=) SCALAR_OPERATOR_IMPL(/=) SCALAR_OPERATOR_IMPL(=) #undef SCALAR_OPERATOR_IMPL /* -------------------------------------------------------------------------- */ #define VEC_OPERATOR_IMPL(op) \ template \ template \ inline GridBase& GridBase::operator op(const GridBase& other) { \ TAMAAS_ASSERT(other.dataSize() == this->dataSize(), \ "surface size does not match"); \ Loop::loop([] CUDA_LAMBDA(T& x, const T1& y) { x op y; }, *this, other); \ return *this; \ } VEC_OPERATOR_IMPL(+=) VEC_OPERATOR_IMPL(-=) VEC_OPERATOR_IMPL(*=) VEC_OPERATOR_IMPL(/=) #undef VEC_OPERATOR /* -------------------------------------------------------------------------- */ #define BROADCAST_OPERATOR_IMPL(op) \ template \ template \ GridBase& GridBase::operator op(const StaticArray& b) { \ TAMAAS_ASSERT(this->dataSize() % size == 0, \ "Broadcast operator cannot broadcast" \ << size << " on array of size " << this->dataSize()); \ Tensor::type, size> a(b); \ Loop::loop([a] CUDA_LAMBDA(VectorProxy g) { g op a; }, \ range>(*this)); \ return *this; \ } BROADCAST_OPERATOR_IMPL(+=) BROADCAST_OPERATOR_IMPL(-=) BROADCAST_OPERATOR_IMPL(*=) BROADCAST_OPERATOR_IMPL(/=) BROADCAST_OPERATOR_IMPL(=) #undef BROADCAST_OPERATOR_IMPL /* -------------------------------------------------------------------------- */ template inline T GridBase::min() const { // const auto id = [] CUDA_LAMBDA (const T&x){return x;}; return Loop::reduce([] CUDA_LAMBDA(const T& x) { return x; }, *this); } /* -------------------------------------------------------------------------- */ template inline T GridBase::max() const { return Loop::reduce([] CUDA_LAMBDA(const T& x) { return x; }, *this); } /* -------------------------------------------------------------------------- */ template inline T GridBase::sum() const { return Loop::reduce([] CUDA_LAMBDA(const T& x) { return x; }, *this); } /* -------------------------------------------------------------------------- */ template inline T GridBase::var() const { const T mu = mean(); const T var = Loop::reduce( [mu] CUDA_LAMBDA(const T& x) { return (x - mu) * (x - mu); }, *this); return var / (globalDataSize() - 1); } /* -------------------------------------------------------------------------- */ template template T GridBase::dot(const GridBase& other) const { return Loop::reduce( [] CUDA_LAMBDA(const T& x, const T1& y) { return x * y; }, *this, other); } } // namespace tamaas #endif // GRID_BASE_HH diff --git a/src/core/grid_tmpl.hh b/src/core/grid_tmpl.hh index 69aee35..499a661 100644 --- a/src/core/grid_tmpl.hh +++ b/src/core/grid_tmpl.hh @@ -1,171 +1,176 @@ /* * SPDX-License-Indentifier: AGPL-3.0-or-later * * Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * Copyright (©) 2020-2022 Lucas Frérot * * 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 GRID_TMPL_HH #define GRID_TMPL_HH /* -------------------------------------------------------------------------- */ #include "grid.hh" #include "tamaas.hh" namespace tamaas { template template Grid::Grid(RandomAccessIterator begin, RandomAccessIterator end, UInt nb_components) : GridBase() { if (end - begin != dim) { TAMAAS_EXCEPTION("Provided sizes (" << end - begin << ") for grid do not match dimension (" << dim << ")"); } this->nb_components = nb_components; this->resize(begin, end); } /* -------------------------------------------------------------------------- */ template template Grid::Grid(RandomAccessIterator begin, RandomAccessIterator end, - UInt nb_components, value_type* data) + UInt nb_components, span data) : GridBase() { if (end - begin != dim) { TAMAAS_EXCEPTION("Provided sizes (" << end - begin << ") for grid do not match dimension (" << dim << ")"); } std::copy(begin, end, this->n.begin()); this->nb_components = nb_components; - this->data.wrap(data, this->computeSize()); + + if (data.size() != computeSize()) + TAMAAS_EXCEPTION("incompatible wrap span size"); + + this->data.wrap(data); + this->computeStrides(); } /* -------------------------------------------------------------------------- */ template inline UInt Grid::computeSize() const { UInt size = 1; for (UInt i = 0; i < dim; i++) size *= n[i]; size *= this->nb_components; return size; } /* -------------------------------------------------------------------------- */ template template void Grid::resize(ForwardIt begin, ForwardIt end) { std::copy(begin, end, this->n.begin()); UInt size = this->computeSize(); GridBase::resize(size); this->computeStrides(); } /* -------------------------------------------------------------------------- */ template template inline UInt Grid::unpackOffset(UInt offset, UInt index_pos, UInt index, T1... rest) const { offset += index * strides[index_pos]; return unpackOffset(offset, index_pos + 1, rest...); // tail-rec bb } template template inline UInt Grid::unpackOffset(UInt offset, UInt index_pos, UInt index) const { return offset + index * strides[index_pos]; } /* -------------------------------------------------------------------------- */ template template inline UInt Grid::computeOffset(std::array tuple) const { static_assert(tdim == dim or tdim == dim + 1, "Tuple dimension is invalid"); return std::inner_product(tuple.begin(), tuple.end(), strides.begin(), 0); } template template inline T& Grid::operator()(std::array tuple) { UInt offset = computeOffset(tuple); return this->data[offset]; } template template inline const T& Grid::operator()(std::array tuple) const { UInt offset = computeOffset(tuple); return this->data[offset]; } /* -------------------------------------------------------------------------- */ template Grid& Grid::operator=(const Grid& other) { this->copy(other); return *this; } template Grid& Grid::operator=(Grid&& other) noexcept { this->move(std::move(other)); return *this; } /* -------------------------------------------------------------------------- */ template template void Grid::copy(const Grid& other) { GridBase::copy(other); this->n = other.n; this->strides = other.strides; } template template void Grid::move(Grid&& other) noexcept { GridBase::move(std::move(other)); this->n = std::move(other.n); this->strides = std::move(other.strides); } /* -------------------------------------------------------------------------- */ /* Stream operator */ /* -------------------------------------------------------------------------- */ template inline std::ostream& operator<<(std::ostream& stream, const Grid& _this) { _this.printself(stream); return stream; } } // namespace tamaas /* -------------------------------------------------------------------------- */ #endif // GRID_TMPL_HH diff --git a/src/core/cuda/unified_allocator.hh b/src/core/span.hh similarity index 50% copy from src/core/cuda/unified_allocator.hh copy to src/core/span.hh index 7630e9d..a4622de 100644 --- a/src/core/cuda/unified_allocator.hh +++ b/src/core/span.hh @@ -1,51 +1,71 @@ /* * SPDX-License-Indentifier: AGPL-3.0-or-later * * Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * Copyright (©) 2020-2022 Lucas Frérot * * 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 UNIFIED_ALLOCATOR_HH -#define UNIFIED_ALLOCATOR_HH +#ifndef SPAN_HH +#define SPAN_HH /* -------------------------------------------------------------------------- */ -#include -#include +#include "tamaas.hh" +#include +#include +#include /* -------------------------------------------------------------------------- */ - namespace tamaas { -/// Class allocating [unified -/// memory](http://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__MEMORY.html#group__CUDART__MEMORY_1gd228014f19cc0975ebe3e0dd2af6dd1b) template -struct UnifiedAllocator { - /// Allocate memory - static T* allocate(std::size_t n) { - T* p = nullptr; - cudaMallocManaged(&p, n * sizeof(T)); - return p; +struct span { + using element_type = T; + using value_type = std::remove_cv_t; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using iterator = T*; + using reverse_iterator = std::reverse_iterator; + + constexpr size_type size() const noexcept { return size_; } + constexpr pointer data() const noexcept { return data_; } + + constexpr iterator begin() const noexcept { return data_; } + constexpr iterator end() const noexcept { return data_ + size_; } + constexpr iterator begin() noexcept { return data_; } + constexpr iterator end() noexcept { return data_ + size_; } + + reference operator[](size_type idx) { + TAMAAS_ASSERT((idx < size_), "index out of span range"); + return data_[idx]; } - /// Free memory - static void deallocate(T* p, __attribute__((unused)) std::size_t n) { - cudaFree(p); + const_reference operator[](size_type idx) const { + TAMAAS_ASSERT((idx < size_), "index out of span range"); + return data_[idx]; } + + pointer data_ = nullptr; + size_type size_ = 0; }; } // namespace tamaas -#endif // UNIFIED_ALLOCATOR_HH +/* -------------------------------------------------------------------------- */ +#endif diff --git a/src/core/tamaas.hh b/src/core/tamaas.hh index 1f15e61..836c0bf 100644 --- a/src/core/tamaas.hh +++ b/src/core/tamaas.hh @@ -1,199 +1,200 @@ /** * @mainpage Tamaas - A high-performance periodic contact library * * @section Introduction * Tamaas is a spectral-integral-equation based contact library. It is made * with love to be fast and friendly! * * @author Lucas Frérot * @author Guillaume Anciaux * @author Valentine Rey * @author Son Pham-Ba * @author Jean-François Molinari * * @section License * * Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * Copyright (©) 2020-2022 Lucas Frérot * * 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 TAMAAS_HH #define TAMAAS_HH /* -------------------------------------------------------------------------- */ #ifndef TAMAAS_USE_CUDA #define TAMAAS_USE_FFTW #endif // Values for fftw backends #define TAMAAS_FFTW_BACKEND_OMP 2 #define TAMAAS_FFTW_BACKEND_THREADS 2 #define TAMAAS_FFTW_BACKEND_NONE 3 // Values for thrust backends #define TAMAAS_LOOP_BACKEND_OMP 1 #define TAMAAS_LOOP_BACKEND_TBB 2 #define TAMAAS_LOOP_BACKEND_CPP 3 #define TAMAAS_LOOP_BACKEND_CUDA 4 // Default loop backend is OpenMP #ifndef TAMAAS_LOOP_BACKEND #define TAMAAS_LOOP_BACKEND TAMAAS_LOOP_BACKEND_OMP #endif // Default FFTW backend is none #ifndef TAMAAS_FFTW_BACKEND #define TAMAAS_FFTW_BACKEND TAMAAS_FFTW_BACKEND_NONE #endif // If the thrust device hasn't been set, set OpenMP #ifndef THRUST_DEVICE_SYSTEM #define THRUST_DEVICE_SYSTEM THRUST_DEVICE_SYSTEM_OMP #endif + +/// Convenience macros +#define TAMAAS_DEBUG_MSG(mesg) \ + __FILE__ << ':' << __LINE__ << ": " << mesg << '\n' +#define TAMAAS_EXCEPTION(mesg) \ + { \ + std::stringstream sstr; \ + sstr << TAMAAS_DEBUG_MSG("FATAL: " << mesg); \ + throw ::tamaas::Exception(sstr.str()); \ + } + +#define SURFACE_FATAL(mesg) TAMAAS_EXCEPTION(mesg) + +#if defined(TAMAAS_DEBUG) +#define TAMAAS_ASSERT(cond, reason) \ + do { \ + if (not(cond)) { \ + TAMAAS_EXCEPTION(#cond " assert failed: " << reason); \ + } \ + } while (0) +#define TAMAAS_DEBUG_EXCEPTION(reason) TAMAAS_EXCEPTION(reason) +#else +#define TAMAAS_ASSERT(cond, reason) +#define TAMAAS_DEBUG_EXCEPTION(reason) +#endif + +#define TAMAAS_ACCESSOR(var, type, name) \ + type& get##name() { return var; } \ + void set##name(const type& new_var) { var = new_var; } + /* -------------------------------------------------------------------------- */ // Standard includes #include #include #include #include #include /* -------------------------------------------------------------------------- */ // Special thrust includes #include #include #ifdef TAMAAS_USE_CUDA #include "cuda/unified_allocator.hh" #else #include "fftw/fftw_allocator.hh" #endif /* -------------------------------------------------------------------------- */ namespace tamaas { /* -------------------------------------------------------------------------- */ /// Cuda specific definitions #define CUDA_LAMBDA __device__ __host__ #ifdef TAMAAS_USE_CUDA template using Allocator = UnifiedAllocator; #else template using Allocator = FFTWAllocator; #endif /// Common types definitions // If type macros have not been set, put default values #ifndef TAMAAS_REAL_TYPE #define TAMAAS_REAL_TYPE double #endif #ifndef TAMAAS_INT_TYPE #define TAMAAS_INT_TYPE int #endif using Real = TAMAAS_REAL_TYPE; ///< default floating point type using Int = TAMAAS_INT_TYPE; ///< default signed integer type using UInt = std::make_unsigned_t; ///< default unsigned integer type template using complex = thrust::complex; ///< template complex wrapper using Complex = complex; ///< default floating point complex type /// Defining random toolbox using ::thrust::random::normal_distribution; using ::thrust::random::uniform_real_distribution; using random_engine = ::thrust::random::default_random_engine; namespace detail { template class Trait, typename Head, typename... Tail> struct fold_trait_tail_rec : std::integral_constant::value, Trait, Tail...>::value> {}; template class Trait, typename Head> struct fold_trait_tail_rec : std::integral_constant::value> {}; } // namespace detail template