FML
Namespaces | Typedefs | Functions
FML::INTERPOLATION Namespace Reference

This namespace deals with interpolation and density assignment. More...

Namespaces

namespace  SPLINE
 This namespace deals with creating and using splines.
 

Typedefs

using FloatType = FML::GRID::FloatType
 
template<int N>
using FFTWGrid = FML::GRID::FFTWGrid< N >
 

Functions

template<int N, int ORDER, class T >
void interpolate_grid_to_particle_positions (const FFTWGrid< N > &grid, const T *part, size_t NumPart, std::vector< FloatType > &interpolated_values)
 Interpolate a grid to a set of positions given by the positions of particles. More...
 
template<int N, int ORDER, class T >
void interpolate_grid_vector_to_particle_positions (const std::array< FFTWGrid< N >, N > &grid_vec, const T *part, size_t NumPart, std::array< std::vector< FloatType >, N > &interpolated_values_vec)
 Interpolate a vector of grids (e.g. More...
 
template<int N, class T >
void interpolate_grid_to_particle_positions (const FFTWGrid< N > &grid, const T *part, size_t NumPart, std::vector< FloatType > &interpolated_values, std::string interpolation_method)
 Interpolate a grid to a set of positions given by the positions of particles. More...
 
template<int N, class T >
void interpolate_grid_vector_to_particle_positions (const std::array< FFTWGrid< N >, N > &grid_vec, const T *part, size_t NumPart, std::array< std::vector< FloatType >, N > &interpolated_values_vec, std::string interpolation_method)
 Interpolate a vector of grids to a set of positions given by the positions of particles. More...
 
template<int N, class T >
void particles_to_grid (const T *part, size_t NumPart, size_t NumPartTot, FFTWGrid< N > &density, std::string density_assignment_method)
 Assign particles to a grid to compute the over density field delta. More...
 
template<int N, int ORDER, class T >
void particles_to_grid (const T *part, size_t NumPart, size_t NumPartTot, FFTWGrid< N > &density)
 Assign particles to a grid to compute the over density field delta. More...
 
template<int N, class T >
void particles_to_fourier_grid_interlacing (T *part, size_t NumPart, size_t NumPartTot, FFTWGrid< N > &density_grid_fourier, std::string density_assignment_method)
 Internal method. More...
 
template<int N, class T >
void particles_to_fourier_grid (T *part, size_t NumPart, size_t NumPartTot, FFTWGrid< N > &density_grid_fourier, std::string density_assignment_method, bool interlacing)
 Assign particles to grid to compute the over density. More...
 
template<int N, int ORDER>
void convolve_grid_with_kernel (const FFTWGrid< N > &grid_in, FFTWGrid< N > &grid_out, std::function< FloatType(std::array< double, N > &)> &convolution_kernel)
 Convolve a grid with a kernel. More...
 
int interpolation_order_from_name (std::string density_assignment_method)
 Get the interpolation order from a string holding the density_assignment_method (NGP, CIC, ...). More...
 
std::pair< int, int > get_extra_slices_needed_for_density_assignment (std::string density_assignment_method)
 Compute how many extra slices we need in the FFTWGrid for a given density assignement / interpolation method. More...
 
template<int ORDER>
std::pair< int, int > get_extra_slices_needed_by_order ()
 Compute how many extra slices we need in the FFTWGrid for a given density assignment order. More...
 
template<int ORDER>
double kernel (double x)
 Internal method. More...
 
template<>
double kernel< 1 > (double x)
 The NGP kernel. More...
 
template<>
double kernel< 2 > (double x)
 The CIC kernel. More...
 
template<>
double kernel< 3 > (double x)
 The TSC kernel. More...
 
template<>
double kernel< 4 > (double x)
 The PCS kernel. More...
 
template<>
double kernel< 5 > (double x)
 The PQS kernel. More...
 
template<int N>
void add_contribution_from_extra_slices (FFTWGrid< N > &density)
 Internal method. For communication between tasks needed when adding particles to grid. More...
 
template<int N>
std::function< double(std::array< double, N > &)> get_window_function (std::string density_assignment_method, int Ngrid)
 This returns the a function giving the window function for a given density assignement method as function of the wave-vector in dimensionless units. More...
 
template<int N>
void deconvolve_window_function_fourier (FFTWGrid< N > &fourier_grid, std::string density_assignment_method)
 Deconvolves the density assignement kernel in Fourier space. More...
 

Detailed Description

This namespace deals with interpolation and density assignment.

We give methods to assign particles to a grid to compute the density contrast and to interpolate a grid to any given position using the same B spline kernels (this is basically a convolution of the kernels with the grid). This kind of interpolation is useful for computing forces from a density field of particles. Using the interpolation method corresponding to the density assignment help prevent unphysical self-forces.

If the particle class has a get_mass method then we will use this mass (divided by the mean mass of all particles so the absolute size of the masses does not matter) when assigning the particles to the grid. Otherwise we will assume all particles have the same mass. The resulting density-field delta will always have mean 0.

The assignment function is a B spline kernel of any order, i.e. H*H*...*H with H being a tophat and * convolution. The fourier space window functions of these are just sinc(pi/2 * k / kny)^ORDER Order 1=NGP, 2=CIC, 3=TSC, 4=PCS, 5=PQS and higher orders are easily added if you for some strange reason needs this (just add the kernel function).

Also contains a method for doing a convolution of a grid with a general kernel.

Compile time defines:

DEBUG_INTERPOL : Check that the interpolation weights sum to unity for density assignment

CELLCENTERSHIFTED : Shift the position of the cell (located at center of cell vs at the corners). Use with care. Not using this option saves a slice for even order interpoation and using it saves a slice for odd ordered interpolation (TSC+). Only relevant if memory is really tight and you need to use TSC or PQS

Typedef Documentation

◆ FFTWGrid

template<int N>
using FML::INTERPOLATION::FFTWGrid = typedef FML::GRID::FFTWGrid<N>

Definition at line 58 of file ParticleGridInterpolation.h.

◆ FloatType

using FML::INTERPOLATION::FloatType = typedef FML::GRID::FloatType

Definition at line 55 of file ParticleGridInterpolation.h.

Function Documentation

◆ add_contribution_from_extra_slices()

template<int N>
void FML::INTERPOLATION::add_contribution_from_extra_slices ( FFTWGrid< N > &  density)

Internal method. For communication between tasks needed when adding particles to grid.

Definition at line 911 of file ParticleGridInterpolation.h.

911 {
912
913 auto Local_nx = density.get_local_nx();
914 auto num_cells_slice = density.get_ntot_real_slice_alloc();
915 int n_extra_left = density.get_n_extra_slices_left();
916 int n_extra_right = density.get_n_extra_slices_right();
917 ;
918
919 std::vector<FloatType> buffer(num_cells_slice);
920
921 // [1] Send to the right, recieve from left
922 for (int i = 0; i < n_extra_right; i++) {
923 FloatType * extra_slice_right = density.get_real_grid_right() + num_cells_slice * i;
924 FloatType * slice_left = density.get_real_grid() + num_cells_slice * i;
925 FloatType * temp = buffer.data();
926
927#ifdef USE_MPI
928 MPI_Status status;
929
930 int send_to = (ThisTask + 1) % NTasks;
931 int recv_from = (ThisTask - 1 + NTasks) % NTasks;
932
933 MPI_Sendrecv(&(extra_slice_right[0]),
934 int(sizeof(FloatType) * num_cells_slice),
935 MPI_CHAR,
936 send_to,
937 0,
938 &(temp[0]),
939 int(sizeof(FloatType) * num_cells_slice),
940 MPI_CHAR,
941 int(recv_from),
942 0,
943 MPI_COMM_WORLD,
944 &status);
945#else
946 temp = extra_slice_right;
947#endif
948
949 // Copy over data from temp
950 for (int j = 0; j < num_cells_slice; j++) {
951 slice_left[j] += (temp[j] + 1.0);
952 }
953 }
954
955 // [2] Send to the left, recieve from right
956 for (int i = 1; i <= n_extra_left; i++) {
957 FloatType * extra_slice_left = density.get_real_grid() - i * num_cells_slice;
958 FloatType * slice_right = density.get_real_grid() + num_cells_slice * (Local_nx - i);
959 FloatType * temp = buffer.data();
960
961#ifdef USE_MPI
962 MPI_Status status;
963
964 int send_to = (ThisTask - 1 + NTasks) % NTasks;
965 int recv_from = (ThisTask + 1) % NTasks;
966
967 MPI_Sendrecv(&(extra_slice_left[0]),
968 int(sizeof(FloatType) * num_cells_slice),
969 MPI_CHAR,
970 send_to,
971 0,
972 &(temp[0]),
973 int(sizeof(FloatType) * num_cells_slice),
974 MPI_CHAR,
975 recv_from,
976 0,
977 MPI_COMM_WORLD,
978 &status);
979#else
980 temp = extra_slice_left;
981#endif
982
983 // Copy over data from temp
984 for (int j = 0; j < num_cells_slice; j++) {
985 slice_right[j] += (temp[j] + 1.0);
986 }
987 }
988 }
float FloatType
Definition: FFTWGlobal.h:29
FloatType * get_real_grid()
The left most slice (slice ix = -nleft_extra,...,-2,-1)
Definition: FFTWGrid.h:444
ptrdiff_t get_ntot_real_slice_alloc() const
The number of cells per slice that we alloc. Useful to jump from slice to slice.
Definition: FFTWGrid.h:583
ptrdiff_t get_local_nx() const
Number of local x-slices in the real grid.
Definition: FFTWGrid.h:1079
FloatType * get_real_grid_right()
The main grid (slice ix = 0...NLocal_x-1)
Definition: FFTWGrid.h:458
int get_n_extra_slices_right() const
How many extra slices we have allocated to the right.
Definition: FFTWGrid.h:1265
int get_n_extra_slices_left() const
How many extra slices we have allocated to the left.
Definition: FFTWGrid.h:1260
FML::GRID::FloatType FloatType
int ThisTask
The MPI task number.
Definition: Global.cpp:33
int NTasks
Total number of MPI tasks.
Definition: Global.cpp:34

◆ convolve_grid_with_kernel()

template<int N, int ORDER>
void FML::INTERPOLATION::convolve_grid_with_kernel ( const FFTWGrid< N > &  grid_in,
FFTWGrid< N > &  grid_out,
std::function< FloatType(std::array< double, N > &)> &  convolution_kernel 
)

Convolve a grid with a kernel.

Template Parameters
NThe dimension of the grid
ORDERThe width of the kernel in units of the cell-size. We consider the \( {\rm ORDER}^{\rm N} \) closest grid-cells, so ORDER/2 cells to the left and right in each dimension.
Parameters
[in]grid_inThe grid we want to convolve.
[out]grid_outThe in grid convolved with the kernel.
[in]convolution_kernelA function taking in the distance to a given cell (in units of the cell size) and returns the kernel. For example the NGP kernel would be the function Prod_i ( |dx[i]| < 0.5 ), i.e. 1 if all positions are within half a grid cell of the cell center.

Definition at line 1005 of file ParticleGridInterpolation.h.

1007 {
1008
1009 auto nextra = get_extra_slices_needed_by_order<ORDER>();
1010 assert_mpi(grid_in.get_n_extra_slices_left() >= nextra.first and
1011 grid_in.get_n_extra_slices_right() >= nextra.second,
1012 "[convolve_grid_with_kernel] Too few extra slices\n");
1013 assert_mpi(grid_in.get_nmesh() > 0, "[convolve_grid_with_kernel] Grid has to be already allocated!\n");
1014
1015 // We need to look at width^N cells in total
1016 constexpr int widthtondim = FML::power(ORDER, N);
1017 std::array<int, N> xstart;
1018 if (ORDER % 2 == 0)
1019 xstart.fill(-ORDER / 2 + 1);
1020 else
1021 xstart.fill(-ORDER / 2);
1022
1023 // Fetch grid information
1024 const int Nmesh = grid_in.get_nmesh();
1025 const auto Local_nx = grid_in.get_local_nx();
1026 const auto Local_x_start = grid_in.get_local_x_start();
1027
1028 // Make outputgrid (this initializes it to zero)
1029 grid_out = FFTWGrid<N>(Nmesh, grid_in.get_n_extra_slices_left(), grid_in.get_n_extra_slices_right());
1030
1031 // Loop over all cells in in-grid
1032#ifdef USE_OMP
1033#pragma omp parallel for
1034#endif
1035 for (int islice = 0; islice < Local_nx; islice++) {
1036 std::array<double, N> dx;
1037 for (auto && real_index : grid_in.get_real_range(islice, islice + 1)) {
1038
1039 // Coordinate of cell
1040 auto ix = grid_in.coord_from_index(real_index);
1041
1042 // Neighbor coord
1043 [[maybe_unused]] std::array<int, N> ix_nbor;
1044 ix_nbor[0] = ix[0];
1045 for (int idim = 1; idim < N; idim++) {
1046 ix_nbor[idim] = ix[idim] + 1;
1047 if (ix_nbor[idim] >= Nmesh)
1048 ix_nbor[idim] -= Nmesh;
1049 }
1050
1051 // Interpolation
1052 FloatType value = 0;
1053 for (int i = 0; i < widthtondim; i++) {
1054 std::array<int, N> icoord;
1055 double w = 1.0;
1056 if constexpr (ORDER == 1) {
1057 icoord = ix;
1058 } else {
1059 for (int idim = 0, n = 1; idim < N; idim++, n *= ORDER) {
1060 int go_left_right_or_stay = xstart[idim] + (i / n % ORDER);
1061 ix_nbor[idim] = ix[idim] + go_left_right_or_stay;
1062 dx[idim] = go_left_right_or_stay;
1063 }
1064 w = convolution_kernel(dx);
1065
1066 // Periodic BC
1067 icoord[0] = ix_nbor[0];
1068 for (int idim = 1; idim < N; idim++) {
1069 icoord[idim] = ix_nbor[idim];
1070 if (icoord[idim] >= Nmesh)
1071 icoord[idim] -= Nmesh;
1072 if (icoord[idim] < 0)
1073 icoord[idim] += Nmesh;
1074 }
1075 }
1076
1077 // Add up
1078 value += w * grid_in.get_real(icoord);
1079 }
1080
1081 // Store the interpolated value
1082 grid_out.set_real(ix, value);
1083 }
1084 }
1085 }
const int Nmesh
Definition: Main.cpp:48
Class for holding grids and performing real-to-complex and complex-to-real FFTs using FFTW with MPI.
Definition: FFTWGrid.h:88
int get_nmesh() const
Number of grid-cells per dimension.
Definition: FFTWGrid.h:1054
ptrdiff_t get_local_x_start() const
The x-slice the first local slice has in the global grid.
Definition: FFTWGrid.h:1084
FloatType get_real(const std::array< int, N > &coord) const
Fetch value in grid by (local) integer coordinate.
Definition: FFTWGrid.h:1000
void set_real(const std::array< int, N > &coord, const FloatType value)
Set value in grid from (local) integer coordinate in Local_nx x [0,Nmesh)^(Ndim-1)
Definition: FFTWGrid.h:1008
RealRange get_real_range(int islice_begin=0, int islice_end=0) const
Range iterator for going through all active cells in the main real real grid by index [ e....
Definition: FFTWGrid.h:400
constexpr long long int power(int base, int exponent)
Simple integer a^b power-function by squaring.
Definition: Global.h:187

◆ deconvolve_window_function_fourier()

template<int N>
void FML::INTERPOLATION::deconvolve_window_function_fourier ( FFTWGrid< N > &  fourier_grid,
std::string  density_assignment_method 
)

Deconvolves the density assignement kernel in Fourier space.

We divide the fourier grid by the FFT of the density assignment kernels \( FFT[ H*H*H*...*H ] = FT[H]^p\).

Template Parameters
NThe dimension of the grid
Parameters
[out]fourier_gridThe Fourier grid of the density contrast that we will deconvolve.
[in]density_assignment_methodThe density assignment method (NGP, CIC, ...) we used when making the density contrast.

Definition at line 425 of file ParticleGridInterpolation.h.

425 {
426
427 const auto Ngrid = fourier_grid.get_nmesh();
428 const auto Local_nx = fourier_grid.get_local_nx();
429
430 assert_mpi(Ngrid > 0, "[deconvolve_window_function_fourier] Ngrid must be positive\n");
431
432 // The order of the method
433 const int p = interpolation_order_from_name(density_assignment_method);
434
435 // Just sinc to the power = order to the method
436 const double knyquist = M_PI * Ngrid;
437 auto window_function = [&](std::array<double, N> & kvec) -> double {
438 double w = 1.0;
439 for (int idim = 0; idim < N; idim++) {
440 const double koverkny = M_PI / 2. * (kvec[idim] / knyquist);
441 w *= koverkny == 0.0 ? 1.0 : std::sin(koverkny) / (koverkny);
442 }
443 // res = pow(w,p);
444 double res = 1;
445 for (int i = 0; i < p; i++)
446 res *= w;
447 return res;
448 };
449
450#ifdef USE_OMP
451#pragma omp parallel for
452#endif
453 for (int islice = 0; islice < Local_nx; islice++) {
454 for (auto && fourier_index : fourier_grid.get_fourier_range(islice, islice + 1)) {
455 auto kvec = fourier_grid.get_fourier_wavevector_from_index(fourier_index);
456 auto w = window_function(kvec);
457 auto value = fourier_grid.get_fourier_from_index(fourier_index);
458 fourier_grid.set_fourier_from_index(fourier_index, value / FML::GRID::FloatType(w));
459 }
460 }
461 }
void set_fourier_from_index(const IndexIntType ind, const ComplexType value)
Set value of cell in fourier grid using (local) index.
Definition: FFTWGrid.h:1036
ComplexType get_fourier_from_index(const IndexIntType index) const
Fetch value in fourier grid by (local) index.
Definition: FFTWGrid.h:1031
std::array< double, N > get_fourier_wavevector_from_index(const IndexIntType index) const
Get the wave-vector of a grid-cell in Fourier space (for physical [k] multiply by 1/Boxsize)
Definition: FFTWGrid.h:1185
FourierRange get_fourier_range(int islice_begin=0, int islice_end=0) const
Range iterator for going through all active cells in the main fourier grid by index [ e....
Definition: FFTWGrid.h:419
int interpolation_order_from_name(std::string density_assignment_method)
Get the interpolation order from a string holding the density_assignment_method (NGP,...

◆ get_extra_slices_needed_by_order()

template<int ORDER>
std::pair< int, int > FML::INTERPOLATION::get_extra_slices_needed_by_order ( )
inline

Compute how many extra slices we need in the FFTWGrid for a given density assignment order.

Template Parameters
ORDERThe order of the B-spline assignment kernel (NGP=1, CIC=2, TSC=3, PCS=4, PQS=5, ...)
Returns
The number of left and right slices.

Definition at line 322 of file ParticleGridInterpolation.h.

322 {
323 if (ORDER == 1)
324 return {0, 0};
325#ifdef CELLCENTERSHIFTED
326 if (ORDER % 2 == 1)
327 return {ORDER / 2, ORDER / 2};
328 if (ORDER % 2 == 0)
329 return {ORDER / 2, ORDER / 2};
330#else
331 if (ORDER % 2 == 1)
332 return {ORDER / 2, ORDER / 2 + 1};
333 if (ORDER % 2 == 0)
334 return {ORDER / 2 - 1, ORDER / 2};
335#endif
336 return {0, 0};
337 }

◆ get_extra_slices_needed_for_density_assignment()

std::pair< int, int > FML::INTERPOLATION::get_extra_slices_needed_for_density_assignment ( std::string  density_assignment_method)
inline

Compute how many extra slices we need in the FFTWGrid for a given density assignement / interpolation method.

Parameters
[in]density_assignment_methodThe density assignement method (NGP, CIC, TSC, PCS or PQS).
Returns
The number of left and right slices.

Example usage:

auto nleftright = get_extra_slices_needed_for_density_assignment("CIC");

FFTWGrid<N> grid (Nmesh, nleftright.first, nleftright.second);

Definition at line 288 of file ParticleGridInterpolation.h.

288 {
289 int p = 0;
290 if (density_assignment_method.compare("NGP") == 0)
291 p = 1;
292 if (density_assignment_method.compare("CIC") == 0)
293 p = 2;
294 if (density_assignment_method.compare("TSC") == 0)
295 p = 3;
296 if (density_assignment_method.compare("PCS") == 0)
297 p = 4;
298 if (density_assignment_method.compare("PQS") == 0)
299 p = 5;
300 assert_mpi(p > 0, "[extra_slices_needed_density_assignment] Unknown density assignment method\n");
301 if (p == 1)
302 return {0, 0};
303#ifdef CELLCENTERSHIFTED
304 if (p % 2 == 1)
305 return {p / 2, p / 2};
306 if (p % 2 == 0)
307 return {p / 2, p / 2};
308#else
309 if (p % 2 == 1)
310 return {p / 2, p / 2 + 1};
311 if (p % 2 == 0)
312 return {p / 2 - 1, p / 2};
313#endif
314 return {0, 0};
315 }

◆ get_window_function()

template<int N>
std::function< double(std::array< double, N > &)> FML::INTERPOLATION::get_window_function ( std::string  density_assignment_method,
int  Ngrid 
)

This returns the a function giving the window function for a given density assignement method as function of the wave-vector in dimensionless units.

Template Parameters
NThe dimension of the grid
Parameters
[in]density_assignment_methodThe density assignment method (NGP, CIC, ...) we used when making the density contrast.
[in]NgridThe grid size (used to set the nyquist frequency)

Definition at line 391 of file ParticleGridInterpolation.h.

392 {
393
394 assert_mpi(Ngrid > 0, "[get_window_function_fourier] Ngrid must be positive\n");
395
396 // The order of the method
397 const int p = interpolation_order_from_name(density_assignment_method);
398
399 // Just sinc to the power = order to the method
400 const double knyquist = M_PI * Ngrid;
401 std::function<double(std::array<double, N> &)> window_function = [=](std::array<double, N> & kvec) {
402 double w = 1.0;
403 for (int idim = 0; idim < N; idim++) {
404 const double koverkny = M_PI / 2. * (kvec[idim] / knyquist);
405 w *= koverkny == 0.0 ? 1.0 : std::sin(koverkny) / (koverkny);
406 }
407 // res = pow(w,p);
408 double res = 1;
409 for (int i = 0; i < p; i++)
410 res *= w;
411 return res;
412 };
413
414 return window_function;
415 }

◆ interpolate_grid_to_particle_positions() [1/2]

template<int N, int ORDER, class T >
void FML::INTERPOLATION::interpolate_grid_to_particle_positions ( const FFTWGrid< N > &  grid,
const T *  part,
size_t  NumPart,
std::vector< FloatType > &  interpolated_values 
)

Interpolate a grid to a set of positions given by the positions of particles.

Template Parameters
NThe dimension of the grid
TThe particle class. Must have a get_pos() method.
ORDERThe order of the B-spline interpolation (1=NGP, 2=CIC, 3=TSC, 4=PCS, 5=PQS, ...)
Parameters
[in]gridA grid.
[in]partA pointer the first particle.
[in]NumPartHow many particles/positions we have that we want to interpolate the grid to.
[out]interpolated_valuesA vector with the interpolated values, one per particle. Allocated in the method.

Definition at line 777 of file ParticleGridInterpolation.h.

780 {
781
782 auto nextra = get_extra_slices_needed_by_order<ORDER>();
783 assert_mpi(grid.get_nmesh() > 0,
784 "[interpolate_grid_to_particle_positions] Grid has to be already allocated!\n");
785 assert_mpi(grid.get_n_extra_slices_left() >= nextra.first and
786 grid.get_n_extra_slices_right() >= nextra.second,
787 "[interpolate_grid_to_particle_positions] Too few extra slices\n");
788
789 // We need to look at width^N cells in total
790 constexpr int widthtondim = FML::power(ORDER, N);
791
792 // Fetch grid information
793 const auto Local_nx = grid.get_local_nx();
794 const auto Local_x_start = grid.get_local_x_start();
795 const int Nmesh = grid.get_nmesh();
796
797 // Allocate memory needed
798 interpolated_values.resize(NumPart);
799
800#ifdef USE_OMP
801#pragma omp parallel for
802#endif
803 for (size_t ind = 0; ind < NumPart; ind++) {
804
805 // Positions in global grid in units of [Nmesh]
806 const auto * pos = FML::PARTICLE::GetPos(const_cast<T *>(part)[ind]);
807 std::array<double, N> x;
808 for (int idim = 0; idim < N; idim++)
809 x[idim] = pos[idim] * Nmesh;
810
811 // Nearest grid-node in grid
812 // Also do some santity checks. Probably better to throw here if these tests kick in
813 std::array<int, N> ix;
814 [[maybe_unused]] std::array<int, N> ix_nbor;
815 for (int idim = 0; idim < N; idim++) {
816 ix[idim] = int(x[idim]);
817 if (idim == 0) {
818 if (ix[0] == (Local_x_start + Local_nx))
819 ix[0] = int(Local_x_start + Local_nx) - 1;
820 if (ix[0] < Local_x_start)
821 ix[0] = int(Local_x_start);
822 } else {
823 if (ix[idim] == Nmesh)
824 ix[idim] = Nmesh - 1;
825 }
826 }
827
828 // Positions to distance from neareste grid-node
829 for (int idim = 0; idim < N; idim++) {
830 x[idim] -= ix[idim];
831 }
832
833 // From global ix to local ix
834 ix[0] -= int(Local_x_start);
835
836 // If we are on the left or right of the cell determines how many cells
837 // we have to go left and right
838 std::array<int, N> xstart;
839 if (ORDER % 2 == 0) {
840 for (int idim = 0; idim < N; idim++) {
841 xstart[idim] = -ORDER / 2 + 1;
842#ifdef CELLCENTERSHIFTED
843 xstart[idim] = -ORDER / 2;
844 if (x[idim] > 0.5)
845 xstart[idim] += 1;
846#endif
847 }
848 } else {
849#ifndef CELLCENTERSHIFTED
850 for (int idim = 0; idim < N; idim++) {
851 xstart[idim] = -ORDER / 2;
852 if (x[idim] > 0.5)
853 xstart[idim] += 1;
854 }
855#endif
856 }
857
858 // Interpolation
859 FloatType value = 0;
860 double sumweight = 0;
861 for (int i = 0; i < widthtondim; i++) {
862 double w = 1.0;
863 std::array<int, N> icoord;
864 if constexpr (ORDER == 1) {
865 icoord = ix;
866 } else {
867 for (int idim = 0, n = 1; idim < N; idim++, n *= ORDER) {
868 int go_left_right_or_stay = xstart[idim] + (i / n % ORDER);
869 ix_nbor[idim] = ix[idim] + go_left_right_or_stay;
870#ifdef CELLCENTERSHIFTED
871 double dx = std::fabs(-x[idim] + go_left_right_or_stay + 0.5);
872#else
873 double dx = std::fabs(-x[idim] + go_left_right_or_stay);
874#endif
875 w *= kernel<ORDER>(dx);
876 }
877
878 // Periodic BC
879 icoord[0] = ix_nbor[0];
880 for (int idim = 1; idim < N; idim++) {
881 icoord[idim] = ix_nbor[idim];
882 if (icoord[idim] >= Nmesh)
883 icoord[idim] -= Nmesh;
884 if (icoord[idim] < 0)
885 icoord[idim] += Nmesh;
886 }
887 }
888
889 // Add up
890 value += grid.get_real(icoord) * w;
891 sumweight += w;
892 }
893
894#ifdef DEBUG_INTERPOL
895 // Check that the weights sum up to unity
896 assert_mpi(std::fabs(sumweight - 1.0) < 1e-3,
897 "[interpolate_grid_to_particle_positions] Possible problem with interpolation: weights does "
898 "not sum to unity!");
899#endif
900
901 // Store the interpolated value
902 interpolated_values[ind] = value;
903 }
904 }
get_vel constexpr double * GetPos(...)
void grid(bool flag)
x
Definition: test.py:18

◆ interpolate_grid_to_particle_positions() [2/2]

template<int N, class T >
void FML::INTERPOLATION::interpolate_grid_to_particle_positions ( const FFTWGrid< N > &  grid,
const T *  part,
size_t  NumPart,
std::vector< FloatType > &  interpolated_values,
std::string  interpolation_method 
)

Interpolate a grid to a set of positions given by the positions of particles.

Template Parameters
NThe dimension of the grid
TThe particle class. Must have a get_pos() method.
Parameters
[in]gridA grid.
[in]partA pointer the first particle.
[in]NumPartHow many particles/positions we have that we want to interpolate the grid to.
[out]interpolated_valuesA vector with the interpolated values, one per particle. Allocated in the method.
[in]interpolation_methodThe interpolation method: NGP, CIC, TSC, PCS or PQS.

Definition at line 242 of file ParticleGridInterpolation.h.

246 {
247 if (interpolation_method.compare("NGP") == 0)
248 interpolate_grid_to_particle_positions<N, 1, T>(grid, part, NumPart, interpolated_values);
249 if (interpolation_method.compare("CIC") == 0)
250 interpolate_grid_to_particle_positions<N, 2, T>(grid, part, NumPart, interpolated_values);
251 if (interpolation_method.compare("TSC") == 0)
252 interpolate_grid_to_particle_positions<N, 3, T>(grid, part, NumPart, interpolated_values);
253 if (interpolation_method.compare("PCS") == 0)
254 interpolate_grid_to_particle_positions<N, 4, T>(grid, part, NumPart, interpolated_values);
255 if (interpolation_method.compare("PQS") == 0)
256 interpolate_grid_to_particle_positions<N, 5, T>(grid, part, NumPart, interpolated_values);
257 }
const std::string interpolation_method
Definition: Main.cpp:64

◆ interpolate_grid_vector_to_particle_positions() [1/2]

template<int N, int ORDER, class T >
void FML::INTERPOLATION::interpolate_grid_vector_to_particle_positions ( const std::array< FFTWGrid< N >, N > &  grid_vec,
const T *  part,
size_t  NumPart,
std::array< std::vector< FloatType >, N > &  interpolated_values_vec 
)

Interpolate a vector of grids (e.g.

force vector) to a set of positions given by the positions of particles.

Template Parameters
NThe dimension of the grid
TThe particle class. Must have a get_pos() method.
ORDERThe order of the B-spline interpolation (1=NGP, 2=CIC, 3=TSC, 4=PCS, 5=PQS, ...)
Parameters
[in]grid_vecA N-dimensional array of grids
[in]partA pointer the first particle.
[in]NumPartHow many particles/positions we have that we want to interpolate the grid to.
[out]interpolated_values_vecThe interpolated values, one per grid per particle. Allocated in the method.

Definition at line 620 of file ParticleGridInterpolation.h.

623 {
624
625 auto nextra = get_extra_slices_needed_by_order<ORDER>();
626 assert_mpi(grid_vec.size() > 0,
627 "[interpolate_grid_to_particle_positions] Grid vector has to be already allocated!\n");
628 for (auto & g : grid_vec) {
629 assert_mpi(g.get_nmesh() > 0,
630 "[interpolate_grid_to_particle_positions] All grids has to be already allocated!\n");
631 assert_mpi(g.get_nmesh() == grid_vec[0].get_nmesh(),
632 "[interpolate_grid_to_particle_positions] All grids has to have the same size!\n");
633 }
634 for (auto & g : grid_vec) {
635 assert_mpi(g.get_n_extra_slices_left() >= nextra.first and
636 g.get_n_extra_slices_right() >= nextra.second,
637 "[interpolate_grid_to_particle_positions] Too few extra slices in some of the grids\n");
638 }
639
640 // We need to look at width^N cells in total
641 constexpr int widthtondim = FML::power(ORDER, N);
642
643 // Fetch grid information
644 const auto Local_nx = grid_vec[0].get_local_nx();
645 const auto Local_x_start = grid_vec[0].get_local_x_start();
646 const int Nmesh = grid_vec[0].get_nmesh();
647
648 // Allocate memory needed
649 for (auto & i : interpolated_values_vec) {
650 if (i.size() < NumPart)
651 i.resize(NumPart);
652 }
653
654#ifdef USE_OMP
655#pragma omp parallel for
656#endif
657 for (size_t ind = 0; ind < NumPart; ind++) {
658
659 // Positions in global grid in units of [Nmesh]
660 const auto * pos = FML::PARTICLE::GetPos(const_cast<T *>(part)[ind]);
661 std::array<double, N> x;
662 for (int idim = 0; idim < N; idim++)
663 x[idim] = pos[idim] * Nmesh;
664
665 // Nearest grid-node in grid
666 // Also do some santity checks. Probably better to throw here if these tests kick in
667 std::array<int, N> ix, ix_nbor;
668 for (int idim = 0; idim < N; idim++) {
669 ix[idim] = int(x[idim]);
670 if (idim == 0) {
671 if (ix[0] == (Local_x_start + Local_nx))
672 ix[0] = int(Local_x_start + Local_nx) - 1;
673 if (ix[0] < Local_x_start)
674 ix[0] = int(Local_x_start);
675 } else {
676 if (ix[idim] == Nmesh)
677 ix[idim] = Nmesh - 1;
678 }
679 }
680
681 // Positions to distance from neareste grid-node
682 for (int idim = 0; idim < N; idim++) {
683 x[idim] -= ix[idim];
684 }
685
686 // From global ix to local ix
687 ix[0] -= int(Local_x_start);
688
689 // If we are on the left or right of the cell determines how many cells
690 // we have to go left and right
691 std::array<int, N> xstart;
692 if (ORDER % 2 == 0) {
693 for (int idim = 0; idim < N; idim++) {
694 xstart[idim] = -ORDER / 2 + 1;
695#ifdef CELLCENTERSHIFTED
696 xstart[idim] = -ORDER / 2;
697 if (x[idim] > 0.5)
698 xstart[idim] += 1;
699#endif
700 }
701 } else {
702#ifndef CELLCENTERSHIFTED
703 for (int idim = 0; idim < N; idim++) {
704 xstart[idim] = -ORDER / 2;
705 if (x[idim] > 0.5)
706 xstart[idim] += 1;
707 }
708#endif
709 }
710
711 // Interpolation
712 std::array<double, N> value;
713 value.fill(0.0);
714 double sumweight = 0;
715 for (int i = 0; i < widthtondim; i++) {
716 double w = 1.0;
717 for (int idim = 0, n = 1; idim < N; idim++, n *= ORDER) {
718 int go_left_right_or_stay = ORDER == 1 ? 0 : xstart[idim] + (i / n % ORDER);
719 ix_nbor[idim] = ix[idim] + go_left_right_or_stay;
720#ifdef CELLCENTERSHIFTED
721 double dx = std::fabs(-x[idim] + go_left_right_or_stay + 0.5);
722#else
723 double dx = std::fabs(-x[idim] + go_left_right_or_stay);
724#endif
725 w *= kernel<ORDER>(dx);
726 }
727
728 // Periodic BC
729 std::array<int, N> icoord;
730 icoord[0] = ix_nbor[0];
731 for (int idim = 1; idim < N; idim++) {
732 icoord[idim] = ix_nbor[idim];
733 if (icoord[idim] >= Nmesh)
734 icoord[idim] -= Nmesh;
735 if (icoord[idim] < 0)
736 icoord[idim] += Nmesh;
737 }
738
739 // Add up
740 for (int idim = 0; idim < N; idim++)
741 value[idim] += grid_vec[idim].get_real(icoord) * w;
742 sumweight += w;
743 }
744
745#ifdef DEBUG_INTERPOL
746 // Check that the weights sum up to unity
747 assert_mpi(std::fabs(sumweight - 1.0) < 1e-3,
748 "[interpolate_grid_to_particle_positions] Possible problem with interpolation: weights does "
749 "not sum to unity!");
750#endif
751
752 // Store the interpolated value
753 for (int idim = 0; idim < N; idim++)
754 interpolated_values_vec[idim][ind] = value[idim];
755 }
756 }

◆ interpolate_grid_vector_to_particle_positions() [2/2]

template<int N, class T >
void FML::INTERPOLATION::interpolate_grid_vector_to_particle_positions ( const std::array< FFTWGrid< N >, N > &  grid_vec,
const T *  part,
size_t  NumPart,
std::array< std::vector< FloatType >, N > &  interpolated_values_vec,
std::string  interpolation_method 
)

Interpolate a vector of grids to a set of positions given by the positions of particles.

Template Parameters
NThe dimension of the grid
TThe particle class. Must have a get_pos() method.
Parameters
[in]grid_vecA vector of grids
[in]partA pointer the first particle.
[in]NumPartHow many particles/positions we have that we want to interpolate the grid to.
[out]interpolated_values_vecThe interpolated values, one per grid per particle. Allocated in the method.
[in]interpolation_methodThe interpolation method: NGP, CIC, TSC, PCS or PQS.

Definition at line 759 of file ParticleGridInterpolation.h.

763 {
764 if (interpolation_method.compare("NGP") == 0)
765 interpolate_grid_vector_to_particle_positions<N, 1, T>(grid, part, NumPart, interpolated_values);
766 if (interpolation_method.compare("CIC") == 0)
767 interpolate_grid_vector_to_particle_positions<N, 2, T>(grid, part, NumPart, interpolated_values);
768 if (interpolation_method.compare("TSC") == 0)
769 interpolate_grid_vector_to_particle_positions<N, 3, T>(grid, part, NumPart, interpolated_values);
770 if (interpolation_method.compare("PCS") == 0)
771 interpolate_grid_vector_to_particle_positions<N, 4, T>(grid, part, NumPart, interpolated_values);
772 if (interpolation_method.compare("PQS") == 0)
773 interpolate_grid_vector_to_particle_positions<N, 5, T>(grid, part, NumPart, interpolated_values);
774 }

◆ interpolation_order_from_name()

int FML::INTERPOLATION::interpolation_order_from_name ( std::string  density_assignment_method)
inline

Get the interpolation order from a string holding the density_assignment_method (NGP, CIC, ...).

Needed for the Fourier-space window function

Definition at line 261 of file ParticleGridInterpolation.h.

261 {
262 if (density_assignment_method.compare("NGP") == 0)
263 return 1;
264 if (density_assignment_method.compare("CIC") == 0)
265 return 2;
266 if (density_assignment_method.compare("TSC") == 0)
267 return 3;
268 if (density_assignment_method.compare("PCS") == 0)
269 return 4;
270 if (density_assignment_method.compare("PQS") == 0)
271 return 5;
272 assert_mpi(false, "[interpolation_order_from_name] Unknown density assignment method\n");
273 return 0;
274 }

◆ kernel()

template<int ORDER>
double FML::INTERPOLATION::kernel ( double  x)
inline

Internal method.

The B-spline interpolation kernels for a given order \( H^{(p)} = H * H * \ldots * H \) where H is the tophat \( H = [ |dx| < 0.5 ? 1 : 0 ] \) and * is a convolution (easily computed with Mathematica)

Definition at line 344 of file ParticleGridInterpolation.h.

344 {
345 static_assert(ORDER > 0 and ORDER <= 5, "Error: kernel order is not implemented\n");
346 return 0.0 / 0.0;
347 }

◆ kernel< 1 >()

template<>
double FML::INTERPOLATION::kernel< 1 > ( double  x)
inline

The NGP kernel.

Definition at line 350 of file ParticleGridInterpolation.h.

350 {
351 return (x <= 0.5) ? 1.0 : 0.0;
352 }

◆ kernel< 2 >()

template<>
double FML::INTERPOLATION::kernel< 2 > ( double  x)
inline

The CIC kernel.

Definition at line 355 of file ParticleGridInterpolation.h.

355 {
356 return (x < 1.0) ? 1.0 - x : 0.0;
357 }

◆ kernel< 3 >()

template<>
double FML::INTERPOLATION::kernel< 3 > ( double  x)
inline

The TSC kernel.

Definition at line 360 of file ParticleGridInterpolation.h.

360 {
361 return (x < 0.5) ? 0.75 - x * x : (x < 1.5 ? 0.5 * (1.5 - x) * (1.5 - x) : 0.0);
362 }

◆ kernel< 4 >()

template<>
double FML::INTERPOLATION::kernel< 4 > ( double  x)
inline

The PCS kernel.

Definition at line 365 of file ParticleGridInterpolation.h.

365 {
366 return (x < 1.0) ? 2.0 / 3.0 + x * x * (-1.0 + 0.5 * x) :
367 ((x < 2.0) ? (2 - x) * (2 - x) * (2 - x) / 6.0 : 0.0);
368 }

◆ kernel< 5 >()

template<>
double FML::INTERPOLATION::kernel< 5 > ( double  x)
inline

The PQS kernel.

Definition at line 371 of file ParticleGridInterpolation.h.

371 {
372 return (x < 0.5) ?
373 115.0 / 192.0 + 0.25 * x * x * (x * x - 2.5) :
374 ((x < 1.5) ?
375 (55 + 4 * x * (5 - 2 * x * (15 + 2 * (-5 + x) * x))) / 96.0 :
376 ((x < 2.5) ? (5 - 2.0 * x) * (5 - 2.0 * x) * (5 - 2.0 * x) * (5 - 2.0 * x) / 384. : 0.0));
377 }

◆ particles_to_fourier_grid()

template<int N, class T >
void FML::INTERPOLATION::particles_to_fourier_grid ( T *  part,
size_t  NumPart,
size_t  NumPartTot,
FFTWGrid< N > &  density_grid_fourier,
std::string  density_assignment_method,
bool  interlacing 
)

Assign particles to grid to compute the over density.

Do this for a normal grid and an interlaced grid and return the alias-corrected fourier transform of the density field in fourier space. This method does not deconvolve the window function

Template Parameters
TThe particle class. Must have a get_pos() method. If the particle has a get_mass method then this is used to weight the particle (we assign the particle with weight mass / mean_mass).
Parameters
[in]partA pointer the first particle.
[in]NumPartHow many particles/positions we have that we want to interpolate the grid to.
[in]NumPartTotHow many particles/positions we have in total over all tasks.
[out]density_grid_fourierThe output density grid in fourier space (must be initialized)
[in]density_assignment_methodThe density assignement method (NGP, CIC, TSC, PCS or PQS).
[in]interlacingIf true use interlacing to reduce aliasing

Definition at line 1174 of file ParticleGridInterpolation.h.

1179 {
1180
1181 if (interlacing) {
1182 particles_to_fourier_grid_interlacing<N, T>(
1183 part, NumPart, NumPartTot, density_grid_fourier, density_assignment_method);
1184 } else {
1185 particles_to_grid<N, T>(part, NumPart, NumPartTot, density_grid_fourier, density_assignment_method);
1186 density_grid_fourier.fftw_r2c();
1187 }
1188 }
void fftw_r2c()
Perform real-to-complex fourier transform.
Definition: FFTWGrid.h:881

◆ particles_to_fourier_grid_interlacing()

template<int N, class T >
void FML::INTERPOLATION::particles_to_fourier_grid_interlacing ( T *  part,
size_t  NumPart,
size_t  NumPartTot,
FFTWGrid< N > &  density_grid_fourier,
std::string  density_assignment_method 
)

Internal method.

Definition at line 1088 of file ParticleGridInterpolation.h.

1092 {
1093
1094 auto Ngrid = density_grid_fourier.get_nmesh();
1095
1096 // Set how many extra slices we need for the density assignment to go smoothly
1097 // One extra slice in general as we need to shift the particle half a grid-cell
1098 auto nleftright = get_extra_slices_needed_for_density_assignment(density_assignment_method);
1099 int nleft = nleftright.first;
1100 int nright = nleftright.second + 1;
1101
1102 // If the grid has too few slices then we must reallocate it
1103 if (density_grid_fourier.get_n_extra_slices_left() < nleft or
1104 density_grid_fourier.get_n_extra_slices_right() < nright) {
1105 density_grid_fourier = FFTWGrid<N>(Ngrid, nleft, nright);
1106 density_grid_fourier.add_memory_label(
1107 "FFTWGrid::particles_to_grid_interlacing::density_grid_fourier (reallocated)");
1108 }
1109
1110 // Bin particles to grid
1111 particles_to_grid<N, T>(part, NumPart, NumPartTot, density_grid_fourier, density_assignment_method);
1112
1113 // Shift particles
1114 const double shift = 1.0 / double(2 * Ngrid);
1115#ifdef USE_OMP
1116#pragma omp parallel for
1117#endif
1118 for (size_t i = 0; i < NumPart; i++) {
1119 auto * pos = FML::PARTICLE::GetPos(part[i]);
1120 pos[0] += shift;
1121 for (int idim = 1; idim < N; idim++) {
1122 pos[idim] += shift;
1123 if (pos[idim] >= 1.0)
1124 pos[idim] -= 1.0;
1125 }
1126 }
1127
1128 // Bin shifted particles to grid
1129 FFTWGrid<N> density_grid_fourier2(Ngrid, nleft, nright);
1130 density_grid_fourier2.add_memory_label(
1131 "FFTWGrid::particles_to_fourier_grid_interlacing::density_grid_fourier2");
1132 particles_to_grid<N, T>(part, NumPart, NumPartTot, density_grid_fourier2, density_assignment_method);
1133
1134 // Shift particles back as not to ruin anything
1135#ifdef USE_OMP
1136#pragma omp parallel for
1137#endif
1138 for (size_t i = 0; i < NumPart; i++) {
1139 auto * pos = FML::PARTICLE::GetPos(part[i]);
1140 pos[0] -= shift;
1141 for (int idim = 1; idim < N; idim++) {
1142 pos[idim] -= shift;
1143 if (pos[idim] < 0.0)
1144 pos[idim] += 1.0;
1145 }
1146 }
1147
1148 // Fourier transform
1149 density_grid_fourier.fftw_r2c();
1150 density_grid_fourier2.fftw_r2c();
1151
1152 // The mean of the two grids (alias cancellation)
1153 auto Local_nx = density_grid_fourier.get_local_nx();
1154
1155#ifdef USE_OMP
1156#pragma omp parallel for
1157#endif
1158 for (int islice = 0; islice < Local_nx; islice++) {
1159 const std::complex<FML::GRID::FloatType> I(0, 1);
1160 for (auto && fourier_index : density_grid_fourier.get_fourier_range(islice, islice + 1)) {
1161 auto kvec = density_grid_fourier.get_fourier_wavevector_from_index(fourier_index);
1162 auto ksum = kvec[0];
1163 for (int idim = 1; idim < N; idim++)
1164 ksum += kvec[idim];
1165 auto norm = std::exp(I * FML::GRID::FloatType(ksum * shift));
1166 auto grid1 = density_grid_fourier.get_fourier_from_index(fourier_index);
1167 auto grid2 = density_grid_fourier2.get_fourier_from_index(fourier_index);
1168 density_grid_fourier.set_fourier_from_index(fourier_index, (grid1 + norm * grid2) / FML::GRID::FloatType(2.0));
1169 }
1170 }
1171 }
void add_memory_label(std::string label)
For memory logging: add a label to the grid.
Definition: FFTWGrid.h:318
std::pair< int, int > get_extra_slices_needed_for_density_assignment(std::string density_assignment_method)
Compute how many extra slices we need in the FFTWGrid for a given density assignement / interpolation...
const double exp
Definition: Global.h:259

◆ particles_to_grid() [1/2]

template<int N, int ORDER, class T >
void FML::INTERPOLATION::particles_to_grid ( const T *  part,
size_t  NumPart,
size_t  NumPartTot,
FFTWGrid< N > &  density 
)

Assign particles to a grid to compute the over density field delta.

Template Parameters
NThe dimension of the grid
TThe particle class. Must have a get_pos() method. If the particle has a get_mass method then this is used to weight the particle (we assign the particle with weight mass / mean_mass).
ORDERThe order of the B-spline interpolation (1=NGP, 2=CIC, 3=TSC, 4=PCS, 5=PQS, ...). If larger than 5 then you must implement kernel<ORDER> yourself (a simple Mathematica calculation), see the source.
Parameters
[in]partA pointer the first particle.
[in]NumPartHow many particles/positions we have that we want to interpolate the grid to.
[in]NumPartTotHow many particles/positions we have in total over all tasks.
[out]densityThe overdensity field.

Definition at line 477 of file ParticleGridInterpolation.h.

477 {
478
479 const auto nextra = get_extra_slices_needed_by_order<ORDER>();
480 assert_mpi(density.get_n_extra_slices_left() >= nextra.first and
481 density.get_n_extra_slices_right() >= nextra.second,
482 "[particles_to_grid] Too few extra slices\n");
483
484 //==========================================================
485 // This is a generic method. You have to specify the kernel
486 // and the corresponding width = the number of cells
487 // the point gets distrubuted to in each dimension which
488 // also corresponds to the order
489 //==========================================================
490
491 // For the kernel above we need to go kernel_width/2 cells to the left and right
492 constexpr int widthtondim = FML::power(ORDER, N);
493
494 // Info about the grid
495 // const auto Local_nx = density.get_local_nx();
496 const auto Local_x_start = density.get_local_x_start();
497 const int Nmesh = density.get_nmesh();
498
499 // Set whole grid (also extra slices) to -1.0
500 density.fill_real_grid(-1.0);
501
502 // Factor to normalize density to the mean density
503 double norm_fac = std::pow((double)Nmesh, N) / double(NumPartTot);
504
505 // Check if particles has a get_mass method and if so
506 // compute the mean mass
507 constexpr bool has_mass = FML::PARTICLE::has_get_mass<T>();
508 if constexpr (has_mass) {
509 double mean_mass = 0.0;
510 for (size_t i = 0; i < NumPart; i++) {
511 mean_mass += FML::PARTICLE::GetMass(part[i]);
512 }
513 SumOverTasks(&mean_mass);
514 mean_mass /= double(NumPartTot);
515 norm_fac /= mean_mass;
516 }
517 double mass = 1.0;
518
519 // Loop over all particles and add them to the grid
520 // OpenMP will not be very good due to critical section needed in add_real
521 for (size_t i = 0; i < NumPart; i++) {
522
523 // Particle position
524 const auto * pos = FML::PARTICLE::GetPos(const_cast<T *>(part)[i]);
525
526 // Fetch mass if this is availiable
527 if constexpr (has_mass)
528 mass = FML::PARTICLE::GetMass(part[i]);
529
530 std::array<double, N> x;
531 std::array<int, N> ix;
532 [[maybe_unused]] std::array<int, N> ix_nbor;
533 for (int idim = 0; idim < N; idim++) {
534 // Scale positions to be in [0, Nmesh]
535 x[idim] = pos[idim] * Nmesh;
536 // Grid-index for cell containing particle
537 ix[idim] = (int)x[idim];
538 // Distance relative to cell
539 x[idim] -= ix[idim];
540 }
541
542 // Periodic BC
543 ix[0] -= int(Local_x_start);
544 for (int idim = 1; idim < N; idim++) {
545 if (ix[idim] == Nmesh)
546 ix[idim] = 0;
547 }
548
549 // If we are on the left or right of the cell determines how many cells
550 // we have to go left and right
551 std::array<int, N> xstart;
552 if (ORDER % 2 == 0) {
553 for (int idim = 0; idim < N; idim++) {
554 xstart[idim] = -ORDER / 2 + 1;
555#ifdef CELLCENTERSHIFTED
556 xstart[idim] = -ORDER / 2;
557 if (x[idim] > 0.5)
558 xstart[idim] += 1;
559#endif
560 }
561 } else {
562#ifndef CELLCENTERSHIFTED
563 for (int idim = 0; idim < N; idim++) {
564 xstart[idim] = -ORDER / 2;
565 if (x[idim] > 0.5)
566 xstart[idim] += 1;
567 }
568#endif
569 }
570
571 // Loop over all nbor cells
572 double sumweights = 0.0;
573 for (int i = 0; i < widthtondim; i++) {
574 double w = 1.0;
575 std::array<int, N> icoord;
576 if constexpr (ORDER == 1) {
577 icoord = ix;
578 } else {
579 for (int idim = 0, n = 1; idim < N; idim++, n *= ORDER) {
580 int go_left_right_or_stay = xstart[idim] + (i / n % ORDER);
581 ix_nbor[idim] = ix[idim] + go_left_right_or_stay;
582#ifdef CELLCENTERSHIFTED
583 double dx = std::fabs(-x[idim] + go_left_right_or_stay + 0.5);
584#else
585 double dx = std::fabs(-x[idim] + go_left_right_or_stay);
586#endif
587 w *= kernel<ORDER>(dx);
588 }
589
590 // Periodic BC for all but x (we have extra slices - XXX should assert that its not too large,
591 // but covered by boundscheck in FFTWGrid if this is turned on)!
592 icoord[0] = ix_nbor[0];
593 for (int idim = 1; idim < N; idim++) {
594 icoord[idim] = ix_nbor[idim];
595 if (icoord[idim] >= Nmesh)
596 icoord[idim] -= Nmesh;
597 if (icoord[idim] < 0)
598 icoord[idim] += Nmesh;
599 }
600 }
601
602 // Add particle to grid
603 density.add_real(icoord, w * norm_fac * mass);
604 sumweights += w;
605 }
606
607#ifdef DEBUG_INTERPOL
608 // Check that the weights sum up to unity
609 assert_mpi(
610 std::fabs(sumweights - 1.0) < 1e-3,
611 "[particles_to_grid] Possible problem with particles to grid: weights does not sum to unity!");
612#endif
613 }
614
615 add_contribution_from_extra_slices<N>(density);
616 }
void fill_real_grid(const FloatType val)
Fill the whole real-grid with a constant value.
Definition: FFTWGrid.h:588
void add_real(const std::array< int, N > &coord, const FloatType value)
Add to value in grid.
Definition: FFTWGrid.h:1014
set_mass constexpr double GetMass(Args &... args)
void SumOverTasks(T *value)
Inplace sum over tasks of a single value.
Definition: Global.h:147

◆ particles_to_grid() [2/2]

template<int N, class T >
void FML::INTERPOLATION::particles_to_grid ( const T *  part,
size_t  NumPart,
size_t  NumPartTot,
FFTWGrid< N > &  density,
std::string  density_assignment_method 
)

Assign particles to a grid to compute the over density field delta.

Template Parameters
NThe dimension of the grid
TThe particle class. Must have a get_pos() method. If the particle has a get_mass method then this is used to weight the particle (we assign the particle with weight mass / mean_mass).
Parameters
[in]partA pointer the first particle.
[in]NumPartHow many particles/positions we have that we want to interpolate the grid to.
[in]NumPartTotHow many particles/positions we have in total over all tasks.
[out]densityThe overdensity field.
[in]density_assignment_methodThe assignment method: NGP, CIC, TSC, PCS or PQS.

Definition at line 224 of file ParticleGridInterpolation.h.

228 {
229 if (density_assignment_method.compare("NGP") == 0)
230 particles_to_grid<N, 1, T>(part, NumPart, NumPartTot, density);
231 if (density_assignment_method.compare("CIC") == 0)
232 particles_to_grid<N, 2, T>(part, NumPart, NumPartTot, density);
233 if (density_assignment_method.compare("TSC") == 0)
234 particles_to_grid<N, 3, T>(part, NumPart, NumPartTot, density);
235 if (density_assignment_method.compare("PCS") == 0)
236 particles_to_grid<N, 4, T>(part, NumPart, NumPartTot, density);
237 if (density_assignment_method.compare("PQS") == 0)
238 particles_to_grid<N, 5, T>(part, NumPart, NumPartTot, density);
239 }