/* ----------------------------------------------------------------------
   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator 

   Original Version:
   http://lammps.sandia.gov, Sandia National Laboratories
   Steve Plimpton, sjplimp@sandia.gov 

   See the README file in the top-level LAMMPS directory. 

   ----------------------------------------------------------------------- 

   USER-CUDA Package and associated modifications:
   https://sourceforge.net/projects/lammpscuda/ 

   Christian Trott, christian.trott@tu-ilmenau.de
   Lars Winterfeld, lars.winterfeld@tu-ilmenau.de
   Theoretical Physics II, University of Technology Ilmenau, Germany 

   See the README file in the USER-CUDA directory. 

   This software is distributed under the GNU General Public License.
------------------------------------------------------------------------- */

#define OFFSET 4096
__device__ int negativCUDA(float f)
{
 return ((unsigned int)1<<31&(__float_as_int(f)))>>31;
}

__device__ void reduceBlock(float* data)
{
  int p2=1;
  while(p2*2<blockDim.x) p2*=2;

  if(threadIdx.x<blockDim.x-p2)
    data[threadIdx.x]+=data[threadIdx.x+p2];
  __syncthreads();
  for(int i=2;i<=p2;i*=2)
  {
    if(threadIdx.x<p2/i)
    data[threadIdx.x]+=data[threadIdx.x+p2/i];
    __syncthreads();
  }
}

__device__ void reduceBlock(double* data)
{
  int p2=1;
  while(p2*2<blockDim.x) p2*=2;

  if(threadIdx.x<blockDim.x-p2)
    data[threadIdx.x]+=data[threadIdx.x+p2];
  __syncthreads();
  for(int i=2;i<=p2;i*=2)
  {
    if(threadIdx.x<p2/i)
    data[threadIdx.x]+=data[threadIdx.x+p2/i];
    __syncthreads();
  }
}

extern __shared__ PPPM_FLOAT sharedmem[];

__global__ void setup_fkxyz_vg(PPPM_FLOAT unitkx,PPPM_FLOAT unitky,PPPM_FLOAT unitkz,PPPM_FLOAT g_ewald)
{
	PPPM_FLOAT my_fkx=unitkx*(int(threadIdx.x) - nx_pppm*(2*int(threadIdx.x)/nx_pppm));
	PPPM_FLOAT my_fky=unitky*(int(blockIdx.y) - ny_pppm*(2*int(blockIdx.y)/ny_pppm));
	PPPM_FLOAT my_fkz=unitkz*(int(blockIdx.x) - nz_pppm*(2*int(blockIdx.x)/nz_pppm));
	
	if((blockIdx.x==0)&&(blockIdx.y==0)) fkx[threadIdx.x]=my_fkx;
	if((blockIdx.x==0)&&(threadIdx.x==0)) fky[blockIdx.y]=my_fky;
	if((threadIdx.x==0)&&(blockIdx.y==0)) fkz[blockIdx.x]=my_fkz;
	__syncthreads();
	if((blockIdx.x >= nzlo_fft)&&(blockIdx.x <=nzhi_fft)&&
	   (blockIdx.y >= nylo_fft)&&(blockIdx.y <=nyhi_fft)&&
	   (threadIdx.x>= nxlo_fft)&&(threadIdx.x<=nxhi_fft))
	{
		int n=((int(blockIdx.x)-nzlo_fft)*(nyhi_fft-nylo_fft+1)+int(blockIdx.y)-nylo_fft)*(nxhi_fft-nxlo_fft+1)+int(threadIdx.x)-nxlo_fft;
		PPPM_FLOAT sqk = my_fkx*my_fkx + my_fky*my_fky + my_fkz*my_fkz;
		PPPM_FLOAT vterm = (sqk==PPPM_F(0.0)) ? PPPM_F(0.0) : PPPM_F(-2.0) * (PPPM_F(1.0)/sqk + PPPM_F(0.25)/(g_ewald*g_ewald));
		vg[6*n+0] = (sqk == PPPM_F(0.0)) ? PPPM_F(0.0) : PPPM_F(1.0) + vterm * my_fkx*my_fkx;
		vg[6*n+1] = (sqk == PPPM_F(0.0)) ? PPPM_F(0.0) : PPPM_F(1.0) + vterm * my_fky*my_fky;
		vg[6*n+2] = (sqk == PPPM_F(0.0)) ? PPPM_F(0.0) : PPPM_F(1.0) + vterm * my_fkz*my_fkz;
		vg[6*n+3] = (sqk == PPPM_F(0.0)) ? PPPM_F(0.0) : vterm * my_fkx*my_fky;
		vg[6*n+4] = (sqk == PPPM_F(0.0)) ? PPPM_F(0.0) : vterm * my_fkx*my_fkz;
		vg[6*n+5] = (sqk == PPPM_F(0.0)) ? PPPM_F(0.0) : vterm * my_fky*my_fkz;
		
	}
}

