]> https://gitweb.dealii.org/ - dealii.git/commitdiff
add a FiniteSizeHistory class to store history of objects 6984/head
authorDenis Davydov <davydden@gmail.com>
Thu, 26 Jul 2018 08:33:56 +0000 (10:33 +0200)
committerDenis Davydov <davydden@gmail.com>
Thu, 2 Aug 2018 14:05:46 +0000 (16:05 +0200)
doc/news/changes/minor/20180726DenisDavydov [new file with mode: 0644]
include/deal.II/numerics/history.h [new file with mode: 0644]
tests/numerics/history.cc [new file with mode: 0644]
tests/numerics/history.output [new file with mode: 0644]

diff --git a/doc/news/changes/minor/20180726DenisDavydov b/doc/news/changes/minor/20180726DenisDavydov
new file mode 100644 (file)
index 0000000..e279c87
--- /dev/null
@@ -0,0 +1,3 @@
+New: Add FiniteSizeHistory class to store a finite-size history of objects.
+<br>
+(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 (file)
index 0000000..d688bd6
--- /dev/null
@@ -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 <deal.II/base/config.h>
+
+#include <deal.II/base/exceptions.h>
+#include <deal.II/base/std_cxx14/memory.h>
+
+#include <deque>
+#include <type_traits>
+
+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 <typename T>
+class FiniteSizeHistory
+{
+public:
+  static_assert(
+    std::is_default_constructible<T>::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<std::unique_ptr<T>> data;
+
+  /**
+   * A deque to cache data, in particular for the case when
+   * removal is followed by addition.
+   */
+  std::deque<std::unique_ptr<T>> cache;
+};
+
+
+
+// -------------------  inline and template functions ----------------
+#ifndef DOXYGEN
+
+
+
+template <typename T>
+FiniteSizeHistory<T>::FiniteSizeHistory(const std::size_t max_elements)
+  : max_n_elements(max_elements)
+{}
+
+
+
+template <typename T>
+void
+FiniteSizeHistory<T>::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 <typename T>
+void
+FiniteSizeHistory<T>::add(const T &element)
+{
+  std::unique_ptr<T> 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<T>(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 <typename T>
+T &FiniteSizeHistory<T>::operator[](const std::size_t ind)
+{
+  Assert(ind < data.size(), ExcIndexRange(ind, 0, data.size()));
+  return *data[ind];
+}
+
+
+
+template <typename T>
+const T &FiniteSizeHistory<T>::operator[](const std::size_t ind) const
+{
+  Assert(ind < data.size(), ExcIndexRange(ind, 0, data.size()));
+  return *data[ind];
+}
+
+
+
+template <typename T>
+std::size_t
+FiniteSizeHistory<T>::size() const
+{
+  return data.size();
+}
+
+
+
+template <typename T>
+std::size_t
+FiniteSizeHistory<T>::max_size() const
+{
+  return max_n_elements;
+}
+
+
+
+template <typename T>
+void
+FiniteSizeHistory<T>::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 (file)
index 0000000..d97019f
--- /dev/null
@@ -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 <deal.II/numerics/history.h>
+
+#include "../tests.h"
+
+void
+test()
+{
+  const unsigned int        max_size = 3;
+  FiniteSizeHistory<double> 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 (file)
index 0000000..6ec4e28
--- /dev/null
@@ -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

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.