https://mooseframework.inl.gov
MultiDimensionalInterpolation.C
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 
11 
12 #include <cassert>
13 #include <fstream>
14 #include <stdexcept>
15 
16 template <typename T>
18  const std::vector<std::vector<Real>> & base_points, const MultiIndex<Real> & data)
19  : _base_points(std::vector<std::vector<Real>>()), _data(MultiIndex<Real>({}))
20 {
21  setData(base_points, data);
22 }
23 
24 template <typename T>
26  : _base_points(std::vector<std::vector<Real>>()), _data(MultiIndex<Real>({}))
27 {
28 }
29 
30 template <typename T>
31 void
32 MultiDimensionalInterpolationTempl<T>::setData(const std::vector<std::vector<Real>> & base_points,
33  const MultiIndex<Real> & data)
34 {
35  _degenerate_interpolation = false;
36  _setup_complete = false;
37 
38  // stash away original dimension of data because we might modify it
39  _original_dim = data.dim();
40  _degenerate_index.resize(_original_dim, false);
41 
42  // need to check this here to make sure the next loop is safe
43  if (data.dim() != base_points.size())
44  {
45  std::ostringstream oss;
46  oss << "Leading dimension of base_points " << base_points.size() << " and data dimensionality "
47  << data.dim() << " are inconsistent.";
48  throw std::domain_error(oss.str());
49  }
50 
51  // first a corner case is addressed; if the size of the MultiIndex in one
52  // dimension is 1, then finding the interpolation "stencil" becomes much
53  // harder. It is easier to slice the data array and keep track of which values
54  // to discard in the interpolation routines
55  MultiIndex<Real>::size_type slice_dimensions;
56  MultiIndex<Real>::size_type slice_indices;
57  MultiIndex<Real>::size_type size = data.size();
58  std::vector<std::vector<Real>> modified_base_points;
59  for (unsigned int j = 0; j < data.dim(); ++j)
60  if (size[j] == 1)
61  {
62  slice_dimensions.push_back(j);
63  slice_indices.push_back(0);
64  _degenerate_index[j] = true;
65  }
66  else
67  modified_base_points.push_back(base_points[j]);
68 
69  // if all dimensions have size 1, then there is only one value to return
70  // this is a weird corner case that we should support to make life easier
71  // for whoever uses this object
72  // In this case, we can just use the passed base_points and data objects
73  // and short-cut the computation in the interpolation function
74  if (modified_base_points.size() == 0)
75  {
76  _degenerate_interpolation = true;
77  _base_points = base_points;
78  _data = data;
79  }
80  else
81  {
82  // set the data
83  _base_points = modified_base_points;
84  _data = data.slice(slice_dimensions, slice_indices);
85  }
86  errorCheck();
87 }
88 
89 template <typename T>
90 void
92 {
93  if (_base_points.size() != _data.dim())
94  throw std::domain_error("Size of the leading dimension of base points must be equal to "
95  "dimensionality of data array");
96 
97  MultiIndex<Real>::size_type shape = _data.size();
98  for (unsigned int j = 0; j < _data.dim(); ++j)
99  {
100  if (shape[j] != _base_points[j].size())
101  {
102  std::ostringstream oss;
103  oss << "Dimension " << j << " has " << _base_points[j].size()
104  << " base points but dimension of data array is " << shape[j];
105  throw std::domain_error(oss.str());
106  }
107 
108  for (unsigned int i = 1; i < _base_points[j].size(); ++i)
109  {
110  if (_base_points[j][i - 1] >= _base_points[j][i])
111  {
112  std::ostringstream oss;
113  oss << "Base point values for dimension " << j << " are not strictly increasing: bp["
114  << i - 1 << "]: " << _base_points[j][i - 1] << " bc[" << i
115  << "]: " << _base_points[j][i];
116  throw std::domain_error(oss.str());
117  }
118  }
119  }
120 
121  // now the setup is complete
122  _setup_complete = true;
123 }
124 
125 template <typename T>
126 void
128  MultiIndex<Real>::size_type & indices) const
129 {
130  assert(values.size() == _data.dim());
131 
132  indices.resize(_data.dim());
133  for (unsigned int d = 0; d < _data.dim(); ++d)
134  indices[d] = linearSearchHelper(values[d], _base_points[d]);
135 }
136 
137 template <typename T>
138 unsigned int
140  const std::vector<Real> & vector) const
141 {
142 // check order of vector only in debug
143 #ifndef NDEBUG
144  for (unsigned int i = 1; i < vector.size(); ++i)
145  {
146  if (vector[i - 1] >= vector[i])
147  {
148  std::ostringstream oss;
149  oss << "In linearSearchHelper vector is not strictly increasing: vector[" << i - 1
150  << "]: " << vector[i - 1] << " vector[" << i << "]: " << vector[i];
151  throw std::domain_error(oss.str());
152  }
153  }
154 #endif
155 
156  // smaller/larger cases for tabulation
157  if (x < vector[0])
158  {
159  x = vector[0];
160  return 0;
161  }
162  else if (x >= vector.back())
163  {
164  // this requires explanation because it looks like a bug (I swear it isn't, it's a feature)
165  // if x >= largest base point value and the index i = vector.size() - 1 is returned, then during
166  // the interpolation, i + 1 will be accessed, causing a segementation fault. Logic in the
167  // interpolation is also bad because it may slow it down. By returning i = vector.size() - 2,
168  // the last two values in the base point array are used, but x is reset to be equal to the last
169  // value. This will give the right interpolation behavior.
170  x = vector.back();
171  return vector.size() - 2;
172  }
173 
174  for (unsigned int j = 0; j < vector.size() - 2; ++j)
175  if (x >= vector[j] && x < vector[j + 1])
176  return j;
177  return vector.size() - 2;
178 }
179 
180 template <typename T>
181 T
183 {
184  // ensure that object has been set up properly
185  if (!_setup_complete)
186  throw std::domain_error(
187  "Object has been constructed properly. This happens when the empty constructor is called "
188  "and setData is not called before interpolation.");
189 
190  // assert does not seem to be enough to check data consistency here
191  // because this function is exposed to user and will be frequently used
192  if (x.size() != _original_dim)
193  {
194  std::ostringstream oss;
195  oss << "In sample the parameter x has size " << x.size() << " but data has dimension "
196  << _data.dim();
197  throw std::domain_error(oss.str());
198  }
199 
200  // in this case there is only one entry that resides at flat index 0
201  if (_degenerate_interpolation)
202  return _data[0];
203 
204  // make a copy of x because linearSearch needs to be able to modify it
205  // also adjust for degenerate indices that were removed
206  std::vector<T> y(_data.dim());
207  unsigned int l = 0;
208  for (unsigned int j = 0; j < _original_dim; ++j)
209  if (!_degenerate_index[j])
210  {
211  y[l] = x[j];
212  ++l;
213  }
214 
215  // obtain the indices using linearSearch & get flat index in _data array
217  linearSearch(y, indices);
218 
219  // pre-compute the volume of the hypercube and weights used for the interpolation
220  Real volume = 1;
221  std::vector<std::vector<T>> weights(_data.dim());
222  for (unsigned int j = 0; j < _data.dim(); ++j)
223  {
224  volume *= _base_points[j][indices[j] + 1] - _base_points[j][indices[j]];
225 
226  // now compute weight for each dimension, note that
227  // weight[j][0] = bp[j][high] - y[j]
228  // weight[j][0] = y[j] - bp[j][low]
229  // because in the interpolation the value on the "left" is weighted with the
230  // distance of y to the "right"
231  weights[j].resize(2);
232  weights[j][0] = _base_points[j][indices[j] + 1] - y[j];
233  weights[j][1] = y[j] - _base_points[j][indices[j]];
234  }
235 
236  // get flat index and stride
237  unsigned int flat_index = _data.flatIndex(indices);
238  MultiIndex<Real>::size_type stride = _data.stride();
239 
240  // this is the interpolation routine, evaluation can be expensive if dim is large
241 
242  // first we retrieve the data around the point y
243  // we use a 2^dim MultiIndex hypercube for generating the permulations
244  MultiIndex<Real>::size_type shape(_data.dim(), 2);
245  MultiIndex<Real> hypercube = MultiIndex<Real>(shape);
246  T interpolated_value = 0;
247  for (const auto & p : hypercube)
248  {
249  // this loop computes the flat index for the point in the
250  // hypercube and accumulates the total weight for this point
251  // the selection of the dimensional weight is based on the swapping
252  // done in the pre-computing loop before
253  unsigned int index = flat_index;
254  T w = 1;
255  for (unsigned int j = 0; j < p.first.size(); ++j)
256  {
257  index += stride[j] * p.first[j];
258  w *= weights[j][p.first[j]];
259  }
260 
261  // add the contribution of this base point to the interpolated value
262  interpolated_value += w * _data[index];
263  }
264 
265  return interpolated_value / volume;
266 }
267 
unsigned int linearSearchHelper(T &x, const std::vector< Real > &vector) const
linear search helper for a single std::vector; note that argument is non-const to handle smaller/larg...
T multiLinearInterpolation(const std::vector< T > &x) const
This function will take an independent variable input and will return the dependent variable based on...
void linearSearch(std::vector< T > &values, MultiIndex< Real >::size_type &indices) const
linearSearch finds the indices i_k of the base point such that base_point[d][i_k] <= values[d] < base...
void setData(const std::vector< std::vector< Real >> &base_points, const MultiIndex< Real > &data)
sets data but also fixes degenerate dimensions in data
unsigned int dim() const
dimension of the container
Definition: MultiIndex.h:62
void resize(const size_type &shape)
Resize container. Must keep dimensionality constant.
Definition: MultiIndex.h:345
const size_type & size() const
container size as dim dimensional vector
Definition: MultiIndex.h:59
Real volume(const MeshBase &mesh, unsigned int dim=libMesh::invalid_uint)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
unsigned int stride(unsigned int j) const
access the stride
Definition: MultiIndex.h:389
This class interpolates multi-dimensional data sets.
MultiIndex< T > slice(unsigned int dimension, unsigned int index) const
accesses a slice of the multi index object
Definition: MultiIndex.h:281
void errorCheck()
checks consistency of the data