https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
26template <typename T>
27class ValueCache;
28template <typename T>
29void dataStore(std::ostream & stream, ValueCache<T> & c, void * context);
30template <typename T>
31void dataLoad(std::istream & stream, ValueCache<T> & c, void * context);
32
41template <typename T>
43{
44public:
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
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
78protected:
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
105template <typename T>
106ValueCache<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
111template <typename T>
112ValueCache<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
131template <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
144template <typename T>
145void
146ValueCache<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
182template <typename T>
183std::tuple<const std::vector<Real> &, const T &, Real>
184ValueCache<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
207template <typename T>
208std::vector<std::tuple<const std::vector<Real> &, const T &, Real>>
209ValueCache<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
234template <typename T>
235std::size_t
237{
238 return kdtree_get_point_count();
239}
240
241template <typename T>
242void
244{
245 _location_data.clear();
246 _kd_tree = nullptr;
247}
248
249template <typename T>
250void
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
274template <typename T>
275std::size_t
277{
278 return _location_data.size();
279}
280
281template <typename T>
282Real
283ValueCache<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
288template <typename T>
289template <class BBOX>
290bool
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
302template <typename T>
303inline void
304dataStore(std::ostream & stream, ValueCache<T> & c, void * context)
305{
306 storeHelper(stream, c._location_data, context);
307}
308
309template <typename T>
310inline void
311dataLoad(std::istream & stream, ValueCache<T> & c, void * context)
312{
313 loadHelper(stream, c._location_data, context);
314 c.rebuildTree();
315}
void storeHelper(std::ostream &stream, P &data, void *context)
Scalar helper routine.
Definition DataIO.h:989
void loadHelper(std::istream &stream, P &data, void *context)
Scalar helper routine.
Definition DataIO.h:1081
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition MooseError.h:345
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
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
unsigned int dim
ValueCache is a generic helper template to implement an unstructured data cache, where arbitrary resu...
Definition ValueCache.h:43
void rebuildTree()
rebuild the kd-tree from scratch and update the bounding box
Definition ValueCache.h:251
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
std::size_t size()
return the number of cache entries
Definition ValueCache.h:236
std::vector< std::pair< Real, Real > > _bbox
bounding box (updated upon insertion)
Definition ValueCache.h:99
bool kdtree_get_bbox(BBOX &bb) const
Definition ValueCache.h:291
const std::size_t _max_leaf_size
Definition ValueCache.h:92
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
~ValueCache()
Object destructor. If the cache was constructed with a file name, it gets written here.
Definition ValueCache.h:132
friend void dataLoad(std::istream &stream, ValueCache< T > &c, void *context)
Definition ValueCache.h:311
std::size_t kdtree_get_point_count() const
Nanoflann interface functions.
Definition ValueCache.h:276
std::vector< std::pair< std::vector< Real >, T > > _location_data
Definition ValueCache.h:88
std::unique_ptr< KdTreeT > _kd_tree
Definition ValueCache.h:89
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
const std::size_t _in_dim
Definition ValueCache.h:91
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
const std::size_t _max_subtrees
Definition ValueCache.h:93
void clear()
remove all data from the cache
Definition ValueCache.h:243
std::optional< std::string > _persistent_storage_file
file name for persistent store/restore of the cache
Definition ValueCache.h:96
nanoflann::KDTreeSingleIndexDynamicAdaptor< nanoflann::L2_Simple_Adaptor< Real, ValueCache< T > >, ValueCache< T >, -1, std::size_t > KdTreeT
Definition ValueCache.h:86
Real kdtree_get_pt(const std::size_t idx, const std::size_t dim) const
Definition ValueCache.h:283
bool checkFileReadable(const std::string &filename, bool check_line_endings, bool throw_on_unreadable, bool check_for_git_lfs_pointer)
Definition MooseUtils.C:265
Real distance(const Point &p)