Page MenuHomec4science

test_fftw.cpp
No OneTemporary

File Metadata

Created
Sat, Apr 27, 20:16

test_fftw.cpp

/*
* SPDX-License-Indentifier: AGPL-3.0-or-later
*
* Copyright (©) 2016-2021 EPFL (École Polytechnique Fédérale de Lausanne),
* Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- */
#include "fftw/fftw_engine.hh"
#include "grid.hh"
#include "grid_hermitian.hh"
#include "grid_view.hh"
#include "test.hh"
using namespace tamaas;
using fft = fftw::helper<Real>;
/* -------------------------------------------------------------------------- */
template <typename T>
struct span {
T* ptr;
std::size_t size;
~span() { fftw::free(ptr); }
const T* begin() const { return ptr; }
const T* end() const { return ptr + size; }
T* begin() { return ptr; }
T* end() { return ptr + size; }
operator T*() { return ptr; }
};
/* -------------------------------------------------------------------------- */
TEST(TestFFTEngine, FFT1D) {
constexpr UInt size = 1000;
FFTWEngine engine;
span<Real> data{fft::alloc_real(size), size};
span<fft::complex> solution{fft::alloc_complex(size / 2 + 1), size / 2 + 1};
fftw::plan<Real> solution_plan{
fftw::plan_1d_forward(size, data, solution, engine.flags())};
std::iota(data.begin(), data.end(), 0);
fftw::execute(solution_plan);
Grid<Real, 1> grid({size}, 1);
GridHermitian<Real, 1> result({size / 2 + 1}, 1);
std::iota(grid.begin(), grid.end(), 0);
engine.forward(grid, result);
ASSERT_TRUE(compare(result, solution, AreComplexEqual()))
<< "1D FFTW transform failed";
}
/* -------------------------------------------------------------------------- */
TEST(TestFFTWEngine, FFT2D) {
constexpr UInt size = 100;
constexpr UInt rsize = size * size;
constexpr UInt csize = size * (size / 2 + 1);
FFTWEngine engine;
span<Real> data{fft::alloc_real(rsize), rsize};
span<fft::complex> solution{fft::alloc_complex(csize), csize};
fftw::plan<Real> solution_plan{
fftw::plan_2d_forward(size, size, data, solution, engine.flags())};
std::iota(data.begin(), data.end(), 0);
fftw::execute(solution_plan);
Grid<Real, 2> grid({size, size}, 1);
GridHermitian<Real, 2> result({size, size / 2 + 1}, 1);
std::iota(grid.begin(), grid.end(), 0);
engine.forward(grid, result);
ASSERT_TRUE(compare(result, solution, AreComplexEqual()))
<< "2D FFTW transform failed";
}
/* -------------------------------------------------------------------------- */
TEST(TestFFTWEngine, FFT2DBackwards) {
const std::ptrdiff_t N0 = 20, N1 = 20;
Grid<Real, 2> real({N0, N1}, 1);
GridHermitian<Real, 2> spectral({N0, N1 / 2 + 1}, 1);
real = 1.;
FFTWEngine engine;
engine.forward(real, spectral);
real = 0;
engine.backward(real, spectral);
Grid<Real, 2> reference({N0, N1}, 1);
reference = 1.;
ASSERT_TRUE(compare(real, reference, AreFloatEqual()));
}
/* -------------------------------------------------------------------------- */
TEST(TestFFTWEngine, FFT1D2Comp) {
constexpr UInt size = 20;
/// 1D single component FFT should be working here
Grid<Real, 1> grid({size}, 2), data({size}, 1);
std::iota(grid.begin(), grid.end(), 0);
std::iota(data.begin(), data.end(), 0);
GridHermitian<Real, 1> result({size / 2 + 1}, 2), solution({size / 2 + 1}, 1);
FFTWEngine engine;
engine.forward(grid, result);
std::iota(data.begin(), data.end(), 0);
data *= 2;
engine.forward(data, solution);
const Real tol = 200 * std::numeric_limits<Real>::epsilon();
ASSERT_TRUE(
compare(make_component_view(result, 0), solution, AreComplexEqual{tol}))
<< "1D FFTW transform with 2 components failed on 1st component";
data += 1;
engine.forward(data, solution);
ASSERT_TRUE(
compare(make_component_view(result, 1), solution, AreComplexEqual{tol}))
<< "1D FFTW transform with 2 components failed on 2nd component";
}
/* -------------------------------------------------------------------------- */
TEST(TestFFTWEngine, FFT2D3Comp) {
constexpr UInt size = 20;
/// 2D single component FFT should be working here
Grid<Real, 2> grid({size, size}, 3), data({size, size}, 1);
std::iota(grid.begin(), grid.end(), 0);
std::iota(data.begin(), data.end(), 0);
data *= 3;
GridHermitian<Real, 2> result({size, size / 2 + 1}, 3),
solution({size, size / 2 + 1}, 1);
FFTWEngine engine;
engine.forward(grid, result);
constexpr Real tol = 5000 * std::numeric_limits<Real>::epsilon();
for (UInt i = 0; i < 3; ++i) {
engine.forward(data, solution);
ASSERT_TRUE(
compare(make_component_view(result, i), solution, AreComplexEqual{tol}))
<< "2D FFTW transform with 3 components failed on " << i
<< "th component";
data += 1;
}
}
/* -------------------------------------------------------------------------- */
TEST(TestFFTWEngine, FFT2DViewTransform) {
constexpr UInt size = 20;
Grid<Real, 2> data({size, size}, 1);
GridHermitian<Real, 2> solution({size, size / 2 + 1}, 1);
std::iota(std::begin(data), std::end(data), 0);
FFTWEngine engine;
engine.forward(data, solution);
Grid<Real, 2> grid({size, size}, 3);
auto view = make_component_view(grid, 1);
std::iota(view.begin(), view.end(), 0);
GridHermitian<Real, 2> result({size, size / 2 + 1}, 1);
engine.forward(view, result);
constexpr Real tol = 5000 * std::numeric_limits<Real>::epsilon();
ASSERT_TRUE(compare(result, solution, AreComplexEqual{tol}))
<< "Fourier transform on component view fail";
}
/* -------------------------------------------------------------------------- */
TEST(TestFFTWEngine, FFTI1D2Comp) {
constexpr UInt size = 20;
Grid<Real, 1> grid({size}, 2);
std::iota(grid.begin(), grid.end(), 0);
GridHermitian<Real, 1> grid_hermitian({size / 2 + 1}, 2);
Grid<Real, 1> result({size}, 2);
FFTWEngine engine;
engine.forward(grid, grid_hermitian);
engine.backward(result, grid_hermitian);
ASSERT_TRUE(compare(grid, result, AreFloatEqual()))
<< "1D FFTI transform with 2 components failed";
}
/* -------------------------------------------------------------------------- */
TEST(TestFFTWEngine, FFTI2D3Comp) {
constexpr UInt size = 20;
Grid<Real, 2> grid({size, size}, 3);
std::iota(grid.begin(), grid.end(), 0);
GridHermitian<Real, 2> grid_hermitian({size, size / 2 + 1}, 3);
Grid<Real, 2> result({size, size}, 3);
FFTWEngine engine;
engine.forward(grid, grid_hermitian);
engine.backward(result, grid_hermitian);
ASSERT_TRUE(compare(grid, result, AreFloatEqual()))
<< "2D FFTI transform with 3 components failed";
}

Event Timeline