2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2016, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
36 /*! \libinternal \file
38 * Declares classes for cubic spline table
41 * \author Erik Lindahl <erik.lindahl@gmail.com>
42 * \ingroup module_tables
44 #ifndef GMX_TABLES_CUBICSPLINETABLE_H
45 #define GMX_TABLES_CUBICSPLINETABLE_H
47 #include <initializer_list>
50 #include "gromacs/simd/simd.h"
51 #include "gromacs/tables/tableinput.h"
52 #include "gromacs/utility/alignedallocator.h"
53 #include "gromacs/utility/classhelpers.h"
54 #include "gromacs/utility/exceptions.h"
55 #include "gromacs/utility/gmxassert.h"
56 #include "gromacs/utility/real.h"
61 /*! \libinternal \brief Cubic spline interpolation table.
63 * This class interpolates a function specified either as an analytical
64 * expression or from user-provided table data.
66 * At initialization, you provide the reference function of vectors
67 * as a list of tuples that contain a brief name, the function, and
68 * derivative for each function to tabulate. To create a table with
69 * two functions this initializer list can for instance look like
71 * { {"LJ6", lj6Func, lj6Der}, {"LJ12", lj12Func, lj12Der} }
73 * The names are only used so exceptions during initialization can
74 * be traced to a specific table.
76 * When interpolating, there are methods to interpolate either 1, 2, or 3
77 * functions in one go. By default these interpolation routines will
78 * operate on tables with the same number of functions as specified in
79 * the interpolation method (debug builds check that this is consistent with
80 * the table). However, it is also possible to use optional template
81 * parameters that specify the total number of functions in a table, and
82 * what function index to interpolate. For instance, to interpolate the
83 * derivative of the second function (i.e., index 1) in a
84 * multi-function-table with three functions in total, you can write
86 * table.evaluateDerivative<3,1>(x,&der);
88 * Here too, debug builds will check that the template parameters are
89 * consistent with the table.
91 * This class interpolates a function specified either as an analytical
92 * expression or from user-provided table data. The coefficients for each
93 * table point are precalculated such that we simply evaluate
96 * V(x) = Y + F \epsilon + G \epsilon^2 + H \epsilon^3
97 * V'(x) = (F + 2 G \epsilon + 3 H \epsilon^2)/h
100 * Where h is the spacing and epsilon the fractional offset from table point.
102 * While it is possible to create tables only from function values
103 * (i.e., no derivatives), it is recommended to provide derivatives for higher
104 * accuracy and to avoid issues with numerical differentiation. Note that the
105 * table input should be smooth, i.e. it should not contain noise e.g. from an
106 * (iterative) Boltzmann inversion procedure - you have been warned.
108 * \note This class is responsible for fundamental interpolation of any function,
109 * which might or might not correspond to a potential. For this reason
110 * both input and output derivatives are proper function derivatives, and
111 * we do not swap any signs to get forces directly from the table.
113 * \note There will be a small additional accuracy loss from the internal
114 * operation where we calculate the epsilon offset from the nearest table
115 * point, since the integer part we subtract can get large in those cases.
117 * While this is technically possible to solve with extended precision
118 * arithmetics, that would introduce extra instructions in some highly
119 * performance-sensitive code parts. For typical GROMACS interaction
120 * functions the derivatives will decay faster than the potential, which
121 * means it will never play any role. For other functions it will only
122 * cause a small increase in the relative error for arguments where the
123 * magnitude of the function or derivative is very small.
124 * Since we typically sum several results in GROMACS, this should never
125 * show up in any real cases, and for this reason we choose not to do
126 * the extended precision arithmetics.
128 * \note These routines are not suitable for table ranges starting far away
129 * from zero, since we allocate memory and calculate indices starting from
130 * range zero for efficiency reasons.
132 class CubicSplineTable
135 /*! \brief Change that function value falls inside range when debugging
137 * \tparam T Lookup argument floating-point type, typically SimdReal or real.
138 * \param r Lookup argument to test
140 * \throws Debug builds will throw gmx::RangeError for values that are
141 * larger than the upper limit of the range, or smaller than 0.
142 * We allow the table to be called with arguments between 0 and
143 * the lower limit of the range, since this might in theory occur
144 * once-in-a-blue-moon with some algorithms.
146 template <typename T
>
148 rangeCheck(T gmx_unused r
) const
151 // Check that all values fall in range when debugging
152 if (anyTrue( r
< T(0.0) || T(range_
.second
) <= r
) )
154 GMX_THROW(RangeError("Interpolation input value falls outside table definition range"));
161 /*! \brief Default tolerance for cubic spline tables
163 * This is 10*GMX_FLOAT_EPS in single precision, and
164 * 1e-10 for double precision. It might not be worth setting
165 * this tolerance lower than 1e-10 in double precision, both because
166 * you will end up with very large tables, and because
167 * functions like r^-12 become so large for small values of r the
168 * table generation code will lead to some precision loss even
169 * in double precision.
171 static const real defaultTolerance
;
173 /*! \brief Initialize table data from function
175 * \param analyticalInputList Initializer list with one or more functions to tabulate,
176 * specified as elements with a string description and
177 * the function as well as derivative. The function will also
178 * be called for values smaller than the lower limit of the
179 * range, but we avoid calling it for 0.0 if that value
180 * is not included in the range.
181 * Constructor will throw gmx::APIError for negative values.
182 * Due to the way the numerical derivative evaluation depends
183 * on machine precision internally, this range must be
184 * at least 0.001, or the constructor throws gmx::APIError.
185 * \param range Range over which the function will be tabulated.
186 * Constructor will throw gmx::APIError for negative values,
187 * or if the value/derivative vector does not cover the
189 * \param tolerance Requested accuracy of the table. This will be used to
190 * calculate the required internal spacing. If this cannot
191 * be achieved (for instance because the table would require
192 * too much memory) the constructor will throw gmx::ToleranceError.
194 * \note The functions are always defined in double precision to avoid
195 * losing accuracy when constructing tables.
197 * \note Since we fill the table for values below range.first, you can achieve
198 * a smaller table by using a smaller range where the tolerance has to be
199 * met, and accept that a few function calls below range.first do not
200 * quite reach the tolerance.
202 * \warning For efficiency reasons (since this code is used in some inner
203 * (kernels), we always allocate memory and calculate table indices
204 * for the complete interval [0,range.second], although the data will
205 * not be valid outside the definition range to avoid calling the
206 * function there. This means you should \a not use this class
207 * to tabulate functions for small ranges very far away from zero,
208 * since you would both waste a huge amount of memory and incur
209 * truncation errors when calculating the index.
211 * \throws gmx::ToleranceError if the requested tolerance cannot be achieved,
212 * and gmx::APIError for other incorrect input.
214 CubicSplineTable(std::initializer_list
<AnalyticalSplineTableInput
> analyticalInputList
,
215 const std::pair
<real
, real
> &range
,
216 real tolerance
= defaultTolerance
);
218 /*! \brief Initialize table data from tabulated values and derivatives
220 * \param numericalInputList Initializer list with one or more functions to tabulate,
221 * specified as a string description, vectors with function and
222 * derivative values, and the input spacing. Data points are
223 * separated by the spacing parameter, starting from 0.
224 * Values below the lower limit of the range will be used to
225 * attempt defining the table, but we avoid using index 0
226 * unless 0.0 is included in the range. Some extra points beyond
227 * range.second are required to re-interpolate values, so add
228 * some margin. The constructor will throw gmx::APIError if the
229 * input vectors are too short to cover the requested range
230 * (and they must always be at least five points).
231 * \param range Range over which the function will be tabulated.
232 * Constructor will throw gmx::APIError for negative values,
233 * or if the value/derivative vector does not cover the
235 * \param tolerance Requested accuracy of the table. This will be used to
236 * calculate the required internal spacing and possibly
237 * re-interpolate. The constructor will throw
238 * gmx::ToleranceError if the input spacing is too coarse
239 * to achieve this accuracy.
241 * \note The input data vectors are always double precision to avoid
242 * losing accuracy when constructing tables.
244 * \note Since we fill the table for values below range.first, you can achieve
245 * a smaller table by using a smaller range where the tolerance has to be
246 * met, and accept that a few function calls below range.first do not
247 * quite reach the tolerance.
249 * \warning For efficiency reasons (since this code is used in some inner
250 * (kernels), we always allocate memory and calculate table indices
251 * for the complete interval [0,range.second], although the data will
252 * not be valid outside the definition range to avoid calling the
253 * function there. This means you should \a not use this class
254 * to tabulate functions for small ranges very far away from zero,
255 * since you would both waste a huge amount of memory and incur
256 * truncation errors when calculating the index.
258 CubicSplineTable(std::initializer_list
<NumericalSplineTableInput
> numericalInputList
,
259 const std::pair
<real
, real
> &range
,
260 real tolerance
= defaultTolerance
);
263 /************************************************************
264 * Evaluation methods for single functions *
265 ************************************************************/
267 /*! \brief Evaluate both function and derivative, single table function
269 * This is a templated method where the template can be either real or SimdReal.
271 * \tparam numFuncInTable Number of separate functions in table, default is 1
272 * \tparam funcIndex Index of function to evaluate in table, default is 0
273 * \tparam T Type (SimdReal or real) of lookup and result
274 * \param r Points for which to evaluate function and derivative
275 * \param[out] functionValue Function value
276 * \param[out] derivativeValue Function derivative
278 * For debug builds we assert that the input values fall in the range
279 * specified when constructing the table.
281 template <int numFuncInTable
= 1, int funcIndex
= 0, typename T
>
283 evaluateFunctionAndDerivative(T r
,
285 T
* derivativeValue
) const
288 GMX_ASSERT(numFuncInTable
== numFuncInTable_
, "Evaluation method not matching number of functions in table");
289 GMX_ASSERT(funcIndex
< numFuncInTable
, "Function index not in range of the number of tables");
291 T rTable
= r
* T(tableScale_
);
292 auto tabIndex
= cvttR2I(rTable
); // type is either std::int32_t or SimdInt32
293 T eps
= rTable
- trunc(rTable
);
296 // Load Derivative, Delta, Function, and Zero values for each table point.
297 // The 4 refers to these four values - not any SIMD width.
298 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4*funcIndex
, tabIndex
, &Y
, &F
, &G
, &H
);
299 *functionValue
= fma(fma(fma(H
, eps
, G
), eps
, F
), eps
, Y
);
300 *derivativeValue
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
303 /*! \brief Evaluate function value only, single table function
305 * This is a templated method where the template can be either real or SimdReal.
307 * \tparam numFuncInTable Number of separate functions in table, default is 1
308 * \tparam funcIndex Index of function to evaluate in table, default is 0
309 * \tparam T Type (SimdReal or real) of lookup and result
310 * \param r Points for which to evaluate function value
311 * \param[out] functionValue Function value
313 * For debug builds we assert that the input values fall in the range
314 * specified when constructing the table.
316 template <int numFuncInTable
= 1, int funcIndex
= 0, typename T
>
318 evaluateFunction(T r
,
319 T
* functionValue
) const
323 evaluateFunctionAndDerivative
<numFuncInTable
, funcIndex
>(r
, functionValue
, &der
);
326 /*! \brief Evaluate function derivative only, single table function
328 * This is a templated method where the template can be either real or SimdReal.
330 * \tparam numFuncInTable Number of separate functions in table, default is 1
331 * \tparam funcIndex Index of function to evaluate in table, default is 0
332 * \tparam T Type (SimdReal or real) of lookup and result
333 * \param r Points for which to evaluate function derivative
334 * \param[out] derivativeValue Function derivative
336 * For debug builds we assert that the input values fall in the range
337 * specified when constructing the table.
339 template <int numFuncInTable
= 1, int funcIndex
= 0, typename T
>
341 evaluateDerivative(T r
,
342 T
* derivativeValue
) const
345 GMX_ASSERT(numFuncInTable
== numFuncInTable_
, "Evaluation method not matching number of functions in table");
346 GMX_ASSERT(funcIndex
< numFuncInTable
, "Function index not in range of the number of tables");
348 T rTable
= r
* T(tableScale_
);
349 auto tabIndex
= cvttR2I(rTable
); // type is either std::int32_t or SimdInt32
350 T eps
= rTable
- trunc(rTable
);
353 // Load Derivative, Delta, Function, and Zero values for each table point.
354 // The 4 refers to these four values - not any SIMD width.
355 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4 * funcIndex
, tabIndex
, &Y
, &F
, &G
, &H
);
356 *derivativeValue
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
359 /************************************************************
360 * Evaluation methods for two functions *
361 ************************************************************/
363 /*! \brief Evaluate both function and derivative, two table functions
365 * This is a templated method where the template can be either real or SimdReal.
367 * \tparam numFuncInTable Number of separate functions in table, default is 2
368 * \tparam funcIndex0 Index of 1st function to evaluate in table, default is 0
369 * \tparam funcIndex1 Index of 2nd function to evaluate in table, default is 1
370 * \tparam T Type (SimdReal or real) of lookup and result
371 * \param r Points for which to evaluate function and derivative
372 * \param[out] functionValue0 Interpolated value for first function
373 * \param[out] derivativeValue0 Interpolated derivative for first function
374 * \param[out] functionValue1 Interpolated value for second function
375 * \param[out] derivativeValue1 Interpolated derivative for second function
377 * For debug builds we assert that the input values fall in the range
378 * specified when constructing the table.
380 template <int numFuncInTable
= 2, int funcIndex0
= 0, int funcIndex1
= 1, typename T
>
382 evaluateFunctionAndDerivative(T r
,
384 T
* derivativeValue0
,
386 T
* derivativeValue1
) const
389 GMX_ASSERT(numFuncInTable
== numFuncInTable_
, "Evaluation method not matching number of functions in table");
390 GMX_ASSERT(funcIndex0
< numFuncInTable
&& funcIndex1
< numFuncInTable
, "Function index not in range of the number of tables");
392 T rTable
= r
* T(tableScale_
);
393 auto tabIndex
= cvttR2I(rTable
); // type is either std::int32_t or SimdInt32
394 T eps
= rTable
- trunc(rTable
);
397 // Load Derivative, Delta, Function, and Zero values for each table point.
398 // The 4 refers to these four values - not any SIMD width.
399 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4*funcIndex0
, tabIndex
, &Y
, &F
, &G
, &H
);
400 *functionValue0
= fma(fma(fma(H
, eps
, G
), eps
, F
), eps
, Y
);
401 *derivativeValue0
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
403 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4*funcIndex1
, tabIndex
, &Y
, &F
, &G
, &H
);
404 *functionValue1
= fma(fma(fma(H
, eps
, G
), eps
, F
), eps
, Y
);
405 *derivativeValue1
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
408 /*! \brief Evaluate function value only, two table functions
410 * This is a templated method where the template can be either real or SimdReal.
412 * \tparam numFuncInTable Number of separate functions in table, default is 2
413 * \tparam funcIndex0 Index of 1st function to evaluate in table, default is 0
414 * \tparam funcIndex1 Index of 2nd function to evaluate in table, default is 1
415 * \tparam T Type (SimdReal or real) of lookup and result
416 * \param r Points for which to evaluate function value
417 * \param[out] functionValue0 Interpolated value for first function
418 * \param[out] functionValue1 Interpolated value for second function
420 * For debug builds we assert that the input values fall in the range
421 * specified when constructing the table.
423 template <int numFuncInTable
= 2, int funcIndex0
= 0, int funcIndex1
= 1, typename T
>
425 evaluateFunction(T r
,
427 T
* functionValue1
) const
432 evaluateFunctionAndDerivative
<numFuncInTable
, funcIndex0
, funcIndex1
>(r
, functionValue0
, &der0
, functionValue1
, &der1
);
435 /*! \brief Evaluate function derivative only, two table functions
437 * This is a templated method where the template can be either real or SimdReal.
439 * \tparam numFuncInTable Number of separate functions in table, default is 2
440 * \tparam funcIndex0 Index of 1st function to evaluate in table, default is 0
441 * \tparam funcIndex1 Index of 2nd function to evaluate in table, default is 1
442 * \tparam T Type (SimdReal or real) of lookup and result
443 * \param r Points for which to evaluate function derivative
444 * \param[out] derivativeValue0 Interpolated derivative for first function
445 * \param[out] derivativeValue1 Interpolated derivative for second function
447 * For debug builds we assert that the input values fall in the range
448 * specified when constructing the table.
450 template <int numFuncInTable
= 2, int funcIndex0
= 0, int funcIndex1
= 1, typename T
>
452 evaluateDerivative(T r
,
453 T
* derivativeValue0
,
454 T
* derivativeValue1
) const
457 GMX_ASSERT(numFuncInTable
== numFuncInTable_
, "Evaluation method not matching number of functions in table");
458 GMX_ASSERT(funcIndex0
< numFuncInTable
&& funcIndex1
< numFuncInTable
, "Function index not in range of the number of tables");
460 T rTable
= r
* T(tableScale_
);
461 auto tabIndex
= cvttR2I(rTable
); // type is either std::int32_t or SimdInt32
462 T eps
= rTable
- trunc(rTable
);
465 // Load Derivative, Delta, Function, and Zero values for each table point.
466 // The 4 refers to these four values - not any SIMD width.
467 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4 * funcIndex0
, tabIndex
, &Y
, &F
, &G
, &H
);
468 *derivativeValue0
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
470 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4 * funcIndex1
, tabIndex
, &Y
, &F
, &G
, &H
);
471 *derivativeValue1
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
474 /************************************************************
475 * Evaluation methods for three functions *
476 ************************************************************/
479 /*! \brief Evaluate both function and derivative, three table functions
481 * This is a templated method where the template can be either real or SimdReal.
483 * \tparam numFuncInTable Number of separate functions in table, default is 3
484 * \tparam funcIndex0 Index of 1st function to evaluate in table, default is 0
485 * \tparam funcIndex1 Index of 2nd function to evaluate in table, default is 1
486 * \tparam funcIndex2 Index of 3rd function to evaluate in table, default is 2
487 * \tparam T Type (SimdReal or real) of lookup and result
488 * \param r Points for which to evaluate function and derivative
489 * \param[out] functionValue0 Interpolated value for first function
490 * \param[out] derivativeValue0 Interpolated derivative for first function
491 * \param[out] functionValue1 Interpolated value for second function
492 * \param[out] derivativeValue1 Interpolated derivative for second function
493 * \param[out] functionValue2 Interpolated value for third function
494 * \param[out] derivativeValue2 Interpolated derivative for third function
496 * For debug builds we assert that the input values fall in the range
497 * specified when constructing the table.
499 template <int numFuncInTable
= 3, int funcIndex0
= 0, int funcIndex1
= 1, int funcIndex2
= 2, typename T
>
501 evaluateFunctionAndDerivative(T r
,
503 T
* derivativeValue0
,
505 T
* derivativeValue1
,
507 T
* derivativeValue2
) const
510 GMX_ASSERT(numFuncInTable
== numFuncInTable_
, "Evaluation method not matching number of functions in table");
511 GMX_ASSERT(funcIndex0
< numFuncInTable
&& funcIndex1
< numFuncInTable
&& funcIndex2
< numFuncInTable
, "Function index not in range of the number of tables");
513 T rTable
= r
* T(tableScale_
);
514 auto tabIndex
= cvttR2I(rTable
); // type is either std::int32_t or SimdInt32
515 T eps
= rTable
- trunc(rTable
);
518 // Load Derivative, Delta, Function, and Zero values for each table point.
519 // The 4 refers to these four values - not any SIMD width.
520 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4*funcIndex0
, tabIndex
, &Y
, &F
, &G
, &H
);
521 *functionValue0
= fma(fma(fma(H
, eps
, G
), eps
, F
), eps
, Y
);
522 *derivativeValue0
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
524 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4*funcIndex1
, tabIndex
, &Y
, &F
, &G
, &H
);
525 *functionValue1
= fma(fma(fma(H
, eps
, G
), eps
, F
), eps
, Y
);
526 *derivativeValue1
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
528 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4*funcIndex2
, tabIndex
, &Y
, &F
, &G
, &H
);
529 *functionValue2
= fma(fma(fma(H
, eps
, G
), eps
, F
), eps
, Y
);
530 *derivativeValue2
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
533 /*! \brief Evaluate function value only, three table functions
535 * This is a templated method where the template can be either real or SimdReal.
537 * \tparam numFuncInTable Number of separate functions in table, default is 3
538 * \tparam funcIndex0 Index of 1st function to evaluate in table, default is 0
539 * \tparam funcIndex1 Index of 2nd function to evaluate in table, default is 1
540 * \tparam funcIndex2 Index of 3rd function to evaluate in table, default is 2
541 * \tparam T Type (SimdReal or real) of lookup and result
542 * \param r Points for which to evaluate function value
543 * \param[out] functionValue0 Interpolated value for first function
544 * \param[out] functionValue1 Interpolated value for second function
545 * \param[out] functionValue2 Interpolated value for third function
547 * For debug builds we assert that the input values fall in the range
548 * specified when constructing the table.
550 template <int numFuncInTable
= 3, int funcIndex0
= 0, int funcIndex1
= 1, int funcIndex2
= 2, typename T
>
552 evaluateFunction(T r
,
555 T
* functionValue2
) const
561 evaluateFunctionAndDerivative
<numFuncInTable
, funcIndex0
, funcIndex1
, funcIndex2
>(r
, functionValue0
, &der0
, functionValue1
, &der1
, functionValue2
, &der2
);
564 /*! \brief Evaluate function derivative only, three table functions
566 * This is a templated method where the template can be either real or SimdReal.
568 * \tparam numFuncInTable Number of separate functions in table, default is 3
569 * \tparam funcIndex0 Index of 1st function to evaluate in table, default is 0
570 * \tparam funcIndex1 Index of 2nd function to evaluate in table, default is 1
571 * \tparam funcIndex2 Index of 3rd function to evaluate in table, default is 2
572 * \tparam T Type (SimdReal or real) of lookup and result
573 * \param r Points for which to evaluate function derivative
574 * \param[out] derivativeValue0 Interpolated derivative for first function
575 * \param[out] derivativeValue1 Interpolated derivative for second function
576 * \param[out] derivativeValue2 Interpolated derivative for third function
578 * For debug builds we assert that the input values fall in the range
579 * specified when constructing the table.
581 template <int numFuncInTable
= 3, int funcIndex0
= 0, int funcIndex1
= 1, int funcIndex2
= 2, typename T
>
583 evaluateDerivative(T r
,
584 T
* derivativeValue0
,
585 T
* derivativeValue1
,
586 T
* derivativeValue2
) const
589 GMX_ASSERT(numFuncInTable
== numFuncInTable_
, "Evaluation method not matching number of functions in table");
590 GMX_ASSERT(funcIndex0
< numFuncInTable
&& funcIndex1
< numFuncInTable
&& funcIndex2
< numFuncInTable
, "Function index not in range of the number of tables");
592 T rTable
= r
* T(tableScale_
);
593 auto tabIndex
= cvttR2I(rTable
); // type is either std::int32_t or SimdInt32
594 T eps
= rTable
- trunc(rTable
);
597 // Load Derivative, Delta, Function, and Zero values for each table point.
598 // The 4 refers to these four values - not any SIMD width.
599 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4 * funcIndex0
, tabIndex
, &Y
, &F
, &G
, &H
);
600 *derivativeValue0
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
602 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4 * funcIndex1
, tabIndex
, &Y
, &F
, &G
, &H
);
603 *derivativeValue1
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
605 gatherLoadBySimdIntTranspose
<4*numFuncInTable
>(yfghMultiTableData_
.data() + 4 * funcIndex2
, tabIndex
, &Y
, &F
, &G
, &H
);
606 *derivativeValue2
= tableScale_
* fma(fma(T(3.0)*H
, eps
, T(2.0)*G
), eps
, F
);
609 /*! \brief Return the table spacing (distance between points)
611 * You should never have to use this for normal code, but due to the
612 * way tables are constructed internally we need this in the unit tests
613 * to check relative tolerances over each interval.
615 * \return table spacing.
618 tableSpacing() const { return 1.0 / tableScale_
; }
622 std::size_t numFuncInTable_
; //!< Number of separate tabluated functions
623 std::pair
<real
, real
> range_
; //!< Range for which table evaluation is allowed
624 real tableScale_
; //!< Table scale (inverse of spacing between points)
626 /*! \brief Vector with combined table data to save calculations after lookup.
628 * For table point i, this vector contains the four coefficients
629 * Y,F,G,H that we use to express the function value as
630 * V(x) = Y + F e + G e^2 + H e^3, where e is the epsilon offset from
631 * the nearest table point.
633 * To allow aligned SIMD loads we need to use an aligned allocator for
636 std::vector
<real
, AlignedAllocator
<real
> > yfghMultiTableData_
;
638 // There should never be any reason to copy the table since it is read-only
639 GMX_DISALLOW_COPY_AND_ASSIGN(CubicSplineTable
);
645 #endif // GMX_TABLES_CUBICSPLINETABLE_H