Fixes for Bluegene/Q
[gromacs.git] / include / gmx_simd_math_single.h
blob28feeaa07519c1a4fdc468cb421344d32b530751
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * 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.
35 #ifndef _gmx_simd_math_single_h_
36 #define _gmx_simd_math_single_h_
39 /* 1.0/sqrt(x) */
40 static gmx_inline gmx_mm_pr
41 gmx_invsqrt_pr(gmx_mm_pr x)
43 const gmx_mm_pr half = gmx_set1_pr(0.5);
44 const gmx_mm_pr one = gmx_set1_pr(1.0);
46 gmx_mm_pr lu = gmx_rsqrt_pr(x);
48 return gmx_madd_pr(gmx_nmsub_pr(x, gmx_mul_pr(lu, lu), one), gmx_mul_pr(lu, half), lu);
52 /* 1.0/x */
53 static gmx_inline gmx_mm_pr
54 gmx_inv_pr(gmx_mm_pr x)
56 const gmx_mm_pr two = gmx_set1_pr(2.0);
58 gmx_mm_pr lu = gmx_rcp_pr(x);
60 return gmx_mul_pr(lu, gmx_nmsub_pr(lu, x, two));
64 /* Calculate the force correction due to PME analytically.
66 * This routine is meant to enable analytical evaluation of the
67 * direct-space PME electrostatic force to avoid tables.
69 * The direct-space potential should be Erfc(beta*r)/r, but there
70 * are some problems evaluating that:
72 * First, the error function is difficult (read: expensive) to
73 * approxmiate accurately for intermediate to large arguments, and
74 * this happens already in ranges of beta*r that occur in simulations.
75 * Second, we now try to avoid calculating potentials in Gromacs but
76 * use forces directly.
78 * We can simply things slight by noting that the PME part is really
79 * a correction to the normal Coulomb force since Erfc(z)=1-Erf(z), i.e.
81 * V= 1/r - Erf(beta*r)/r
83 * The first term we already have from the inverse square root, so
84 * that we can leave out of this routine.
86 * For pme tolerances of 1e-3 to 1e-8 and cutoffs of 0.5nm to 1.8nm,
87 * the argument beta*r will be in the range 0.15 to ~4. Use your
88 * favorite plotting program to realize how well-behaved Erf(z)/z is
89 * in this range!
91 * We approximate f(z)=erf(z)/z with a rational minimax polynomial.
92 * However, it turns out it is more efficient to approximate f(z)/z and
93 * then only use even powers. This is another minor optimization, since
94 * we actually WANT f(z)/z, because it is going to be multiplied by
95 * the vector between the two atoms to get the vectorial force. The
96 * fastest flops are the ones we can avoid calculating!
98 * So, here's how it should be used:
100 * 1. Calculate r^2.
101 * 2. Multiply by beta^2, so you get z^2=beta^2*r^2.
102 * 3. Evaluate this routine with z^2 as the argument.
103 * 4. The return value is the expression:
106 * 2*exp(-z^2) erf(z)
107 * ------------ - --------
108 * sqrt(Pi)*z^2 z^3
110 * 5. Multiply the entire expression by beta^3. This will get you
112 * beta^3*2*exp(-z^2) beta^3*erf(z)
113 * ------------------ - ---------------
114 * sqrt(Pi)*z^2 z^3
116 * or, switching back to r (z=r*beta):
118 * 2*beta*exp(-r^2*beta^2) erf(r*beta)
119 * ----------------------- - -----------
120 * sqrt(Pi)*r^2 r^3
123 * With a bit of math exercise you should be able to confirm that
124 * this is exactly D[Erf[beta*r]/r,r] divided by r another time.
126 * 6. Add the result to 1/r^3, multiply by the product of the charges,
127 * and you have your force (divided by r). A final multiplication
128 * with the vector connecting the two particles and you have your
129 * vectorial force to add to the particles.
132 static gmx_mm_pr
133 gmx_pmecorrF_pr(gmx_mm_pr z2)
135 const gmx_mm_pr FN6 = gmx_set1_pr(-1.7357322914161492954e-8f);
136 const gmx_mm_pr FN5 = gmx_set1_pr(1.4703624142580877519e-6f);
137 const gmx_mm_pr FN4 = gmx_set1_pr(-0.000053401640219807709149f);
138 const gmx_mm_pr FN3 = gmx_set1_pr(0.0010054721316683106153f);
139 const gmx_mm_pr FN2 = gmx_set1_pr(-0.019278317264888380590f);
140 const gmx_mm_pr FN1 = gmx_set1_pr(0.069670166153766424023f);
141 const gmx_mm_pr FN0 = gmx_set1_pr(-0.75225204789749321333f);
143 const gmx_mm_pr FD4 = gmx_set1_pr(0.0011193462567257629232f);
144 const gmx_mm_pr FD3 = gmx_set1_pr(0.014866955030185295499f);
145 const gmx_mm_pr FD2 = gmx_set1_pr(0.11583842382862377919f);
146 const gmx_mm_pr FD1 = gmx_set1_pr(0.50736591960530292870f);
147 const gmx_mm_pr FD0 = gmx_set1_pr(1.0f);
149 gmx_mm_pr z4;
150 gmx_mm_pr polyFN0, polyFN1, polyFD0, polyFD1;
152 z4 = gmx_mul_pr(z2, z2);
154 polyFD0 = gmx_madd_pr(FD4, z4, FD2);
155 polyFD1 = gmx_madd_pr(FD3, z4, FD1);
156 polyFD0 = gmx_madd_pr(polyFD0, z4, FD0);
157 polyFD0 = gmx_madd_pr(polyFD1, z2, polyFD0);
159 polyFD0 = gmx_inv_pr(polyFD0);
161 polyFN0 = gmx_madd_pr(FN6, z4, FN4);
162 polyFN1 = gmx_madd_pr(FN5, z4, FN3);
163 polyFN0 = gmx_madd_pr(polyFN0, z4, FN2);
164 polyFN1 = gmx_madd_pr(polyFN1, z4, FN1);
165 polyFN0 = gmx_madd_pr(polyFN0, z4, FN0);
166 polyFN0 = gmx_madd_pr(polyFN1, z2, polyFN0);
168 return gmx_mul_pr(polyFN0, polyFD0);
172 /* Calculate the potential correction due to PME analytically.
174 * See gmx_pmecorrF_pr() for details about the approximation.
176 * This routine calculates Erf(z)/z, although you should provide z^2
177 * as the input argument.
179 * Here's how it should be used:
181 * 1. Calculate r^2.
182 * 2. Multiply by beta^2, so you get z^2=beta^2*r^2.
183 * 3. Evaluate this routine with z^2 as the argument.
184 * 4. The return value is the expression:
187 * erf(z)
188 * --------
191 * 5. Multiply the entire expression by beta and switching back to r (z=r*beta):
193 * erf(r*beta)
194 * -----------
197 * 6. Add the result to 1/r, multiply by the product of the charges,
198 * and you have your potential.
200 static gmx_mm_pr
201 gmx_pmecorrV_pr(gmx_mm_pr z2)
203 const gmx_mm_pr VN6 = gmx_set1_pr(1.9296833005951166339e-8f);
204 const gmx_mm_pr VN5 = gmx_set1_pr(-1.4213390571557850962e-6f);
205 const gmx_mm_pr VN4 = gmx_set1_pr(0.000041603292906656984871f);
206 const gmx_mm_pr VN3 = gmx_set1_pr(-0.00013134036773265025626f);
207 const gmx_mm_pr VN2 = gmx_set1_pr(0.038657983986041781264f);
208 const gmx_mm_pr VN1 = gmx_set1_pr(0.11285044772717598220f);
209 const gmx_mm_pr VN0 = gmx_set1_pr(1.1283802385263030286f);
211 const gmx_mm_pr VD3 = gmx_set1_pr(0.0066752224023576045451f);
212 const gmx_mm_pr VD2 = gmx_set1_pr(0.078647795836373922256f);
213 const gmx_mm_pr VD1 = gmx_set1_pr(0.43336185284710920150f);
214 const gmx_mm_pr VD0 = gmx_set1_pr(1.0f);
216 gmx_mm_pr z4;
217 gmx_mm_pr polyVN0, polyVN1, polyVD0, polyVD1;
219 z4 = gmx_mul_pr(z2, z2);
221 polyVD1 = gmx_madd_pr(VD3, z4, VD1);
222 polyVD0 = gmx_madd_pr(VD2, z4, VD0);
223 polyVD0 = gmx_madd_pr(polyVD1, z2, polyVD0);
225 polyVD0 = gmx_inv_pr(polyVD0);
227 polyVN0 = gmx_madd_pr(VN6, z4, VN4);
228 polyVN1 = gmx_madd_pr(VN5, z4, VN3);
229 polyVN0 = gmx_madd_pr(polyVN0, z4, VN2);
230 polyVN1 = gmx_madd_pr(polyVN1, z4, VN1);
231 polyVN0 = gmx_madd_pr(polyVN0, z4, VN0);
232 polyVN0 = gmx_madd_pr(polyVN1, z2, polyVN0);
234 return gmx_mul_pr(polyVN0, polyVD0);
238 #endif /* _gmx_simd_math_single_h_ */