__device__ PPPM_FLOAT gf_denom(PPPM_FLOAT x, PPPM_FLOAT y, PPPM_FLOAT z)
{
  PPPM_FLOAT sx,sy,sz;
  sz = sy = sx = PPPM_F(0.0);
  for (int l = order-1; l >= 0; l--) {
    sx = gf_b[l] + sx*x;
    sy = gf_b[l] + sy*y;
    sz = gf_b[l] + sz*z;
  }
  PPPM_FLOAT s = sx*sy*sz;
  return s*s;
}

__global__ void setup_greensfn(PPPM_FLOAT unitkx,PPPM_FLOAT unitky,PPPM_FLOAT unitkz,PPPM_FLOAT g_ewald,
int nbx,int nby,int nbz,
PPPM_FLOAT xprd,PPPM_FLOAT yprd,PPPM_FLOAT zprd_slab)
{
  PPPM_FLOAT sqk;
  int nx,ny,nz,kper,lper,mper,k,l,m;
  PPPM_FLOAT snx,sny,snz,snx2,sny2,snz2;
  PPPM_FLOAT argx,argy,argz,wx,wy,wz,sx,sy,sz,qx,qy,qz;
  PPPM_FLOAT sum1,dot1,dot2;
  PPPM_FLOAT numerator,denominator;

  PPPM_FLOAT form=PPPM_F(1.0);
  int n=(blockIdx.x*gridDim.y+blockIdx.y)*blockDim.x+threadIdx.x;
  m=blockIdx.x;
  l=blockIdx.y;
  k=threadIdx.x;

    mper = m - nz_pppm*(2*m/nz_pppm);
    snz = sin(PPPM_F(0.5)*unitkz*mper*zprd_slab/nz_pppm);
    snz2 = snz*snz;


    lper = l - ny_pppm*(2*l/ny_pppm);
    sny = sin(PPPM_F(0.5)*unitky*lper*yprd/ny_pppm);
    sny2 = sny*sny;

	kper = k - nx_pppm*(2*k/nx_pppm);
	snx = sin(PPPM_F(0.5)*unitkx*kper*xprd/nx_pppm);
	snx2 = snx*snx;
      
	sqk = pow(unitkx*kper,PPPM_F(2.0)) + pow(unitky*lper,PPPM_F(2.0)) + 
	  pow(unitkz*mper,PPPM_F(2.0));

	if (sqk != PPPM_F(0.0)) {
	  numerator = form*PPPM_F(12.5663706)/sqk;
	  denominator = gf_denom(snx2,sny2,snz2);  
	  sum1 = PPPM_F(0.0);
	  for (nx = -nbx; nx <= nbx; nx++) {
	    qx = unitkx*(kper+nx_pppm*nx);
	    sx = exp(PPPM_F(-.25)*pow(qx/g_ewald,PPPM_F(2.0)));
	    wx = PPPM_F(1.0);
	    argx = PPPM_F(0.5)*qx*xprd/nx_pppm;
	    if (argx != PPPM_F(0.0)) wx = pow(sin(argx)/argx,order);
	    for (ny = -nby; ny <= nby; ny++) {
	      qy = unitky*(lper+ny_pppm*ny);
	      sy = exp(PPPM_F(-.25)*pow(qy/g_ewald,PPPM_F(2.0)));
	      wy = PPPM_F(1.0);
	      argy = PPPM_F(0.5)*qy*yprd/ny_pppm;
	      if (argy != PPPM_F(0.0)) wy = pow(sin(argy)/argy,order);
	      for (nz = -nbz; nz <= nbz; nz++) {
		qz = unitkz*(mper+nz_pppm*nz);
		sz = exp(PPPM_F(-.25)*pow(qz/g_ewald,PPPM_F(2.0)));
		wz = PPPM_F(1.0);
		argz = PPPM_F(0.5)*qz*zprd_slab/nz_pppm;
		if (argz != PPPM_F(0.0)) wz = pow(sin(argz)/argz,order);

		dot1 = unitkx*kper*qx + unitky*lper*qy + unitkz*mper*qz;
		dot2 = qx*qx+qy*qy+qz*qz;
		sum1 += (dot1/dot2) * sx*sy*sz * pow(wx*wy*wz,PPPM_F(2.0));
	      }
	    }
	  }
	  greensfn[n] = numerator*sum1/denominator;
	} else greensfn[n] = PPPM_F(0.0);
}

__global__ void poisson_scale_kernel()
{
  int i=(blockIdx.x*gridDim.y+blockIdx.y)*blockDim.x+threadIdx.x;
  FFT_FLOAT scaleinv=FFT_F(1.0)/(gridDim.x*gridDim.y*blockDim.x);
    work1[2*i] *= scaleinv * greensfn[i];
    work1[2*i+1] *= scaleinv * greensfn[i];
}

