https://mooseframework.inl.gov
ValueCache.h
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #pragma once
11 
12 #include "MooseTypes.h"
13 #include "MooseUtils.h"
14 #include "DataIO.h"
15 #include "libmesh/int_range.h"
16 #include "libmesh/nanoflann.hpp"
17 #include <cstdlib>
18 #include <fstream>
19 #include <iostream>
20 #include <memory>
21 #include <vector>
22 #include <tuple>
23 #include <optional>
24 #include <exception>
25 
26 template <typename T>
27 class ValueCache;
28 template <typename T>
29 void dataStore(std::ostream & stream, ValueCache<T> & c, void * context);
30 template <typename T>
31 void dataLoad(std::istream & stream, ValueCache<T> & c, void * context);
32 
41 template <typename T>
42 class ValueCache
43 {
44 public:
46  ValueCache(std::size_t in_dim, std::size_t max_leaf_size = 10);
47 
49  ValueCache(const std::string & file_name, std::size_t in_dim, std::size_t max_leaf_size = 10);
50 
52  ~ValueCache();
53 
55  void insert(const std::vector<Real> & in_val, const T & out_val);
56 
58  std::tuple<const std::vector<Real> &, const T &, Real>
59  getNeighbor(const std::vector<Real> & in_val);
60 
62  std::vector<std::tuple<const std::vector<Real> &, const T &, Real>>
63  getNeighbors(const std::vector<Real> & in_val, const std::size_t k);
64 
66  std::size_t size();
67 
69  void clear();
70 
72  std::size_t kdtree_get_point_count() const;
73  Real kdtree_get_pt(const std::size_t idx, const std::size_t dim) const;
74  template <class BBOX>
75  bool kdtree_get_bbox(BBOX & bb) const;
77 
78 protected:
80  void rebuildTree();
81 
82  using KdTreeT =
83  nanoflann::KDTreeSingleIndexDynamicAdaptor<nanoflann::L2_Simple_Adaptor<Real, ValueCache<T>>,
85  -1,
86  std::size_t>;
87 
88  std::vector<std::pair<std::vector<Real>, T>> _location_data;
89  std::unique_ptr<KdTreeT> _kd_tree;
90 
91  const std::size_t _in_dim;
92  const std::size_t _max_leaf_size;
93  const std::size_t _max_subtrees;
94 
96  std::optional<std::string> _persistent_storage_file;
97 
99  std::vector<std::pair<Real, Real>> _bbox;
100 
101  friend void dataStore<T>(std::ostream & stream, ValueCache<T> & c, void * context);
102  friend void dataLoad<T>(std::istream & stream, ValueCache<T> & c, void * context);
103 };
104 
105 template <typename T>
106 ValueCache<T>::ValueCache(std::size_t in_dim, std::size_t max_leaf_size)
107  : _kd_tree(nullptr), _in_dim(in_dim), _max_leaf_size(max_leaf_size), _max_subtrees(100)
108 {
109 }
110 
111 template <typename T>
112 ValueCache<T>::ValueCache(const std::string & file_name,
113  std::size_t in_dim,
114  std::size_t max_leaf_size)
115  : ValueCache(in_dim, max_leaf_size)
116 {
117  _persistent_storage_file = file_name;
118 
119  // if the persistent storage file exists and is readable, load it
121  /*check_line_endings =*/false,
122  /*throw_on_unreadable =*/false))
123  {
124  std::ifstream in_file(_persistent_storage_file->c_str());
125  if (!in_file)
126  mooseError("Failed to open '", *_persistent_storage_file, "' for reading.");
127  dataLoad(in_file, *this, nullptr);
128  }
129 }
130 
131 template <typename T>
133 {
134  // if a persistent storage file was specified, write results back to it
135  if (_persistent_storage_file.has_value())
136  {
137  std::ofstream out_file(_persistent_storage_file->c_str());
138  if (!out_file)
139  mooseWarning("Failed to open '", *_persistent_storage_file, "' for writing.");
140  dataStore(out_file, *this, nullptr);
141  }
142 }
143 
144 template <typename T>
145 void
146 ValueCache<T>::insert(const std::vector<Real> & in_val, const T & out_val)
147 {
148  mooseAssert(in_val.size() == _in_dim, "Key dimensions do not match cache dimensions");
149 
150  auto id = size();
151  _location_data.emplace_back(in_val, out_val);
152 
153  // update bounding box
154  if (id == 0)
155  {
156  // first item is inserted
157  _bbox.resize(_in_dim);
158  for (const auto i : make_range(_in_dim))
159  _bbox[i] = {in_val[i], in_val[i]};
160  }
161  else
162  for (const auto i : make_range(_in_dim))
163  _bbox[i] = {std::min(_bbox[i].first, in_val[i]), std::max(_bbox[i].second, in_val[i])};
164 
165  // do we have too many subtrees?
166  if (_kd_tree && _kd_tree->getAllIndices().size() > _max_subtrees)
167  _kd_tree = nullptr;
168 
169  // rebuild tree or add point
170  if (!_kd_tree)
171  {
172  _kd_tree = std::make_unique<KdTreeT>(_in_dim, *this, _max_leaf_size);
173  mooseAssert(_kd_tree != nullptr, "KDTree was not properly initialized.");
174  }
175  else
176  _kd_tree->addPoints(id, id);
177 }
178 
182 template <typename T>
183 std::tuple<const std::vector<Real> &, const T &, Real>
184 ValueCache<T>::getNeighbor(const std::vector<Real> & in_val)
185 {
186  // throw an exception if this is called on an empty cache
187  if (_location_data.empty())
188  throw std::runtime_error("Attempting to retrieve a neighbor from an empty ValueCache.");
189 
190  // buffers for the kNN search
191  nanoflann::KNNResultSet<Real> result_set(1);
192  std::size_t return_index;
193  Real distance;
194 
195  // kNN search
196  result_set.init(&return_index, &distance);
197  _kd_tree->findNeighbors(result_set, in_val.data());
198 
199  const auto & [location, data] = _location_data[return_index];
200  return {std::cref(location), std::cref(data), distance};
201 }
202 
207 template <typename T>
208 std::vector<std::tuple<const std::vector<Real> &, const T &, Real>>
209 ValueCache<T>::getNeighbors(const std::vector<Real> & in_val, const std::size_t k)
210 {
211  // return early if no points are stored
212  if (_location_data.empty())
213  return {};
214 
215  // buffers for the kNN search
216  nanoflann::KNNResultSet<Real> result_set(std::min(k, size()));
217  std::vector<std::size_t> return_indices(std::min(k, size()));
218  std::vector<Real> distances(std::min(k, size()));
219 
220  // kNN search
221  result_set.init(return_indices.data(), distances.data());
222  _kd_tree->findNeighbors(result_set, in_val.data());
223 
224  // prepare results to be returned
225  std::vector<std::tuple<const std::vector<Real> &, const T &, Real>> nearest_neighbors;
226  for (const auto i : index_range(result_set))
227  {
228  const auto & [location, data] = _location_data[return_indices[i]];
229  nearest_neighbors.emplace_back(std::cref(location), std::cref(data), distances[i]);
230  }
231  return nearest_neighbors;
232 }
233 
234 template <typename T>
235 std::size_t
237 {
238  return kdtree_get_point_count();
239 }
240 
241 template <typename T>
242 void
244 {
245  _location_data.clear();
246  _kd_tree = nullptr;
247 }
248 
249 template <typename T>
250 void
252 {
253  if (_location_data.empty())
254  return;
255 
256  // reset bounding box (must be done before the tree is built)
257  _bbox.resize(_in_dim);
258  const auto & location0 = _location_data[0].first;
259  for (const auto i : make_range(_in_dim))
260  _bbox[i] = {location0[i], location0[i]};
261 
262  for (const auto & pair : _location_data)
263  {
264  const auto & location = pair.first;
265  for (const auto i : make_range(_in_dim))
266  _bbox[i] = {std::min(_bbox[i].first, location[i]), std::max(_bbox[i].second, location[i])};
267  }
268 
269  // build kd-tree
270  _kd_tree = std::make_unique<KdTreeT>(_in_dim, *this, _max_leaf_size);
271  mooseAssert(_kd_tree != nullptr, "KDTree was not properly initialized.");
272 }
273 
274 template <typename T>
275 std::size_t
277 {
278  return _location_data.size();
279 }
280 
281 template <typename T>
282 Real
283 ValueCache<T>::kdtree_get_pt(const std::size_t idx, const std::size_t dim) const
284 {
285  return _location_data[idx].first[dim];
286 }
287 
288 template <typename T>
289 template <class BBOX>
290 bool
292 {
293  if (_location_data.empty())
294  return false;
295 
296  // return the bounding box incrementally built upon insertion
297  for (const auto i : make_range(_in_dim))
298  bb[i] = {_bbox[i].first, _bbox[i].second};
299  return true;
300 }
301 
302 template <typename T>
303 inline void
304 dataStore(std::ostream & stream, ValueCache<T> & c, void * context)
305 {
306  storeHelper(stream, c._location_data, context);
307 }
308 
309 template <typename T>
310 inline void
311 dataLoad(std::istream & stream, ValueCache<T> & c, void * context)
312 {
313  loadHelper(stream, c._location_data, context);
314  c.rebuildTree();
315 }
std::tuple< const std::vector< Real > &, const T &, Real > getNeighbor(const std::vector< Real > &in_val)
get a single neighbor of in_val along with stored values and distances
Definition: ValueCache.h:184
bool kdtree_get_bbox(BBOX &bb) const
Definition: ValueCache.h:291
friend void dataLoad(std::istream &stream, ValueCache< T > &c, void *context)
Definition: ValueCache.h:311
std::size_t size()
return the number of cache entries
Definition: ValueCache.h:236
std::optional< std::string > _persistent_storage_file
file name for persistent store/restore of the cache
Definition: ValueCache.h:96
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition: MooseError.h:336
static constexpr std::size_t dim
This is the dimension of all vector and tensor datastructures used in MOOSE.
Definition: Moose.h:153
ValueCache is a generic helper template to implement an unstructured data cache, where arbitrary resu...
Definition: ValueCache.h:27
ValueCache(std::size_t in_dim, std::size_t max_leaf_size=10)
Construct a ValueCache with indices in in_dim dimensions.
Definition: ValueCache.h:106
Real distance(const Point &p)
const std::size_t _in_dim
Definition: ValueCache.h:91
auto max(const L &left, const R &right)
void storeHelper(std::ostream &stream, P &data, void *context)
Scalar helper routine.
Definition: DataIO.h:893
nanoflann::KDTreeSingleIndexDynamicAdaptor< nanoflann::L2_Simple_Adaptor< Real, ValueCache< T > >, ValueCache< T >, -1, std::size_t > KdTreeT
Definition: ValueCache.h:86
std::vector< std::tuple< const std::vector< Real > &, const T &, Real > > getNeighbors(const std::vector< Real > &in_val, const std::size_t k)
get a list of up to k neighbors of in_val along with stored values and distances
Definition: ValueCache.h:209
void insert(const std::vector< Real > &in_val, const T &out_val)
insert a new value out_value at the position in_val
Definition: ValueCache.h:146
bool checkFileReadable(const std::string &filename, bool check_line_endings=false, bool throw_on_unreadable=true, bool check_for_git_lfs_pointer=true)
Checks to see if a file is readable (exists and permissions)
Definition: MooseUtils.C:250
void clear()
remove all data from the cache
Definition: ValueCache.h:243
const std::size_t _max_subtrees
Definition: ValueCache.h:93
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::size_t kdtree_get_point_count() const
Nanoflann interface functions.
Definition: ValueCache.h:276
std::vector< std::pair< Real, Real > > _bbox
bounding box (updated upon insertion)
Definition: ValueCache.h:99
Real kdtree_get_pt(const std::size_t idx, const std::size_t dim) const
Definition: ValueCache.h:283
std::unique_ptr< KdTreeT > _kd_tree
Definition: ValueCache.h:89
const std::size_t _max_leaf_size
Definition: ValueCache.h:92
IntRange< T > make_range(T beg, T end)
void rebuildTree()
rebuild the kd-tree from scratch and update the bounding box
Definition: ValueCache.h:251
~ValueCache()
Object destructor. If the cache was constructed with a file name, it gets written here...
Definition: ValueCache.h:132
void dataStore(std::ostream &stream, ValueCache< T > &c, void *context)
Definition: ValueCache.h:304
void dataLoad(std::istream &stream, ValueCache< T > &c, void *context)
Definition: ValueCache.h:311
auto min(const L &left, const R &right)
void loadHelper(std::istream &stream, P &data, void *context)
Scalar helper routine.
Definition: DataIO.h:985
auto index_range(const T &sizable)
std::vector< std::pair< std::vector< Real >, T > > _location_data
Definition: ValueCache.h:88
unsigned int idx(const ElemType type, const unsigned int nx, const unsigned int i, const unsigned int j)