https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
16template <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
24template <typename T>
26 : _base_points(std::vector<std::vector<Real>>()), _data(MultiIndex<Real>({}))
27{
28}
29
30template <typename T>
31void
32MultiDimensionalInterpolationTempl<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;
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
89template <typename T>
90void
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
125template <typename T>
126void
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
137template <typename T>
138unsigned 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
180template <typename T>
181T
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
std::array< Real, 2 > values
Definition MortarUtils.C:52
This class interpolates multi-dimensional data sets.
void errorCheck()
checks consistency of the data
void setData(const std::vector< std::vector< Real > > &base_points, const MultiIndex< Real > &data)
sets data but also fixes degenerate dimensions in data
T multiLinearInterpolation(const std::vector< T > &x) const
This function will take an independent variable input and will return the dependent variable based on...
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...
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...
Implements a container class for multi-indexed objects with an arbitrary number of indices.
Definition MultiIndex.h:23
MultiIndex< T > slice(unsigned int dimension, unsigned int index) const
accesses a slice of the multi index object
Definition MultiIndex.h:281
std::vector< unsigned int > size_type
Definition MultiIndex.h:31
const size_type & size() const
container size as dim dimensional vector
Definition MultiIndex.h:59
void resize(const size_type &shape)
Resize container. Must keep dimensionality constant.
Definition MultiIndex.h:345
unsigned int dim() const
dimension of the container
Definition MultiIndex.h:62