446d50307f9e2bf2da800be03ed2a2550f264f2d
[alexxy/gromacs.git] / src / gromacs / fft / tests / fft.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014, 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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Tests utilities for fft calculations.
38  *
39  * Current reference data is generated in double precision using the Reference
40  * build type, except for the compiler (Apple Clang).
41  *
42  * \author Roland Schulz <roland@utk.edu>
43  * \ingroup module_fft
44  */
45 #include "gmxpre.h"
46
47 #include "gromacs/fft/fft.h"
48
49 #include <vector>
50
51 #include <gtest/gtest.h>
52
53 #include "gromacs/fft/parallel_3dfft.h"
54 #include "gromacs/utility/stringutil.h"
55
56 #include "testutils/refdata.h"
57 #include "testutils/testasserts.h"
58
59 namespace
60 {
61
62 //! Input data for FFT tests.
63 const real inputdata[] = { //print ",\n".join([",".join(["%4s"%(random.randint(-99,99)/10.,) for i in range(25)]) for j in range(20)])
64     -3.5, 6.3, 1.2, 0.3, 1.1, -5.7, 5.8, -1.9, -6.3, -1.4, 7.4, 2.4, -9.9, -7.2, 5.4, 6.1, -1.9, -7.6, 1.4, -3.5, 0.7, 5.6, -4.2, -1.1, -4.4,
65     -6.3, -7.2, 4.6, -3.0, -0.9, 7.2, 2.5, -3.6, 6.1, -3.2, -2.1, 6.5, -0.4, -9.0, 2.3, 8.4, 4.0, -5.2, -9.0, 4.7, -3.7, -2.0, -9.5, -3.9, -3.6,
66     7.1, 0.8, -0.6, 5.2, -9.3, -4.5, 5.9, 2.2, -5.8, 5.0, 1.2, -0.1, 2.2, 0.2, -7.7, 1.9, -8.4, 4.4, 2.3, -2.9, 6.7, 2.7, 5.8, -3.6, 8.9,
67     8.9, 4.3, 9.1, 9.3, -8.7, 4.1, 9.6, -6.2, 6.6, -9.3, 8.2, 4.5, 6.2, 9.4, -8.0, -6.8, -3.3, 7.2, 1.7, 0.6, -4.9, 9.8, 1.3, 3.2, -0.2,
68     9.9, 4.4, -9.9, -7.2, 4.4, 4.7, 7.2, -0.3, 0.3, -2.1, 8.4, -2.1, -6.1, 4.1, -5.9, -2.2, -3.8, 5.2, -8.2, -7.8, -8.8, 6.7, -9.5, -4.2, 0.8,
69     8.3, 5.2, -9.0, 8.7, 9.8, -9.9, -7.8, -8.3, 9.0, -2.8, -9.2, -9.6, 8.4, 2.5, 6.0, -0.4, 1.3, -0.5, 9.1, -9.5, -0.8, 1.9, -6.2, 4.3, -3.8,
70     8.6, -1.9, -2.1, -0.4, -7.1, -3.7, 9.1, -6.4, -0.6, 2.5, 8.0, -5.2, -9.8, -4.3, 4.5, 1.7, 9.3, 9.2, 1.0, 5.3, -4.5, 6.4, -6.6, 3.1, -6.8,
71     2.1, 2.0, 7.3, 8.6, 5.0, 5.2, 0.4, -7.1, 4.5, -9.2, -9.1, 0.2, -6.3, -1.1, -9.6, 7.4, -3.7, -5.5, 2.6, -3.5, -0.7, 9.0, 9.8, -8.0, 3.6,
72     3.0, -2.2, -2.8, 0.8, 9.0, 2.8, 7.7, -0.7, -5.0, -1.8, -2.3, -0.4, -6.2, -9.1, -9.2, 0.5, 5.7, -3.9, 2.1, 0.6, 0.4, 9.1, 7.4, 7.1, -2.5,
73     7.3, 7.8, -4.3, 6.3, -0.8, -3.8, -1.5, 6.6, 2.3, 3.9, -4.6, 5.8, -7.4, 5.9, 2.8, 4.7, 3.9, -5.4, 9.1, -1.6, -1.9, -4.2, -2.6, 0.6, -5.1,
74     1.8, 5.2, 4.0, -6.2, 6.5, -9.1, 0.5, 2.1, 7.1, -8.6, 7.6, -9.7, -4.6, -5.7, 6.1, -1.8, -7.3, 9.4, 8.0, -2.6, -1.8, 5.7, 9.3, -7.9, 7.4,
75     6.3, 2.0, 9.6, -4.5, -6.2, 6.1, 2.3, 0.8, 5.9, -2.8, -3.5, -1.5, 6.0, -4.9, 3.5, 7.7, -4.2, -9.7, 2.4, 8.1, 5.9, 3.4, -7.5, 7.5, 2.6,
76     4.7, 2.7, 2.2, 2.6, 6.2, 7.5, 0.2, -6.4, -2.8, -0.5, -0.3, 0.4, 1.2, 3.5, -4.0, -0.5, 9.3, -7.2, 8.5, -5.5, -1.7, -5.3, 0.3, 3.9, -3.6,
77     -3.6, 4.7, -8.1, 1.4, 4.0, 1.3, -4.3, -8.8, -7.3, 6.3, -7.5, -9.0, 9.1, 4.5, -1.9, 1.9, 9.9, -1.7, -9.1, -5.1, 8.5, -9.3, 2.1, -5.8, -3.6,
78     -0.8, -0.9, -3.3, -2.7, 7.0, -7.2, -5.0, 7.4, -1.4, 0.0, -4.5, -9.7, 0.7, -1.0, -9.1, -5.3, 4.3, 3.4, -6.6, 9.8, -1.1, 8.9, 5.0, 2.9, 0.2,
79     -2.9, 0.8, 6.7, -0.6, 0.6, 4.1, 5.3, -1.7, -0.3, 4.2, 3.7, -8.3, 4.0, 1.3, 6.3, 0.2, 1.3, -1.1, -3.5, 2.8, -7.7, 6.2, -4.9, -9.9, 9.6,
80     3.0, -9.2, -8.0, -3.9, 7.9, -6.1, 6.0, 5.9, 9.6, 1.2, 6.2, 3.6, 2.1, 5.8, 9.2, -8.8, 8.8, -3.3, -9.2, 4.6, 1.8, 4.6, 2.9, -2.7, 4.2,
81     7.3, -0.4, 7.7, -7.0, 2.1, 0.3, 3.7, 3.3, -8.6, 9.8, 3.6, 3.1, 6.5, -2.4, 7.8, 7.5, 8.4, -2.8, -6.3, -5.1, -2.7, 9.3, -0.8, -9.2, 7.9,
82     8.9, 3.4, 0.1, -5.3, -6.8, 4.9, 4.3, -0.7, -2.2, -3.2, -7.5, -2.3, 0.0, 8.1, -9.2, -2.3, -5.7, 2.1, 2.6, 2.0, 0.3, -8.0, -2.0, -7.9, 6.6,
83     8.4, 4.0, -6.2, -6.9, -7.2, 7.7, -5.0, 5.3, 1.9, -5.3, -7.5, 8.8, 8.3, 9.0, 8.1, 3.2, 1.2, -5.4, -0.2, 2.1, -5.2, 9.5, 5.9, 5.6, -7.8,
84 };
85
86
87 class BaseFFTTest : public ::testing::Test
88 {
89     public:
90         BaseFFTTest()
91             : checker_(data_.rootChecker()), flags_(GMX_FFT_FLAG_CONSERVATIVE)
92         {
93             // TODO: These tolerances are just something that has been observed
94             // to be sufficient to pass the tests.  It would be nicer to
95             // actually argue about why they are sufficient (or what is).
96 #ifdef GMX_DOUBLE
97             checker_.setDefaultTolerance(gmx::test::relativeRealTolerance(10.0, 512));
98 #else
99             checker_.setDefaultTolerance(gmx::test::relativeRealTolerance(10.0, 64));
100 #endif
101         }
102         ~BaseFFTTest()
103         {
104             gmx_fft_cleanup();
105         }
106
107         gmx::test::TestReferenceData    data_;
108         gmx::test::TestReferenceChecker checker_;
109         std::vector<real>               in_, out_;
110         int                             flags_;
111 };
112
113 class FFTTest : public BaseFFTTest
114 {
115     public:
116         FFTTest() : fft_(NULL)
117         {
118         }
119         ~FFTTest()
120         {
121             if (fft_)
122             {
123                 gmx_fft_destroy(fft_);
124             }
125         }
126         gmx_fft_t fft_;
127 };
128
129 class ManyFFTTest : public BaseFFTTest
130 {
131     public:
132         ManyFFTTest() : fft_(NULL)
133         {
134         }
135         ~ManyFFTTest()
136         {
137             if (fft_)
138             {
139                 gmx_many_fft_destroy(fft_);
140             }
141         }
142         gmx_fft_t fft_;
143 };
144
145
146 //TODO: Add tests for aligned/not-aligned input/output memory
147
148 class FFTTest1D : public FFTTest, public ::testing::WithParamInterface<int>
149 {
150
151 };
152
153 class FFFTest3D : public BaseFFTTest
154 {
155     public:
156         FFFTest3D() : fft_(NULL)
157         {
158         }
159         ~FFFTest3D()
160         {
161             if (fft_)
162             {
163                 gmx_parallel_3dfft_destroy(fft_);
164             }
165         }
166         gmx_parallel_3dfft_t fft_;
167 };
168
169
170 TEST_P(FFTTest1D, Complex)
171 {
172     const int nx = GetParam();
173     ASSERT_LE(nx*2, static_cast<int>(sizeof(inputdata)/sizeof(real)));
174
175     in_  = std::vector<real>(inputdata, inputdata+nx*2);
176     out_ = std::vector<real>(nx*2);
177     real* in  = &in_[0];
178     real* out = &out_[0];
179
180     gmx_fft_init_1d(&fft_, nx, flags_);
181
182     gmx_fft_1d(fft_, GMX_FFT_FORWARD, in, out);
183     checker_.checkSequenceArray(nx*2, out, "forward");
184     gmx_fft_1d(fft_, GMX_FFT_BACKWARD, in, out);
185     checker_.checkSequenceArray(nx*2, out, "backward");
186 }
187
188 TEST_P(FFTTest1D, Real)
189 {
190     const int rx = GetParam();
191     const int cx = (rx/2+1);
192     ASSERT_LE(cx*2, static_cast<int>(sizeof(inputdata)/sizeof(real)));
193
194     in_  = std::vector<real>(inputdata, inputdata+cx*2);
195     out_ = std::vector<real>(cx*2);
196     real* in  = &in_[0];
197     real* out = &out_[0];
198
199     gmx_fft_init_1d_real(&fft_, rx, flags_);
200
201     gmx_fft_1d_real(fft_, GMX_FFT_REAL_TO_COMPLEX, in, out);
202     checker_.checkSequenceArray(cx*2, out, "forward");
203     gmx_fft_1d_real(fft_, GMX_FFT_COMPLEX_TO_REAL, in, out);
204     checker_.checkSequenceArray(rx, out, "backward");
205 }
206
207 INSTANTIATE_TEST_CASE_P(7_8_25_36_60,
208                         FFTTest1D, ::testing::Values(7, 8, 25, 36, 60));
209
210
211 TEST_F(ManyFFTTest, Complex1DLength48Multi5Test)
212 {
213     const int nx = 48;
214     const int N  = 5;
215
216     in_  = std::vector<real>(inputdata, inputdata+nx*2*N);
217     out_ = std::vector<real>(nx*2*N);
218     real* in  = &in_[0];
219     real* out = &out_[0];
220
221     gmx_fft_init_many_1d(&fft_, nx, N, flags_);
222
223     gmx_fft_many_1d(fft_, GMX_FFT_FORWARD, in, out);
224     checker_.checkSequenceArray(nx*2*N, out, "forward");
225     gmx_fft_many_1d(fft_, GMX_FFT_BACKWARD, in, out);
226     checker_.checkSequenceArray(nx*2*N, out, "backward");
227 }
228
229 TEST_F(ManyFFTTest, Real1DLength48Multi5Test)
230 {
231     const int rx = 48;
232     const int cx = (rx/2+1);
233     const int N  = 5;
234
235     in_  = std::vector<real>(inputdata, inputdata+cx*2*N);
236     out_ = std::vector<real>(cx*2*N);
237     real* in  = &in_[0];
238     real* out = &out_[0];
239
240     gmx_fft_init_many_1d_real(&fft_, rx, N, flags_);
241
242     gmx_fft_many_1d_real(fft_, GMX_FFT_REAL_TO_COMPLEX, in, out);
243     checker_.checkSequenceArray(cx*2*N, out, "forward");
244     gmx_fft_many_1d_real(fft_, GMX_FFT_COMPLEX_TO_REAL, in, out);
245     checker_.checkSequenceArray(rx*N, out, "backward");
246 }
247
248 TEST_F(FFTTest, Real2DLength18_15Test)
249 {
250     const int rx = 18;
251     const int cx = (rx/2+1);
252     const int ny = 15;
253
254     in_  = std::vector<real>(inputdata, inputdata+cx*2*ny);
255     out_ = std::vector<real>(cx*2*ny);
256     real* in  = &in_[0];
257     real* out = &out_[0];
258
259     gmx_fft_init_2d_real(&fft_, rx, ny, flags_);
260
261     gmx_fft_2d_real(fft_, GMX_FFT_REAL_TO_COMPLEX, in, out);
262     checker_.checkSequenceArray(cx*2*ny, out, "forward");
263 //    known to be wrong for gmx_fft_mkl. And not used.
264 //    gmx_fft_2d_real(_fft,GMX_FFT_COMPLEX_TO_REAL,in,out);
265 //    _checker.checkSequenceArray(rx*ny, out, "backward");
266 }
267
268 //TODO: test with threads and more than 1 MPI ranks
269 TEST_F(FFFTest3D, Real5_6_9)
270 {
271     int        ndata[] = {5, 6, 9};
272     MPI_Comm   comm[]  = {MPI_COMM_NULL, MPI_COMM_NULL};
273     real     * rdata;
274     t_complex* cdata;
275     ivec       local_ndata, offset, rsize, csize, complex_order;
276
277     gmx_parallel_3dfft_init(&fft_, ndata, &rdata, &cdata,
278                             comm, TRUE, 1);
279
280     gmx_parallel_3dfft_real_limits(fft_, local_ndata, offset, rsize);
281     gmx_parallel_3dfft_complex_limits(fft_, complex_order,
282                                       local_ndata, offset, csize);
283     checker_.checkVector(rsize, "rsize");
284     checker_.checkVector(csize, "csize");
285     int size = csize[0]*csize[1]*csize[2];
286
287     memcpy(rdata, inputdata, size*sizeof(t_complex));
288     gmx_parallel_3dfft_execute(fft_, GMX_FFT_REAL_TO_COMPLEX, 0, NULL);
289     //TODO use std::complex and add checkComplex for it
290     checker_.checkSequenceArray(size*2,
291                                 reinterpret_cast<real*>(cdata), "forward");
292
293     memcpy(cdata, inputdata, size*sizeof(t_complex));
294     gmx_parallel_3dfft_execute(fft_, GMX_FFT_COMPLEX_TO_REAL, 0, NULL);
295     for (int i = 0; i < ndata[0]*ndata[1]; i++) //check sequence but skip unused data
296     {
297         checker_.checkSequenceArray(ndata[2], rdata+i*rsize[2],
298                                     gmx::formatString("backward %d", i).c_str());
299     }
300 }
301
302 } // namespace