#ifndef __COSMOTOOL_FOURIER_EUCLIDIAN_HPP #define __COSMOTOOL_FOURIER_EUCLIDIAN_HPP #include #include #include #include #include "base_types.hpp" #include "fft/fftw_calls.hpp" #include "../algo.hpp" namespace CosmoTool { template class EuclidianSpectrum_1D: public SpectrumFunction { public: typedef boost::function1 Function; protected: Function f; public: typedef typename SpectrumFunction::FourierMapType FourierMapType; typedef boost::shared_ptr ptr_map; EuclidianSpectrum_1D(Function P) : f(P) { } ptr_map newRandomFourier(gsl_rng *rng, const FourierMapType& like_map) const; void mul(FourierMap >& m) const; void mul_sqrt(FourierMap >& m) const; void mul_inv(FourierMap >& m) const; void mul_inv_sqrt(FourierMap >& m) const; }; template class EuclidianFourierMapBase: public FourierMap { public: typedef std::vector DimArray; private: boost::shared_ptr m_data; DimArray m_dims; long m_size; public: EuclidianFourierMapBase(boost::shared_ptr indata, const DimArray& indims) { m_data = indata; m_dims = indims; m_size = 1; for (int i = 0; i < m_dims.size(); i++) m_size *= m_dims[i]; } virtual ~EuclidianFourierMapBase() { } const DimArray& getDims() const { return m_dims; } virtual const T *data() const { return m_data.get(); } virtual T *data() { return m_data.get(); } virtual long size() const { return m_size; } virtual FourierMap *copy() const { FourierMap *m = this->mimick(); m->eigen() = this->eigen(); return m; } }; template class EuclidianFourierMapReal: public EuclidianFourierMapBase { public: typedef typename EuclidianFourierMapBase::DimArray DimArray; EuclidianFourierMapReal(boost::shared_ptr indata, const DimArray& indims) : EuclidianFourierMapBase(indata, indims) {} virtual FourierMap *mimick() const { return new EuclidianFourierMapReal( boost::shared_ptr((T *)fftw_malloc(sizeof(T)*this->size()), std::ptr_fun(fftw_free)), this->getDims()); } virtual T dot_product(const FourierMap& other) const throw(std::bad_cast) { const EuclidianFourierMapReal& m2 = dynamic_cast&>(other); if (this->size() != m2.size()) throw std::bad_cast(); return (this->eigen()*m2.eigen()).sum(); } }; template class EuclidianFourierMapComplex: public EuclidianFourierMapBase > { protected: typedef boost::shared_ptr > ptr_t; std::vector delta_k; long plane_size; public: typedef typename EuclidianFourierMapBase >::DimArray DimArray; EuclidianFourierMapComplex(ptr_t indata, const DimArray& indims, const std::vector& dk) : EuclidianFourierMapBase >(indata, indims), delta_k(dk) { assert(dk.size() == indims.size()); plane_size = 1; for (int q = 1; q < indims.size(); q++) plane_size *= indims[q]; } virtual FourierMap > *mimick() const { return new EuclidianFourierMapComplex( ptr_t((std::complex *) fftw_malloc(sizeof(std::complex)*this->size()), std::ptr_fun(fftw_free)), this->getDims(), this->delta_k); } template double get_K(const Array& ik) { const DimArray& dims = this->getDims(); assert(ik.size() == dims.size()); double k2 = 0; for (int q = 0; q < ik.size(); q++) { int dk = ik[q]; if (dk > dims[q]/2) dk = dk - dims[q]; k2 += CosmoTool::square(delta_k[q]*dk); } return std::sqrt(k2); } double get_K(long p) { const DimArray& dims = this->getDims(); DimArray d(delta_k.size()); for (int q = 0; q < d.size(); q++) { d[q] = p%dims[q]; p = (p-d[q])/dims[q]; } return get_K(d); } virtual std::complex dot_product(const FourierMap >& other) const throw(std::bad_cast) { const EuclidianFourierMapComplex& m2 = dynamic_cast&>(other); if (this->size() != m2.size()) throw std::bad_cast(); const std::complex *d1 = this->data(); const std::complex *d2 = m2.data(); const DimArray& dims = this->getDims(); int N0 = dims[0]; std::complex result = 0; for (long q0 = 1; q0 < N0-1; q0++) { for (long p = 0; p < plane_size; p++) { long idx = q0+N0*p; assert(idx < this->size()); result += 2*(conj(d1[idx]) * d2[idx]).real(); } } for (long p = 0; p < plane_size; p++) { long q0 = N0*p, q1 = (p+1)*N0-1; result += conj(d1[q0]) * d2[q0]; result += conj(d1[q1]) * d2[q1]; } return result; } }; template class EuclidianFourierTransform: public FourierTransform { public: typedef typename EuclidianFourierMapBase::DimArray DimArray; private: typedef FFTW_Calls calls; EuclidianFourierMapReal *realMap; EuclidianFourierMapComplex *fourierMap; typename calls::plan_type m_analysis, m_synthesis; double volume; long N; DimArray m_dims, m_dims_hc; std::vector m_L; public: EuclidianFourierTransform(const DimArray& dims, const std::vector& L) { assert(L.size() == dims.size()); std::vector dk(L.size()); m_dims = dims; m_dims_hc = dims; m_dims_hc[0] = dims[0]/2+1; m_L = L; N = 1; volume = 1; for (int i = 0; i < dims.size(); i++) { N *= dims[i]; volume *= L[i]; dk[i] = 2*M_PI/L[i]; } realMap = new EuclidianFourierMapReal( boost::shared_ptr(calls::alloc_real(N), std::ptr_fun(calls::free)), m_dims); fourierMap = new EuclidianFourierMapComplex( boost::shared_ptr >((std::complex*)calls::alloc_complex(N), std::ptr_fun(calls::free)), m_dims_hc, dk); m_analysis = calls::plan_dft_r2c(dims.size(), &dims[0], realMap->data(), (typename calls::complex_type *)fourierMap->data(), FFTW_MEASURE); m_synthesis = calls::plan_dft_c2r(dims.size(), &dims[0], (typename calls::complex_type *)fourierMap->data(), realMap->data(), FFTW_MEASURE); } virtual ~EuclidianFourierTransform() { delete realMap; delete fourierMap; calls::destroy_plan(m_synthesis); calls::destroy_plan(m_analysis); } void synthesis() { calls::execute(m_synthesis); realMap->scale(1/volume); } void analysis() { calls::execute(m_analysis); fourierMap->scale(volume/N); } void synthesis_conjugate() { calls::execute(m_analysis); fourierMap->scale(1/volume); } void analysis_conjugate() { calls::execute(m_synthesis); realMap->scale(volume/N); } const FourierMap >& fourierSpace() const { return *fourierMap; } FourierMap >& fourierSpace() { return *fourierMap; } const FourierMap& realSpace() const { return *realMap; } FourierMap& realSpace() { return *realMap; } FourierTransform *mimick() const { return new EuclidianFourierTransform(m_dims, m_L); } }; template class EuclidianFourierTransform_2d: public EuclidianFourierTransform { private: template static std::vector make_2d_vector(T2 a, T2 b) { T2 arr[2] = { a, b}; return std::vector(&arr[0], &arr[2]); } public: EuclidianFourierTransform_2d(int Nx, int Ny, double Lx, double Ly) : EuclidianFourierTransform(make_2d_vector(Nx, Ny), make_2d_vector(Lx, Ly)) { } virtual ~EuclidianFourierTransform_2d() {} }; template class EuclidianFourierTransform_3d: public EuclidianFourierTransform { private: template static std::vector make_3d_vector(T2 a, T2 b, T2 c) { T2 arr[2] = { a, b, c}; return std::vector(&arr[0], &arr[3]); } public: EuclidianFourierTransform_3d(int Nx, int Ny, int Nz, double Lx, double Ly, double Lz) : EuclidianFourierTransform(make_3d_vector(Nx, Ny, Nz), make_3d_vector(Lx, Ly, Lz)) { } virtual ~EuclidianFourierTransform_3d() {} }; template typename EuclidianSpectrum_1D::ptr_map EuclidianSpectrum_1D::newRandomFourier(gsl_rng *rng, const FourierMapType& like_map) const { typedef EuclidianFourierMapComplex MapT; typedef typename EuclidianSpectrum_1D::ptr_map ptr_map; typedef typename MapT::DimArray DimArray; const MapT& m_c = dynamic_cast(like_map); ptr_map ret_map = ptr_map(m_c.mimick()); MapT& rand_map = dynamic_cast(*ret_map.get()); std::complex *d = rand_map.data(); long idx; const DimArray& dims = rand_map.getDims(); long plane_size; for (long p = 1; p < m_c.size(); p++) { double A_k = std::sqrt(0.5*f(rand_map.get_K(p))); d[p] = std::complex(gsl_ran_gaussian(rng, A_k), gsl_ran_gaussian(rng, A_k)); } // Generate the mean value d[0] = std::complex(std::sqrt(f(0)), 0); // Correct the Nyquist plane idx = dims[0]-1; // Stick to the last element of the first dimension // 1D is special case if (dims.size() == 1) { d[idx] = std::complex(d[idx].real() + d[idx].imag(), 0); return ret_map; } plane_size = 1; for (int q = 1; q < dims.size(); q++) { plane_size *= dims[q]; } for (long p = 1; p < plane_size/2; p++) { long q = (p+1)*dims[0]-1; long q2 = (plane_size-p+1)*dims[0]-1; assert(q < plane_size*dims[0]); assert(q2 < plane_size*dims[0]); d[q] = conj(d[q2]); } long q = dims[0]; d[q] = std::complex(d[q].real() + d[q].imag()); return ret_map; } template void EuclidianSpectrum_1D::mul(FourierMap >& m) const { EuclidianFourierMapComplex& m_c = dynamic_cast&>(m); std::complex *d = m.data(); for (long p = 0; p < m_c.size(); p++) d[p] *= f(m_c.get_K(p)); } template void EuclidianSpectrum_1D::mul_sqrt(FourierMap >& m) const { EuclidianFourierMapComplex& m_c = dynamic_cast&>(m); std::complex *d = m.data(); for (long p = 0; p < m_c.size(); p++) d[p] *= std::sqrt(f(m_c.get_K(p))); } template void EuclidianSpectrum_1D::mul_inv(FourierMap >& m) const { EuclidianFourierMapComplex& m_c = dynamic_cast&>(m); std::complex *d = m.data(); for (long p = 0; p < m_c.size(); p++) { T A = f(m_c.get_K(p)); if (A==0) d[p] = 0; else d[p] /= A; } } template void EuclidianSpectrum_1D::mul_inv_sqrt(FourierMap >& m) const { EuclidianFourierMapComplex& m_c = dynamic_cast&>(m); std::complex *d = m.data(); for (long p = 0; p < m_c.size(); p++) { T A = std::sqrt(f(m_c.get_K(p))); if (A == 0) d[p] = 0; else d[p] /= A; } } }; #endif