From 15634c4c7f38e1627d7875ff3837ebc2bf32f265 Mon Sep 17 00:00:00 2001 From: Denis Davydov Date: Thu, 26 Jul 2018 10:33:56 +0200 Subject: [PATCH] add a FiniteSizeHistory class to store history of objects --- doc/news/changes/minor/20180726DenisDavydov | 3 + include/deal.II/numerics/history.h | 255 ++++++++++++++++++++ tests/numerics/history.cc | 93 +++++++ tests/numerics/history.output | 30 +++ 4 files changed, 381 insertions(+) create mode 100644 doc/news/changes/minor/20180726DenisDavydov create mode 100644 include/deal.II/numerics/history.h create mode 100644 tests/numerics/history.cc create mode 100644 tests/numerics/history.output diff --git a/doc/news/changes/minor/20180726DenisDavydov b/doc/news/changes/minor/20180726DenisDavydov new file mode 100644 index 0000000000..e279c877e5 --- /dev/null +++ b/doc/news/changes/minor/20180726DenisDavydov @@ -0,0 +1,3 @@ +New: Add FiniteSizeHistory class to store a finite-size history of objects. +
+(Denis Davydov 2018/07/26) diff --git a/include/deal.II/numerics/history.h b/include/deal.II/numerics/history.h new file mode 100644 index 0000000000..d688bd6a42 --- /dev/null +++ b/include/deal.II/numerics/history.h @@ -0,0 +1,255 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE.md at +// the top level directory of deal.II. +// +// --------------------------------------------------------------------- + +#ifndef dealii_storage_h +#define dealii_storage_h + +#include + +#include +#include + +#include +#include + +DEAL_II_NAMESPACE_OPEN + +/** + * A helper class to store a finite-size collection of objects of type `T`. + * If the number of elements exceeds the specified maximum size of the + * container, the oldest element is removed. Additionally, random access and + * removal of elements is implemented. Indexing is done relative to the last + * added element. + * + * In order to optimize the container for usage with + * memory-demanding objects (i.e. linear algebra vectors), the removal of an + * element does not free the memory. Instead the element + * is being kept in a separate cache so that subsequent addition does not + * require re-allocation of memory. + * + * The primary usage of this class is in solvers to store a history of vectors. + * That is, if at the iteration $k$ we store $m$ vectors from previous + * iterations + * $\{k-1,k-2,...,k-m\}$, then addition of the new element will make + * the object contain elements from iterations $\{k,k-1,k-2,...,k-m+1\}$. + * + * @author Denis Davydov, 2018 + */ +template +class FiniteSizeHistory +{ +public: + static_assert( + std::is_default_constructible::value, + "This class requires that the elements of type T are default constructible."); + + /** + * Constructor. + * + * @parameter max_elements maximum number of elements to be stored in the + * history. + */ + FiniteSizeHistory(const std::size_t max_elements = 0); + + /** + * Add new object by copying. + * If the maximum number of elements is reached, the oldest element is + * removed. + */ + void + add(const T &element); + + /** + * Remove an element with index @p index, + * counting from the last added element. + * `index==0` therefore corresponds to removing + * the newset element. + */ + void + remove(const std::size_t index); + + /** + * Read/write access to an element with index @p index, + * counting from the last added element. + * `index==0` therefore corresponds to the newset element. + */ + T &operator[](const std::size_t index); + + /** + * Read access to an element with index @p index, + * counting from the last added element. + * `index==0` therefore corresponds to the newset element. + */ + const T &operator[](const std::size_t index) const; + + /** + * Return the current size of the history. + */ + std::size_t + size() const; + + /** + * Return the maximum allowed size of the history. + */ + std::size_t + max_size() const; + + /** + * Clear the contents, including the cache. + */ + void + clear(); + +private: + /** + * Maximum number of elements to be stored. + */ + std::size_t max_n_elements; + + /** + * A deque which stores the data. + */ + std::deque> data; + + /** + * A deque to cache data, in particular for the case when + * removal is followed by addition. + */ + std::deque> cache; +}; + + + +// ------------------- inline and template functions ---------------- +#ifndef DOXYGEN + + + +template +FiniteSizeHistory::FiniteSizeHistory(const std::size_t max_elements) + : max_n_elements(max_elements) +{} + + + +template +void +FiniteSizeHistory::remove(const std::size_t ind) +{ + Assert(ind < data.size(), ExcIndexRange(ind, 0, data.size())); + auto el = std::move(data[ind]); + data.erase(data.begin() + ind); + + cache.push_back(std::move(el)); + + // whatever we do, we shall not store more than the maximum number of + // elements + Assert(data.size() + cache.size() <= max_n_elements, ExcInternalError()); +} + + + +template +void +FiniteSizeHistory::add(const T &element) +{ + std::unique_ptr new_el; + if (data.size() < max_n_elements) + // have not reached the maximum number of elements yet + { + if (cache.size() == 0) + // nothing is cached, just copy a given element + { + new_el = std_cxx14::make_unique(element); + } + else + // something is cached, take one element and copy + // the user provided one there. + { + new_el = std::move(cache.back()); + (*new_el) = element; + + cache.pop_back(); // removes a pointer that is now a nullptr anyway + } + } + else + // we reached the maximum number of elements and + // thus have to re-order/cycle elements currently stored + { + new_el = std::move(data.back()); + (*new_el) = element; + + data.pop_back(); // removes a pointer that is now a nullptr anyway + } + + // finally insert the new one where appropriate + data.push_front(std::move(new_el)); + + // whatever we do, we shall not store more than the maximum number of + // elements + Assert(data.size() + cache.size() <= max_n_elements, ExcInternalError()); +} + + + +template +T &FiniteSizeHistory::operator[](const std::size_t ind) +{ + Assert(ind < data.size(), ExcIndexRange(ind, 0, data.size())); + return *data[ind]; +} + + + +template +const T &FiniteSizeHistory::operator[](const std::size_t ind) const +{ + Assert(ind < data.size(), ExcIndexRange(ind, 0, data.size())); + return *data[ind]; +} + + + +template +std::size_t +FiniteSizeHistory::size() const +{ + return data.size(); +} + + + +template +std::size_t +FiniteSizeHistory::max_size() const +{ + return max_n_elements; +} + + + +template +void +FiniteSizeHistory::clear() +{ + data.clear(); + cache.clear(); +} + +#endif // Doxygen + +DEAL_II_NAMESPACE_CLOSE + +#endif // dealii_storage_h diff --git a/tests/numerics/history.cc b/tests/numerics/history.cc new file mode 100644 index 0000000000..d97019f3d5 --- /dev/null +++ b/tests/numerics/history.cc @@ -0,0 +1,93 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2018 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE.md at +// the top level directory of deal.II. +// +// --------------------------------------------------------------------- + +// check FiniteSizeHistory class + + +#include + +#include "../tests.h" + +void +test() +{ + const unsigned int max_size = 3; + FiniteSizeHistory storage(max_size); + deallog << "size: " << storage.size() << std::endl + << "max_size: " << storage.max_size() << std::endl; + + for (unsigned int i = 0; i < 2; i++) + storage.add(0.1 * (i + 1)); + + // 2 elemens + deallog << "initial:" << std::endl; + for (unsigned int i = 0; i < storage.size(); i++) + deallog << storage[i] << std::endl; + + // 3 elements + deallog << "append:" << std::endl; + storage.add(0.555); + for (unsigned int i = 0; i < storage.size(); i++) + deallog << storage[i] << std::endl; + + // 2 elements: + deallog << "remove second element:" << std::endl; + storage.remove(1); + for (unsigned int i = 0; i < storage.size(); i++) + deallog << storage[i] << std::endl; + + // 2 elements: + deallog << "change 0th:" << std::endl; + storage[0] = 0.33; + for (unsigned int i = 0; i < storage.size(); i++) + deallog << storage[i] << std::endl; + + // 3 elements: + deallog << "append:" << std::endl; + storage.add(0.666); + for (unsigned int i = 0; i < storage.size(); i++) + deallog << storage[i] << std::endl; + + // 3 elements: + deallog << "change 0th:" << std::endl; + storage[0] = 0.22; + for (unsigned int i = 0; i < storage.size(); i++) + deallog << storage[i] << std::endl; + + // 3 elements: + deallog << "append:" << std::endl; + storage.add(0.777); + for (unsigned int i = 0; i < storage.size(); i++) + deallog << storage[i] << std::endl; + + // 2 elements: + deallog << "remove last:" << std::endl; + storage.remove(storage.size() - 1); + for (unsigned int i = 0; i < storage.size(); i++) + deallog << storage[i] << std::endl; + + storage.clear(); +} + + +int +main(int argc, char **argv) +{ + std::ofstream logfile("output"); + dealii::deallog.attach(logfile, /*do not print job id*/ false); + dealii::deallog.depth_console(0); + + test(); +} diff --git a/tests/numerics/history.output b/tests/numerics/history.output new file mode 100644 index 0000000000..6ec4e28ab2 --- /dev/null +++ b/tests/numerics/history.output @@ -0,0 +1,30 @@ +DEAL::size: 0 +DEAL::max_size: 3 +DEAL::initial: +DEAL::0.200000 +DEAL::0.100000 +DEAL::append: +DEAL::0.555000 +DEAL::0.200000 +DEAL::0.100000 +DEAL::remove second element: +DEAL::0.555000 +DEAL::0.100000 +DEAL::change 0th: +DEAL::0.330000 +DEAL::0.100000 +DEAL::append: +DEAL::0.666000 +DEAL::0.330000 +DEAL::0.100000 +DEAL::change 0th: +DEAL::0.220000 +DEAL::0.330000 +DEAL::0.100000 +DEAL::append: +DEAL::0.777000 +DEAL::0.220000 +DEAL::0.330000 +DEAL::remove last: +DEAL::0.777000 +DEAL::0.220000 -- 2.39.5