2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2016,2017,2018,2019, 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.
38 * Declares internal utility functions for spline tables
40 * \author Erik Lindahl <erik.lindahl@gmail.com>
41 * \ingroup module_tables
43 #ifndef GMX_TABLES_SPLINEUTIL_H
44 #define GMX_TABLES_SPLINEUTIL_H
50 #include "gromacs/utility/alignedallocator.h"
51 #include "gromacs/utility/arrayref.h"
52 #include "gromacs/utility/gmxassert.h"
53 #include "gromacs/utility/real.h"
61 /*! \brief Ensure analytical derivative is the derivative of analytical function.
63 * This routine evaluates the numerical derivative of the function for
64 * a few (1000) points in the interval and checks that the relative difference
65 * between numerical and analytical derivative is within the expected error
66 * for the numerical derivative approximation we use.
68 * The main point of this routine is to make sure the user has not made a
69 * mistake or sign error when defining the functions.
71 * \param function Analytical function to differentiate
72 * \param derivative Analytical derivative to compare with
73 * \param range Range to test
75 * \throws If the provided derivative does not seem to match the function.
77 * \note The function/derivative are always double-valued to avoid accuracy loss.
79 void throwUnlessDerivativeIsConsistentWithFunction(const std::function<double(double)>& function,
80 const std::function<double(double)>& derivative,
81 const std::pair<real, real>& range);
83 /*! \brief Ensure vector of derivative values is the derivative of function vector.
85 * This routine differentiates a vector of numerical values and checks
86 * that the relative difference to a provided vector of numerical derivatives
87 * is smaller than the expected error from the numerical differentiation.
89 * The main point of this routine is to make sure the user has not made a
90 * mistake or sign error when defining the functions.
92 * To avoid problems if the vectors change from zero to finite values at the
93 * start/end of the interval, we only check inside the range requested.
95 * \param function Numerical function value vector to differentiate
96 * \param derivative Numerical derivative vector to compare with
97 * \param inputSpacing Distance between input points
98 * \param range Range to test
100 * \throws If the provided derivative does not seem to match the function.
102 * \note The function/derivative vectors and spacing are always double-valued
103 * to avoid accuracy loss.
105 void throwUnlessDerivativeIsConsistentWithFunction(ArrayRef<const double> function,
106 ArrayRef<const double> derivative,
108 const std::pair<real, real>& range);
111 /*! \brief Find smallest quotient between analytical function and its 2nd derivative
113 * Used to calculate spacing for quadratic spline tables. This function divides the
114 * function value by the second derivative (or a very small number when that is zero),
115 * and returns the smallest such quotient found in the range.
117 * Our quadratic tables corresponds to linear interpolation of the derivative,
118 * which means the derivative will typically have larger error than the value
119 * when interpolating. The spacing required to reach a particular relative
120 * tolerance in the derivative depends on the quotient between the first
121 * derivative and the third derivative of the function itself.
123 * You should call this routine with the analytical derivative as the "function"
124 * parameter, and the quotient between "function and second derivative" will
125 * then correspond to the quotient bewteen the derivative and the third derivative
126 * of the actual function we want to tabulate.
128 * Since all functions that can be tabulated efficiently are reasonably smooth,
129 * we simply check 1,000 points in the interval rather than bother about
130 * implementing any complicated global optimization scheme.
132 * \param f Analytical function
133 * \param range Interval
135 * \return Smallest quotient found in range.
137 * \note The function is always double-valued to avoid accuracy loss.
139 real findSmallestQuotientOfFunctionAndSecondDerivative(const std::function<double(double)>& f,
140 const std::pair<real, real>& range);
143 /*! \brief Find smallest quotient between vector of values and its 2nd derivative
145 * Used to calculate spacing for quadratic spline tables. This function divides the
146 * function value by the second derivative (or a very small number when that is zero),
147 * and returns the smallest such quotient found in the range.
149 * Our quadratic tables corresponds to linear interpolation of the derivative,
150 * which means the derivative will typically have larger error than the value
151 * when interpolating. The spacing required to reach a particular relative
152 * tolerance in the derivative depends on the quotient between the first
153 * derivative and the third derivative of the function itself.
155 * You should call this routine with the analytical derivative as the "function"
156 * parameter, and the quotient between "function and second derivative" will
157 * then correspond to the quotient bewteen the derivative and the third derivative
158 * of the actual function we want to tabulate.
160 * \param function Vector with function values
161 * \param inputSpacing Spacing between function values
162 * \param range Interval to check
164 * \return Smallest quotient found in range.
166 * \note The function vector and input spacing are always double-valued to
167 * avoid accuracy loss.
169 real findSmallestQuotientOfFunctionAndSecondDerivative(ArrayRef<const double> function,
171 const std::pair<real, real>& range);
174 /*! \brief Find smallest quotient between analytical function and its 3rd derivative
176 * Used to calculate table spacing. This function divides the function value
177 * by the second derivative (or a very small number when that is zero), and
178 * returns the smallest such quotient found in the range.
180 * Our quadratic tables corresponds to linear interpolation of the derivative,
181 * which means the derivative will typically have larger error than the value
182 * when interpolating. The spacing required to reach a particular relative
183 * tolerance in the derivative depends on the quotient between the first
184 * derivative and the third derivative of the function itself.
186 * You should call this routine with the analytical derivative as the "function"
187 * parameter, and the quotient between "function and second derivative" will
188 * then correspond to the quotient bewteen the derivative and the third derivative
189 * of the actual function we want to tabulate.
191 * Since all functions that can be tabulated efficiently are reasonably smooth,
192 * we simply check 1,000 points in the interval rather than bother about
193 * implementing any complicated global optimization scheme.
195 * \param f Analytical function
196 * \param range Interval
198 * \return Smallest quotient found in range.
200 * \note The function is always double-valued to avoid accuracy loss.
202 real findSmallestQuotientOfFunctionAndThirdDerivative(const std::function<double(double)>& f,
203 const std::pair<real, real>& range);
206 /*! \brief Find smallest quotient between function and 2nd derivative (vectors)
208 * Used to calculate table spacing. This function divides the function value
209 * by the second derivative (or a very small number when that is zero), and
210 * returns the smallest such quotient found in the range.
212 * Our quadratic tables corresponds to linear interpolation of the derivative,
213 * which means the derivative will typically have larger error than the value
214 * when interpolating. The spacing required to reach a particular relative
215 * tolerance in the derivative depends on the quotient between the first
216 * derivative and the third derivative of the function itself.
218 * You should call this routine with the analytical derivative as the "function"
219 * parameter, and the quotient between "function and second derivative" will
220 * then correspond to the quotient bewteen the derivative and the third derivative
221 * of the actual function we want to tabulate.
223 * \param function Vector with function values
224 * \param inputSpacing Spacing between function values
225 * \param range Interval to check
227 * \return Smallest quotient found in range.
229 * \note The function vector and input spacing are always double-valued to
230 * avoid accuracy loss.
232 real findSmallestQuotientOfFunctionAndThirdDerivative(ArrayRef<const double> function,
234 const std::pair<real, real>& range);
237 /*! \brief Calculate second derivative of vector and return vector of same length
239 * 5-point approximations are used, with endpoints using non-center interpolation.
241 * \param f Vector (function) for which to calculate second derivative
242 * \param spacing Spacing of input data.
244 * \throws If the input vector has fewer than five data points.
246 * \note This function always uses double precision arguments since it is meant
247 * to be used on raw user input data for tables, where we want to avoid
248 * accuracy loss (since differentiation can be numerically fragile).
250 std::vector<double> vectorSecondDerivative(ArrayRef<const double> f, double spacing);
253 /*! \brief Copy (temporary) table data into aligned multiplexed vector
255 * This routine takes the temporary data generated for a single table
256 * and writes multiplexed output into a multiple-table-data vector.
257 * If the output vector is empty we will resize it to fit the data, and
258 * otherwise we assert the size is correct to add out input data.
260 * \tparam T Type of container for input data
261 * \tparam U Type of container for output data
263 * \param[in] inputData Input data for single table
264 * \param[inout] multiplexedOutputData Multiplexed output vector, many tables.
265 * \param[in] valuesPerTablePoint Number of real values for each table point,
266 * for instance 4 in DDFZ tables.
267 * \param[in] numTables Number of tables mixed into multiplexed output
268 * \param[in] thisTableIndex Index of this table in multiplexed output
270 * \note The output container type can be different from the input since the latter
271 * sometimes uses an aligned allocator so the data can be loaded efficiently
272 * in the GROMACS nonbonded kernels.
274 template<class T, class U>
275 void fillMultiplexedTableData(const T inputData,
276 U* multiplexedOutputData,
277 std::size_t valuesPerTablePoint,
278 std::size_t numTables,
279 std::size_t thisTableIndex)
281 if (multiplexedOutputData->empty())
283 multiplexedOutputData->resize(inputData.size() * numTables);
287 GMX_ASSERT(inputData.size() * numTables == multiplexedOutputData->size(),
288 "Size mismatch when multiplexing table data");
291 GMX_ASSERT(inputData.size() % valuesPerTablePoint == 0,
292 "Input table size must be a multiple of valuesPerTablePoint");
294 std::size_t points = inputData.size() / valuesPerTablePoint;
296 for (std::size_t i = 0; i < points; i++)
298 std::size_t inputOffset = valuesPerTablePoint * i;
299 std::size_t outputOffset = valuesPerTablePoint * (numTables * i + thisTableIndex);
301 for (std::size_t j = 0; j < valuesPerTablePoint; j++)
303 (*multiplexedOutputData)[outputOffset + j] = inputData[inputOffset + j];
309 } // namespace internal
313 #endif // GMX_TABLES_SPLINEUTIL_H