From c762dab4c6c17d6f847c369223bea5d5b7e9b7ee Mon Sep 17 00:00:00 2001 From: Jean-Matthieu Etancelin <jean-matthieu.etancelin@univ-reims.fr> Date: Thu, 7 Jan 2016 14:38:54 +0100 Subject: [PATCH] Add new 3D scalar field spectrum computation (not validated but seems ok) --- HySoP/hysop/f2py/fftw2py.f90 | 18 +++++ HySoP/hysop/operator/discrete/spectrum.py | 84 +++++++++++++++++++++ HySoP/hysop/operator/spectrum.py | 34 +++++++++ HySoP/hysop/operator/tests/test_spectrum.py | 44 +++++++++++ HySoP/src/fftw/fft3d.f90 | 66 +++++++++++++++- 5 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 HySoP/hysop/operator/discrete/spectrum.py create mode 100644 HySoP/hysop/operator/spectrum.py create mode 100644 HySoP/hysop/operator/tests/test_spectrum.py diff --git a/HySoP/hysop/f2py/fftw2py.f90 b/HySoP/hysop/f2py/fftw2py.f90 index 655982667..040262cf5 100755 --- a/HySoP/hysop/f2py/fftw2py.f90 +++ b/HySoP/hysop/f2py/fftw2py.f90 @@ -307,5 +307,23 @@ contains end subroutine solve_curl_2d + !> Compute spectrum of a scalar field + !! @param[in] field + !! @param[out] spectrum + subroutine spectrum_3d(field, spectrum, wavelengths, ghosts, length) + real(pk),dimension(:,:,:),intent(in):: field + integer, dimension(3), intent(in) :: ghosts + real(pk),dimension(:), intent(inout) :: spectrum + real(pk),dimension(:), intent(inout) :: wavelengths + real(pk),intent(in) :: length + !f2py intent(in) :: field + !f2py intent(inout) :: spectrum + !f2py intent(inout) :: wavelengths + + call r2c_3d_scal(field, ghosts) + + call filter_spectrum_3d(spectrum, wavelengths, length) + + end subroutine spectrum_3d end module fftw2py diff --git a/HySoP/hysop/operator/discrete/spectrum.py b/HySoP/hysop/operator/discrete/spectrum.py new file mode 100644 index 000000000..72502d24b --- /dev/null +++ b/HySoP/hysop/operator/discrete/spectrum.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +""" +@file spectrum.py +Discrete Spectrum operator using FFTW (fortran) +""" +try: + from hysop.f2py import fftw2py +except ImportError: + from hysop.fakef2py import fftw2py +from hysop.operator.discrete.discrete import DiscreteOperator +from hysop.constants import debug, np, HYSOP_MPI_REAL +from hysop.tools.profiler import profile +from hysop.tools.io_utils import IO +import hysop.tools.numpywrappers as npw +from hysop.mpi import MPI +import os + + +class FFTSpectrum(DiscreteOperator): + """ + Discretized Spectrum operator based on FFTW. + + """ + @debug + def __init__(self, field, prefix=None, **kwds): + """ + Constructor. + @param[in] vorticity : field to compute. + """ + # Discretisation of the input field + self.field = field + + if self.field.nb_components > 1: + raise AttributeError("Vector case not yet implemented.") + # Base class initialisation + assert 'variables' not in kwds, 'variables parameter is useless.' + super(FFTSpectrum, self).__init__(variables=[field], + **kwds) + self.input = [self.field] + l = np.min(self.field.topology.mesh.discretization.resolution) + self._tmp = npw.zeros(((l - 1) / 2, )) + self._kx = npw.zeros(((l - 1) / 2, )) + self.res = npw.zeros(((l - 1) / 2, )) + self._prefix = "spectrum" if prefix is None else prefix + IO.check_dir(os.path.join(IO.default_path(), + self._prefix + "_00000.dat")) + + @debug + @profile + def apply(self, simulation=None): + assert simulation is not None, \ + "Missing dt value for diffusion computation." + ite = simulation.currentIteration + ghosts = self.field.topology.ghosts() + if self.field.dimension == 3: + fftw2py.spectrum_3d(self.field.data[0], + self._tmp, self._kx, + ghosts, np.min(self.domain.length)) + if self.field.topology.size == 1: + self.res[...] = self._tmp + else: + self.field.topology.comm.Reduce( + [self._tmp, self.res.shape[0], HYSOP_MPI_REAL], + [self.res, self.res.shape[0], HYSOP_MPI_REAL], + op=MPI.SUM, root=0) + if self.field.topology.rank == 0: + _file = open(os.path.join( + IO.default_path(), + self._prefix + "_{0:05d}.dat".format(ite)), 'w') + for i in xrange(self.res.shape[0]): + _file.write("{0} {1}\n".format( + self._kx[i], self.res[i])) + _file.close() + else: + raise ValueError("invalid problem dimension") + + def finalize(self): + """ + Clean memory (fftw plans and so on) + """ + pass + # TODO : fix bug that occurs when several finalize + # of fft operators are called. + # fftw2py.clean_fftw_solver(self.vorticity.dimension) diff --git a/HySoP/hysop/operator/spectrum.py b/HySoP/hysop/operator/spectrum.py new file mode 100644 index 000000000..1b4f5c1b0 --- /dev/null +++ b/HySoP/hysop/operator/spectrum.py @@ -0,0 +1,34 @@ +""" +@file spectrum.py +""" +from hysop.operator.computational import Computational +from hysop.operator.discrete.spectrum import FFTSpectrum +from hysop.constants import debug +from hysop.operator.continuous import opsetup + + +class Spectrum(Computational): + """ + Fourier spectrum computation of a scalar field. + """ + + def __init__(self, field, prefix=None, **kwds): + """ + Constructor for Spectrum operator + @param[in] field : field to compute + """ + super(Spectrum, self).__init__(variables=[field], **kwds) + self.field = field + self.input = [field] + self._prefix = prefix + + def discretize(self): + super(Spectrum, self)._fftw_discretize() + + @debug + @opsetup + def setup(self, rwork=None, iwork=None): + self.discrete_op = FFTSpectrum(self.discreteFields[self.field], + method=self.method, + prefix=self._prefix) + self._is_uptodate = True diff --git a/HySoP/hysop/operator/tests/test_spectrum.py b/HySoP/hysop/operator/tests/test_spectrum.py new file mode 100644 index 000000000..0ca10a859 --- /dev/null +++ b/HySoP/hysop/operator/tests/test_spectrum.py @@ -0,0 +1,44 @@ +from hysop.domain.box import Box +from hysop.constants import np, HDF5 +from hysop.fields.continuous import Field +from hysop.operator.spectrum import Spectrum +from hysop.tools.parameters import Discretization, IOParams +from hysop.problem.simulation import Simulation +from hysop.operator.hdf_io import HDF_Writer +pi = np.pi +sin = np.sin +cos = np.cos + + +def computeScal(res, x, y, z, t): + res[0][...] = z * sin((3*pi*x)*(2*pi*y)) + return res + + +def test_spectrum(): + dom = Box(origin=[0., 0., 0.], length=[1., 1., 1.]) + field = Field(domain=dom, name='Field', + is_vector=False, formula=computeScal) + d3D = Discretization([257, 257, 257]) + + op = Spectrum(field, discretization=d3D) + op.discretize() + op.setup() + topo = op.discreteFields[field].topology + field.initialize(topo=topo) + + prt = HDF_Writer(variables={field: topo, }, + io_params=IOParams(frequency=1, + filename='field', + fileformat=HDF5)) + prt.discretize() + prt.setup() + + simu = Simulation(nbIter=1) + simu.initialize() + op.apply(simu) + prt.apply(simu) + + +if __name__ == '__main__': + test_spectrum() diff --git a/HySoP/src/fftw/fft3d.f90 b/HySoP/src/fftw/fft3d.f90 index 1527da3dd..280b93c02 100755 --- a/HySoP/src/fftw/fft3d.f90 +++ b/HySoP/src/fftw/fft3d.f90 @@ -26,7 +26,7 @@ module fft3d getParamatersTopologyFFTW3d,filter_poisson_3d,filter_curl_diffusion_3d, & init_r2c_3d_many, r2c_3d_many, c2r_3d_many, filter_diffusion_3d_many,& filter_poisson_3d_many, filter_diffusion_3d, filter_curl_3d, filter_projection_om_3d,& - filter_multires_om_3d, filter_pressure_3d + filter_multires_om_3d, filter_pressure_3d, r2c_3d_scal, filter_spectrum_3d !> plan for fftw "c2c" forward or r2c transform type(C_PTR) :: plan_forward1, plan_forward2, plan_forward3 @@ -1038,4 +1038,68 @@ contains end subroutine getParamatersTopologyFFTW3d + !> forward transform - The result is saved in local buffers + !! @param[in] field 3d scalar field, x-component of the input vector field + !! @param[in] ghosts, number of points in the ghost layer of input fields. + subroutine r2c_3d_scal(field, ghosts) + + real(mk),dimension(:,:,:),intent(in) :: field + integer, dimension(3), intent(in) :: ghosts + !real(8) :: start + integer(C_INTPTR_T) :: i,j,k, ig, jg, kg + + ! ig, jg, kg are used to take into account + ! ghost points in input fields + ! init + do k =1, local_resolution(c_Z) + kg = k + ghosts(c_Z) + do j = 1, fft_resolution(c_Y) + jg = j + ghosts(c_Y) + do i = 1, fft_resolution(c_X) + ig = i + ghosts(c_X) + rdatain1(i,j,k) = field(ig,jg,kg) + end do + end do + end do + + ! compute transforms for each component + !start = MPI_WTIME() + call fftw_mpi_execute_dft_r2c(plan_forward1, rdatain1, dataout1) + !!print *, "r2c time = ", MPI_WTIME() - start + + end subroutine r2c_3d_scal + + !> Compute spectrum of the given data + subroutine filter_spectrum_3d(spectrum,wavelengths,length) + + real(mk),dimension(:),intent(out) :: spectrum + real(mk),dimension(:),intent(out) :: wavelengths + real(mk),intent(in) :: length + integer(C_INTPTR_T) :: i,j,k + real(mk) :: c + real(mk) :: kk,dk,kc,eps + integer(C_INTPTR_T) :: ik + spectrum = 0 + dk = 2.0_mk*pi/length + kc = pi*fft_resolution(c_X)/length + eps=kc/1000000.0_mk + + !! mind the transpose -> index inversion between y and z + c = 1._mk/real(fft_resolution(c_Z)*fft_resolution(c_Y)*fft_resolution(c_X),mk) + c = c * c + do j = 1,local_resolution(c_Y) + do k = 1, fft_resolution(c_Z) + do i = 1, local_resolution(c_X) + kk=sqrt(kx(i)**2+ky(j)**2+kz(k)**2) + if ((kk.gt.eps).and.(kk.le.kc)) then + ik=1+int(kk/dk+0.5_mk) + spectrum(ik) = spectrum(ik) + real(dataout1(i,j,k)*conjg(dataout1(i,j,k)), mk) * c + end if + end do + end do + end do + wavelengths(:) = kx + + end subroutine filter_spectrum_3d + end module fft3d -- GitLab