__global__ void poisson_xgrad_kernel()
{
  int i=(blockIdx.x*gridDim.y+blockIdx.y)*blockDim.x+threadIdx.x;
    work2[2*i] = fkx[threadIdx.x] * work1[2*i+1];
    work2[2*i+1] = -fkx[threadIdx.x] * work1[2*i];
}

__global__ void poisson_ygrad_kernel()
{
  int i=(blockIdx.x*gridDim.y+blockIdx.y)*blockDim.x+threadIdx.x;
    work2[2*i] = fky[blockIdx.y] * work1[2*i+1];
    work2[2*i+1] = -fky[blockIdx.y] * work1[2*i];
}

__global__ void poisson_zgrad_kernel()
{
  int i=(blockIdx.x*gridDim.y+blockIdx.y)*blockDim.x+threadIdx.x;
    work2[2*i] = fkz[blockIdx.x] * work1[2*i+1];
    work2[2*i+1] = -fkz[blockIdx.x] * work1[2*i];
}

__global__ void poisson_vdx_brick_kernel(int ilo,int jlo,int klo)
{
  int k=blockIdx.x+klo;
  k+=nz_pppm*negativCUDA(CUDA_F(1.0)*k)-nz_pppm*negativCUDA(CUDA_F(1.0)*(nz_pppm-k-1));
  int j=blockIdx.y+jlo;
  j+=ny_pppm*negativCUDA(CUDA_F(1.0)*j)-ny_pppm*negativCUDA(CUDA_F(1.0)*(ny_pppm-j-1));
  int i=threadIdx.x+ilo;
  i+=nx_pppm*negativCUDA(CUDA_F(1.0)*i)-nx_pppm*negativCUDA(CUDA_F(1.0)*(nx_pppm-i-1));
  vdx_brick[((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1)+threadIdx.x] = work3[2*(((k)*ny_pppm+(j))*nx_pppm+i)];
}

__global__ void poisson_vdy_brick_kernel(int ilo,int jlo,int klo)
{
  int k=blockIdx.x+klo;
  k+=nz_pppm*negativCUDA(CUDA_F(1.0)*k)-nz_pppm*negativCUDA(CUDA_F(1.0)*(nz_pppm-k-1));
  int j=blockIdx.y+jlo;
  j+=ny_pppm*negativCUDA(CUDA_F(1.0)*j)-ny_pppm*negativCUDA(CUDA_F(1.0)*(ny_pppm-j-1));
  int i=threadIdx.x+ilo;
  i+=nx_pppm*negativCUDA(CUDA_F(1.0)*i)-nx_pppm*negativCUDA(CUDA_F(1.0)*(nx_pppm-i-1));
  vdy_brick[((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1)+threadIdx.x] = work3[2*(((k)*ny_pppm+(j))*nx_pppm+i)];
}

__global__ void poisson_vdz_brick_kernel(int ilo,int jlo,int klo)
{
  int k=blockIdx.x+klo;
  k+=nz_pppm*negativCUDA(CUDA_F(1.0)*k)-nz_pppm*negativCUDA(CUDA_F(1.0)*(nz_pppm-k-1));
  int j=blockIdx.y+jlo;
  j+=ny_pppm*negativCUDA(CUDA_F(1.0)*j)-ny_pppm*negativCUDA(CUDA_F(1.0)*(ny_pppm-j-1));
  int i=threadIdx.x+ilo;
  i+=nx_pppm*negativCUDA(CUDA_F(1.0)*i)-nx_pppm*negativCUDA(CUDA_F(1.0)*(nx_pppm-i-1));
  vdz_brick[((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1)+threadIdx.x] = work3[2*(((k)*ny_pppm+(j))*nx_pppm+i)];
}

__global__ void poisson_energy_kernel(int nxlo_fft,int nylo_fft,int nzlo_fft,int vflag)
{
  ENERGY_FLOAT scaleinv=FFT_F(1.0)/(nx_pppm*ny_pppm*nz_pppm);
  int i=(blockIdx.x+nzlo_fft)*ny_pppm*nx_pppm+(blockIdx.y+nylo_fft)*nx_pppm+threadIdx.x+nxlo_fft;
  ENERGY_FLOAT* s_energy=(ENERGY_FLOAT*) sharedmem;
  ENERGY_FLOAT myenergy= scaleinv*scaleinv * greensfn[i] * (work1[2*i]*work1[2*i] + work1[2*i+1]*work1[2*i+1]);
  s_energy[threadIdx.x]=myenergy;
  
  __syncthreads();
  reduceBlock(s_energy);
  if(threadIdx.x==0)
  energy[blockIdx.x*ny_pppm+blockIdx.y]=s_energy[0];
  if(vflag)
  {
  	__syncthreads();
  	for (int j = 0; j < 6; j++) 
  	{
  	  s_energy[threadIdx.x]= myenergy*vg[((blockIdx.x*gridDim.y+blockIdx.y)*(blockDim.x)+threadIdx.x)*6+j];
      __syncthreads();
      reduceBlock(s_energy);
      if(threadIdx.x==0)
      virial[blockIdx.x*ny_pppm+blockIdx.y+j*nz_pppm*ny_pppm]=s_energy[0];
  	}
  }   
}


__global__ void sum_energy_kernel1(int vflag)
{
  ENERGY_FLOAT myenergy=energy[(blockIdx.x*ny_pppm+threadIdx.x)];
  ENERGY_FLOAT* s_energy=(ENERGY_FLOAT*) sharedmem;
  s_energy[threadIdx.x]=myenergy;
  __syncthreads();
  reduceBlock(s_energy);
  if(threadIdx.x==0)  
  energy[blockIdx.x*ny_pppm]=s_energy[0];
  if(vflag)
  {
  	__syncthreads();
  	for (int j = 0; j < 6; j++) 
  	{
        myenergy=virial[blockIdx.x*ny_pppm+threadIdx.x+j*ny_pppm*nz_pppm];
        s_energy[threadIdx.x]=myenergy;
        __syncthreads();
	    reduceBlock(s_energy);
        if(threadIdx.x==0)  
        virial[blockIdx.x*ny_pppm+j*ny_pppm*nz_pppm]=s_energy[0];
  	}
  }

}

__global__ void sum_energy_kernel2(int vflag)
{
  ENERGY_FLOAT myenergy=energy[threadIdx.x*ny_pppm];
  ENERGY_FLOAT* s_energy=(ENERGY_FLOAT*) sharedmem;
  s_energy[threadIdx.x]=myenergy;
  __syncthreads();
  reduceBlock(s_energy);
  if(threadIdx.x==0)  
  energy[0]=s_energy[0];
  if(vflag)
  {
  	__syncthreads();
  	for (int j = 0; j < 6; j++) 
  	{
        myenergy=virial[threadIdx.x*ny_pppm+j*ny_pppm*nz_pppm];
        s_energy[threadIdx.x]=myenergy;
        __syncthreads();
	    reduceBlock(s_energy);
        if(threadIdx.x==0)  
        virial[j]=s_energy[0];
  	}
  }
}

__device__ PPPM_FLOAT rho1d(int k,PPPM_FLOAT d,PPPM_FLOAT* srho_coeff)
{
    PPPM_FLOAT rho1d_tmp=PPPM_F(0.0);
    for (int l = order-1; l >= 0; l--) 
      rho1d_tmp = srho_coeff[l*order+k-(1-order)/2] + rho1d_tmp*d;
    return rho1d_tmp;
}

__global__ void particle_map_kernel(int* flag)
{
  int i=blockIdx.x*blockDim.x+threadIdx.x;
  if(i<nlocal)
  {
    int nx,ny,nz;
  	PPPM_FLOAT shift=PPPM_F(0.5)-shiftone;//+OFFSET;
    nx = (int)((_x[i]-_boxlo[0])*delxinv+shift);// - OFFSET;
    ny = (int)((_x[i+nmax]-_boxlo[1])*delyinv+shift);// - OFFSET;
    nz = (int)((_x[i+2*nmax]-_boxlo[2])*delzinv+shift);// - OFFSET;

    part2grid[i] = nx;
    part2grid[i+nmax] = ny;
    part2grid[i+2*nmax] = nz;
    // check that entire stencil around nx,ny,nz will fit in my 3d brick
    if (nx+nlower < nxlo_out || nx+nupper > nxhi_out ||
	ny+nlower < nylo_out || ny+nupper > nyhi_out ||
	nz+nlower < nzlo_out || nz+nupper > nzhi_out) 
	{flag[0]++;
	  debugdata[0]=i;
	  debugdata[1]=_boxlo[0];
	  debugdata[2]=_boxlo[1];
	  debugdata[3]=_boxlo[2];
	  debugdata[4]=nx;
	  debugdata[5]=ny;
	  debugdata[6]=nz;
	  debugdata[7]=_x[i];
	  debugdata[8]=_x[i+_nmax];
	  debugdata[9]=_x[i+2*_nmax];
	  debugdata[10]=nlocal;
	  
	}
  }
}

__global__ void make_rho_kernelA()
{
 int i,l,m,n,nx,ny,nz,mx,my,mz;

  // clear 3d density array


  // loop over my charges, add their contribution to nearby grid points
  // (nx,ny,nz) = global coords of grid pt to "lower left" of charge
  // (dx,dy,dz) = distance to "lower left" grid pt
  // (mx,my,mz) = global coords of moving stencil pt

  i=blockIdx.x*blockDim.x+threadIdx.x;
  
  if(i < nlocal) {

    PPPM_FLOAT dx,dy,dz,x0,y0,z0;
    nx = part2grid[i];
    ny = part2grid[i+nmax];
    nz = part2grid[i+2*nmax];
    dx = nx+shiftone - (_x[i]-_boxlo[0])*delxinv;
    dy = ny+shiftone - (_x[i+nmax]-_boxlo[1])*delyinv;
    dz = nz+shiftone - (_x[i+2*nmax]-_boxlo[2])*delzinv;
    
    z0 = delxinv*delyinv*delzinv * _q[i];
    for (n = nlower; n <= nupper; n++) 
    {
      mz = n+nz;
      y0 = z0*rho1d(n,dz,rho_coeff);
      for (m = nlower; m <= nupper; m++) 
      {
		my = m+ny;
		x0 = y0*rho1d(m,dy,rho_coeff);
		for (l = nlower; l <= nupper; l++) 
		{
	  		mx = l+nx;
			int mzyx=((mz-nzlo_out)*(nyhi_out-nylo_out+1)+my-nylo_out)*(nxhi_out-nxlo_out+1)+mx-nxlo_out;
	  	 	while(atomicAdd(&density_brick_int[mzyx],1)!=0) atomicAdd(&density_brick_int[mzyx],-1);
	  	 	density_brick[mzyx]+=x0*rho1d(l,dx,rho_coeff);
	  	 	__threadfence();
	  	 	atomicAdd(&density_brick_int[mzyx],-1);
	  	 	__syncthreads();

	    }
      }
    }
  }
}

__global__ void make_rho_kernel(int* flag,int read_threads_at_same_time)
{
 int i,l,m,n,nx,ny,nz,mx,my,mz,a,b;

  // clear 3d density array


  // loop over my charges, add their contribution to nearby grid points
  // (nx,ny,nz) = global coords of grid pt to "lower left" of charge
  // (dx,dy,dz) = distance to "lower left" grid pt
  // (mx,my,mz) = global coords of moving stencil pt
 // int nzxy=blockIdx.x*gridDim.y+blockIdx.y;
  
  int nelements=nupper-nlower+1;
  int* idx=(int*) sharedmem;
  int* sdensity_brick_int=&idx[blockDim.x];
  PPPM_FLOAT* srho_coeff=(PPPM_FLOAT*) &sdensity_brick_int[nelements*blockDim.x];
  if(threadIdx.x<order*(order/2-(1-order)/2+1))
  srho_coeff[threadIdx.x]=rho_coeff[threadIdx.x];
  __syncthreads();
    
 i=blockIdx.x*blockDim.x+threadIdx.x;
 
 if(false)
 {
  if(i < nlocal) {

    PPPM_FLOAT dx,dy,dz,x0,y0,z0;
    nx = part2grid[i];
    ny = part2grid[i+nmax];
    nz = part2grid[i+2*nmax];
    dx = nx+shiftone - (_x[i]-_boxlo[0])*delxinv;
    dy = ny+shiftone - (_x[i+nmax]-_boxlo[1])*delyinv;
    dz = nz+shiftone - (_x[i+2*nmax]-_boxlo[2])*delzinv;
    
    z0 = delxinv*delyinv*delzinv * _q[i];
    for (n = nlower; n <= nupper; n++) 
    {
      mz = n+nz;
      y0 = z0*rho1d(n,dz,srho_coeff);
      for (m = nlower; m <= nupper; m++) 
      {
		my = m+ny;
		x0 = y0*rho1d(m,dy,srho_coeff);
		for (l = nlower; l <= nupper; l++) 
		{
	  		mx = l+nx;
			int mzyx=((mz-nzlo_out)*(nyhi_out-nylo_out+1)+my-nylo_out)*(nxhi_out-nxlo_out+1)+mx-nxlo_out;
	  	 	
	  	 	a=int(x0*rho1d(l,dx,srho_coeff)*density_intScale);
	  	 	b=(atomicAdd(&density_brick_int[mzyx],a)|a);
	  	 	if(((b)&(0x7c000000))&&(not((b)&(0x80000000)))) 
	  	 	{
	  	 	  flag[1]++;
	  	 	  if((b)&(0x60000000)) flag[0]++;
	  	 	}
	  	 	__syncthreads();
	    }
      }
    }
  }
  return;
  }
  
  i=blockIdx.x*blockDim.x+threadIdx.x;
  {

    PPPM_FLOAT dx,dy,dz,x0,y0,z0,qtmp;
    if(i < nlocal) 
    {
      qtmp= _q[i];
      nx = part2grid[i];
      ny = part2grid[i+nmax];
      nz = part2grid[i+2*nmax];
      dx = nx+shiftone - (_x[i]-_boxlo[0])*delxinv;
      dy = ny+shiftone - (_x[i+nmax]-_boxlo[1])*delyinv;
      dz = nz+shiftone - (_x[i+2*nmax]-_boxlo[2])*delzinv;
      z0 = delxinv*delyinv*delzinv * qtmp;
    }
    else
    {
      nx=ny=nz=1; dx=dy=dz=PPPM_F(0.1);
    }
    __syncthreads();
    for (n = nlower; n <= nupper; n++) 
    {
      mz = n+nz;
      y0 = z0*rho1d(n,dz,srho_coeff);
      for (m = nlower; m <= nupper; m++) 
      {
		my = m+ny;
		x0 = y0*rho1d(m,dy,srho_coeff);
        if(i<nlocal)
        {
	   	  idx[threadIdx.x]=((mz-nzlo_out)*(nyhi_out-nylo_out+1)+my-nylo_out)*(nxhi_out-nxlo_out+1)+nx+nlower-nxlo_out;
		  for (l = nlower; l <= nupper; l++) 
		  {
	  	 	sdensity_brick_int[threadIdx.x*nelements+l-nlower]=int(x0*rho1d(l,dx,srho_coeff)*density_intScale);
	      }
        }
	   	else idx[threadIdx.x]=-1;
		__syncthreads();
		
 		for(int ii=0;ii<blockDim.x;ii+=read_threads_at_same_time)
		{
			int kk=threadIdx.x/nelements;
		
			if((threadIdx.x<nelements*read_threads_at_same_time)&&(kk+ii<blockDim.x)&&(idx[ii+kk]>-1))
			{
			  a=sdensity_brick_int[ii*nelements+threadIdx.x];
			  //if(a*a>1e-100)
			  b=(atomicAdd(&density_brick_int[idx[ii+kk]+threadIdx.x-kk*nelements],a)|a);
			  //else 
			  //b=(density_brick_int[idx[ii+kk]+threadIdx.x-kk*nelements]|a);
	  	 	  if(((b)&(0x7c000000))&&(not((b)&(0x80000000)))) 
	  	 	  {
	  	 	    flag[1]++;
	  	 	    if((b)&(0x60000000)) flag[0]++;
	  	 	  }
			}
		}
		__syncthreads();	   //*/ 
      }
    }
  
  }
}

__global__ void scale_rho_kernel()
{
  int i=(blockIdx.x*gridDim.y+blockIdx.y)*blockDim.x+threadIdx.x;
  density_brick[i]=(1.0/density_intScale)*density_brick_int[i];      		
}

__global__ void fieldforce_kernel(int elements_per_thread,int read_threads_at_same_time,int* flag) //20*x64 0.36
{
  int i;

  // loop over my charges, interpolate electric field from nearby grid points
  // (nx,ny,nz) = global coords of grid pt to "lower left" of charge
  // (dx,dy,dz) = distance to "lower left" grid pt
  // (mx,my,mz) = global coords of moving stencil pt
  // ek = 3 components of E-field on particle
  i=blockIdx.x*blockDim.x+threadIdx.x;
  int* idx=(int*) sharedmem;
  PPPM_FLOAT* tmp_brick=(PPPM_FLOAT*) &idx[blockDim.x];
  PPPM_FLOAT* srho_coeff=(PPPM_FLOAT*) &tmp_brick[3*blockDim.x*elements_per_thread];
  if(threadIdx.x<order*(order/2-(1-order)/2+1))
  srho_coeff[threadIdx.x]=rho_coeff[threadIdx.x];
  __syncthreads();
  {
  	int l,m,n,nx,ny,nz,my,mz;
    PPPM_FLOAT dx,dy,dz,x0,y0,z0;
    PPPM_FLOAT ek[3];
    if(i<nlocal)
    {
      nx = part2grid[i];
      ny = part2grid[i+nmax];
      nz = part2grid[i+2*nmax];
      dx = nx+shiftone - (_x[i]-_boxlo[0])*delxinv;
      dy = ny+shiftone - (_x[i+nmax]-_boxlo[1])*delyinv;
      dz = nz+shiftone - (_x[i+2*nmax]-_boxlo[2])*delzinv;
    
      ek[0] = ek[1] = ek[2] = PPPM_F(0.0);
    }
    else
    {
      nx=ny=nz=1; dx=dy=dz=PPPM_F(0.1);
    }
    __syncthreads();
    for (n = nlower; n <= nupper; n++) 
    {
      mz = n+nz;
      z0 = rho1d(n,dz,srho_coeff);
      for (m = nlower; m <= nupper; m++) 
      {
	    my = m+ny;
	    y0 = z0*rho1d(m,dy,srho_coeff);
	
	
        if(i<nlocal)
	   	idx[threadIdx.x]=((mz-nzlo_out)*(nyhi_out-nylo_out+1)+my-nylo_out)*(nxhi_out-nxlo_out+1)+nx+nlower-nxlo_out;
	   	else idx[threadIdx.x]=-1;
		__syncthreads();

		for(int ii=0;ii<blockDim.x;ii+=read_threads_at_same_time)
		{
			int kk=threadIdx.x/elements_per_thread;
		
			if((threadIdx.x<elements_per_thread*read_threads_at_same_time)&&(kk+ii<blockDim.x)&&(idx[ii+kk]>-1))
			{
			tmp_brick[ii*elements_per_thread+threadIdx.x]=vdx_brick[idx[ii+kk]+threadIdx.x-kk*elements_per_thread];
			tmp_brick[(ii+blockDim.x)*elements_per_thread+threadIdx.x]=vdy_brick[idx[ii+kk]+threadIdx.x-kk*elements_per_thread];
			tmp_brick[(ii+2*blockDim.x)*elements_per_thread+threadIdx.x]=vdz_brick[idx[ii+kk]+threadIdx.x-kk*elements_per_thread];
			}
		}
		__syncthreads();
 		
        if(i<nlocal)
 		for (l = nlower; l <= nupper; l++) 
 		{
	  		x0 = y0*rho1d(l,dx,srho_coeff);

	  		ek[0] -= x0*tmp_brick[threadIdx.x*elements_per_thread+l-nlower];
	  		ek[1] -= x0*tmp_brick[threadIdx.x*elements_per_thread+l-nlower+blockDim.x*elements_per_thread];
	  		ek[2] -= x0*tmp_brick[threadIdx.x*elements_per_thread+l-nlower+2*blockDim.x*elements_per_thread];
	  	}
		__syncthreads();
      }
    }
    // convert E-field to force


    _f[i] += qqrd2e*_q[i]*ek[0];
    _f[i+nmax] += qqrd2e*_q[i]*ek[1];
    _f[i+2*nmax] += qqrd2e*_q[i]*ek[2];
  }
}

__global__ void slabcorr_energy_kernel(ENERGY_FLOAT* buf)
{
	ENERGY_FLOAT* dipole =(ENERGY_FLOAT*) sharedmem;
	int i=blockIdx.x*blockDim.x+threadIdx.x;
	if(i<nlocal)
	dipole[threadIdx.x]=_q[i]*_x[i+2*nmax];
	else
	dipole[threadIdx.x]=ENERGY_F(0.0);
    __syncthreads();
    reduceBlock(dipole);
    if(threadIdx.x==0) buf[blockIdx.x]=dipole[0];
}

__global__ void slabcorr_force_kernel(F_FLOAT ffact)
{
	int i=blockIdx.x*blockDim.x+threadIdx.x;
	if(i<nlocal)
	_f[i+2*nmax] += qqrd2e*_q[i]*ffact;
}


__global__ void initfftdata_core_kernel(PPPM_FLOAT* in, FFT_FLOAT* out)
{
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]=in[(((blockIdx.x+nzlo_in-nzlo_out)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)+1]=0;
}

__global__ void initfftdata_z_kernel(PPPM_FLOAT* in, FFT_FLOAT* out)
{
   if(slabflag){
   if(blockIdx.x<nzlo_in-nzlo_out)
   out[2*(((nzhi_in-nzlo_in+2-nupper-slabflag+blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1)+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];
   }else{
   if(blockIdx.x<nzlo_in-nzlo_out)
   out[2*(((blockIdx.x+2*(nzhi_in+1)-nzlo_in-nzhi_out)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1)+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];   
   }
   if(blockIdx.x<nzhi_out-nzhi_in)
   out[2*((((blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+(nzhi_out-nzlo_in))*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];
 }

__global__ void initfftdata_y_kernel(PPPM_FLOAT* in, FFT_FLOAT* out)
{
   if(blockIdx.y<nylo_in-nylo_out)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+(2*(nyhi_in+1)-nylo_in-nyhi_out)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzlo_in-nzlo_out)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];
   if(blockIdx.y<nyhi_out-nyhi_in)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzlo_in-nzlo_out)*(nyhi_out-nylo_out+1)+blockIdx.y+(nyhi_out-nylo_in))*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];
}

__global__ void initfftdata_x_kernel(PPPM_FLOAT* in, FFT_FLOAT* out)
{
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x+nzlo_in-nzlo_out)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x];
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzlo_in-nzlo_out)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];
}

__global__ void initfftdata_yz_kernel(PPPM_FLOAT* in, FFT_FLOAT* out)
{
   if(slabflag){
   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nyhi_out-nyhi_in)
   out[2*((((nzhi_in-nzlo_in+2-nupper-slabflag+blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nylo_in-nylo_out)
   out[2*((((nzhi_in-nzlo_in+2-nupper-slabflag+blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];
   }else{
   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nyhi_out-nyhi_in)
   out[2*((((blockIdx.x+2*(nzhi_in+1)-nzlo_in-nzhi_out)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nylo_in-nylo_out)
   out[2*((((blockIdx.x+2*(nzhi_in+1)-nzlo_in-nzhi_out)*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];
   }
   
   if(blockIdx.x<nzhi_out-nzhi_in)
   if(blockIdx.y<nyhi_out-nyhi_in)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzhi_in-nzlo_out+1)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];

   if(blockIdx.x<nzhi_out-nzhi_in)
   if(blockIdx.y<nylo_in-nylo_out)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzhi_in-nzlo_out+1)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxlo_in-nxlo_out];
}

__global__ void initfftdata_xz_kernel(PPPM_FLOAT* in, FFT_FLOAT* out)
{
   if(blockIdx.x<nzhi_out-nzhi_in)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x+nzhi_in-nzlo_out+1)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.x<nzhi_out-nzhi_in)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzhi_in-nzlo_out+1)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];

   if(slabflag)
   {
   if(blockIdx.x<nzlo_in-nzlo_out)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*((((nzhi_in-nzlo_in+2-nupper-slabflag+blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*((((nzhi_in-nzlo_in+2-nupper-slabflag+blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];
   }else{
   if(blockIdx.x<nzlo_in-nzlo_out)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*((((blockIdx.x+2*(nzhi_in+1)-nzlo_in-nzhi_out)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*((((blockIdx.x+2*(nzhi_in+1)-nzlo_in-nzhi_out)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nylo_in-nylo_out)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];
   }
}

__global__ void initfftdata_xy_kernel(PPPM_FLOAT* in, FFT_FLOAT* out)
{
   if(blockIdx.y<nyhi_out-nyhi_in)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x+nzlo_in-nzlo_out)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.y<nyhi_out-nyhi_in)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzlo_in-nzlo_out)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];

   if(blockIdx.y<nylo_in-nylo_out)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x+nzlo_in-nzlo_out)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.y<nylo_in-nylo_out)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzlo_in-nzlo_out)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];
}

__global__ void initfftdata_xyz_kernel(PPPM_FLOAT* in, FFT_FLOAT* out)
{
   if(blockIdx.x<nzhi_out-nzhi_in)
   if(blockIdx.y<nyhi_out-nyhi_in)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x+nzhi_in-nzlo_out+1)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.x<nzhi_out-nzhi_in)
   if(blockIdx.y<nyhi_out-nyhi_in)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzhi_in-nzlo_out+1)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];

   if(blockIdx.x<nzhi_out-nzhi_in)
   if(blockIdx.y<nylo_in-nylo_out)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x+nzhi_in-nzlo_out+1)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.x<nzhi_out-nzhi_in)
   if(blockIdx.y<nylo_in-nylo_out)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*(((blockIdx.x*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x+nzhi_in-nzlo_out+1)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];

   if(slabflag){
   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nyhi_out-nyhi_in)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*((((nzhi_in-nzlo_in+2-nupper-slabflag+blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nyhi_out-nyhi_in)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*((((nzhi_in-nzlo_in+2-nupper-slabflag+blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nylo_in-nylo_out)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*((((nzhi_in-nzlo_in+2-nupper-slabflag+blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nylo_in-nylo_out)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*((((nzhi_in-nzlo_in+2-nupper-slabflag+blockIdx.x)*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];
   }else{
   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nyhi_out-nyhi_in)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*((((blockIdx.x+2*(nzhi_in+1)-nzlo_in-nzhi_out)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nyhi_out-nyhi_in)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*((((blockIdx.x+2*(nzhi_in+1)-nzlo_in-nzhi_out)*(nyhi_in-nylo_in+1)+blockIdx.y)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y+nyhi_in-nylo_out+1)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nylo_in-nylo_out)
   if(threadIdx.x<nxlo_in-nxlo_out)
   out[2*((((blockIdx.x+2*(nzhi_in+1)-nzlo_in-nzhi_out)*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x+2*(nxhi_in+1)-nxlo_in-nxhi_out)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x];

   if(blockIdx.x<nzlo_in-nzlo_out)
   if(blockIdx.y<nylo_in-nylo_out)
   if(threadIdx.x<nxhi_out-nxhi_in)
   out[2*((((blockIdx.x+2*(nzhi_in+1)-nzlo_in-nzhi_out)*(nyhi_in-nylo_in+1)+blockIdx.y+2*(nyhi_in+1)-nylo_in-nyhi_out)*(nxhi_in-nxlo_in+1))+threadIdx.x)]+=in[(((blockIdx.x)*(nyhi_out-nylo_out+1)+blockIdx.y)*(nxhi_out-nxlo_out+1))+threadIdx.x+nxhi_in-nxlo_out+1];
   }
}
