
/*******************************************************************************
*
* File blk_umap.c
*
* Copyright (C) 2006 Martin Luescher
*
* This software is distributed under the terms of the GNU General Public
* License (GPL)
*
* Copying of the gauge fields to and from the blocks in a block grid
*
* The externally accessible functions are
*
*   void assign_u2ubgr(blk_grid_t grid)
*     Assigns the global single-precision gauge field to the corresponding
*     single-precision fields in the specified grid
*
*   void assign_ud2ubgr(blk_grid_t grid)
*     Assigns the global double-precision gauge field to the corresponding
*     single-precision fields in the specified grid
*
*   void assign_ud2udbgr(blk_grid_t grid)
*     Assigns the global double-precision gauge field to the corresponding
*     double-precision fields in the specified grid
*
*   void swap_active(blk_grid_t grid)
*     Exchanges the active double-precision link variables on the blocks of
*     the specified block grid with the corresponding variables of the
*     global double-precision gauge field
*
*   void assign_udb2ub(blk_grid_t grid,int n)
*     Assigns the double-precision link variables on the block number n
*     of the specified block grid to the corresponding single-precision
*     variables. The operation is applied to all blocks in the grid if n
*     is set to the number of blocks in the local lattice
*
* Notes:
*
* Except for assign_udb2ub(), all programs in this module perform global
* operations and must be called on all processes simultaneously
*
* The assignment programs map all the field variables on the blocks and
* their boundaries. An exception to this rule are the block field variables
* on the links that stick out of the block, which are left untouched
*
*******************************************************************************/

#define BLK_UMAP_C

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "mpi.h"
#include "start.h"
#include "misc.h"
#include "block.h"
#include "global.h"

#if (defined SSE)
static su3_dble ws __attribute__ ((aligned (16)));
#else
static su3_dble ws;
#endif


static int check_parms(blk_grid_t grid,int nb)
{
   int iprms[2];

   iprms[0]=(int)(grid);
   iprms[1]=nb;

   MPI_Bcast(iprms,2,MPI_INT,0,MPI_COMM_WORLD);

   if ((iprms[0]!=(int)(grid))||(iprms[1]!=nb))
      return 1;
   else
      return 0;
}


static void cp_ubb(bndry_t *bb)
{
   int ifc,n;
   int np,nbf,saddr,raddr,tag;
   float *sbuf,*rbuf;
   MPI_Status stat;

   ifc=(*bb).ifc;
   n=(*bb).vol/2;
   np=(cpr[0]+cpr[1]+cpr[2]+cpr[3]+ifc)&0x1;
   saddr=npr[ifc^0x1];
   raddr=npr[ifc];

   sbuf=(float*)((*bb).u);
   rbuf=(float*)((*bb).u+n);
   nbf=18*n;
   tag=mpi_tag();

   if (np==0)
   {
      MPI_Send(sbuf,nbf,MPI_FLOAT,saddr,tag,MPI_COMM_WORLD);
      MPI_Recv(rbuf,nbf,MPI_FLOAT,raddr,tag,MPI_COMM_WORLD,&stat);
   }
   else
   {
      MPI_Recv(rbuf,nbf,MPI_FLOAT,raddr,tag,MPI_COMM_WORLD,&stat);
      MPI_Send(sbuf,nbf,MPI_FLOAT,saddr,tag,MPI_COMM_WORLD);
   }
}


static void assign_u2ub(block_t *b)
{
   int mu,ifc,n;
   int ix,iy,*imb,*ipp;
   su3 *ub,*u,*um;
   bndry_t *bb;

   ub=(*b).u[0];

   for (ix=(*b).volh;ix<(*b).vol;ix++)
   {
      iy=(*b).imb[ix];
      u=pu[iy][0];

      for (mu=0;mu<4;mu++)
      {
         if ((*b).iup[ix][mu]<(*b).vol)
            *ub=*u;
         ub+=1;
         u+=1;

         if ((*b).idn[ix][mu]<(*b).vol)
            *ub=*u;
         ub+=1;
         u+=1;
      }
   }

   bb=(*b).bb;

   if (bb!=NULL)
   {
      for (ifc=0;ifc<8;ifc++)
      {
         n=(*bb).vol/2;
         imb=(*bb).imb+n;
         ub=(*bb).u;
         if ((*bb).ibn==0)
            ub+=n;
         um=ub+n;

         for (;ub<um;ub++)
         {
            *ub=*(pu[*imb][0]+ifc);
            imb+=1;
         }

         if ((*bb).ibn==1)
            cp_ubb(bb);

         ipp=(*bb).ipp;
         ub=(*bb).u;
         um=ub+n;

         for (;ub<um;ub++)
         {
            ix=(*b).imb[*ipp];
            *ub=*(pu[ix][0]+(ifc^0x1));
            ipp+=1;
         }

         bb+=1;
      }
   }
}


void assign_u2ubgr(blk_grid_t grid)
{
   int nb,isw;
   block_t *b,*bm;

   b=blk_list(grid,&nb,&isw);
   bm=b+nb;

   error(check_parms(grid,nb)!=0,1,
         "assign_u2ubgr [blk_umap.c]","Parameters are not global");

   error(check_blk_flags(grid,ASSIGN_U2UBGR,nb),1,
         "assign_u2ubgr [blk_umap.c]","Fields are not in proper condition");

   for (;b<bm;b++)
      assign_u2ub(b);

   set_blk_flags(grid,ASSIGNED_U2UBGR,nb);
}


static void set_ud2u(su3_dble *ud,su3 *u)
{
   float *r;
   double *rd;

   r=(float*)(u);
   rd=(double*)(ud);

   r[ 0]=(float)(rd[ 0]);
   r[ 1]=(float)(rd[ 1]);
   r[ 2]=(float)(rd[ 2]);
   r[ 3]=(float)(rd[ 3]);
   r[ 4]=(float)(rd[ 4]);
   r[ 5]=(float)(rd[ 5]);

   r[ 6]=(float)(rd[ 6]);
   r[ 7]=(float)(rd[ 7]);
   r[ 8]=(float)(rd[ 8]);
   r[ 9]=(float)(rd[ 9]);
   r[10]=(float)(rd[10]);
   r[11]=(float)(rd[11]);

   r[12]=(float)(rd[12]);
   r[13]=(float)(rd[13]);
   r[14]=(float)(rd[14]);
   r[15]=(float)(rd[15]);
   r[16]=(float)(rd[16]);
   r[17]=(float)(rd[17]);
}


static void assign_ud2ub(block_t *b)
{
   int mu,ifc,n;
   int ix,iy,*imb,*ipp;
   su3 *ub,*um;
   su3_dble *u;
   bndry_t *bb;

   ub=(*b).u[0];

   for (ix=(*b).volh;ix<(*b).vol;ix++)
   {
      iy=(*b).imb[ix];
      u=pud[iy][0];

      for (mu=0;mu<4;mu++)
      {
         if ((*b).iup[ix][mu]<(*b).vol)
            set_ud2u(u,ub);
         ub+=1;
         u+=1;

         if ((*b).idn[ix][mu]<(*b).vol)
            set_ud2u(u,ub);
         ub+=1;
         u+=1;
      }
   }

   bb=(*b).bb;

   if (bb!=NULL)
   {
      for (ifc=0;ifc<8;ifc++)
      {
         n=(*bb).vol/2;

         imb=(*bb).imb+n;
         ub=(*bb).u;
         if ((*bb).ibn==0)
            ub+=n;
         um=ub+n;

         for (;ub<um;ub++)
         {
            set_ud2u(pud[*imb][0]+ifc,ub);
            imb+=1;
         }

         if ((*bb).ibn==1)
            cp_ubb(bb);

         ipp=(*bb).ipp;
         ub=(*bb).u;
         um=ub+n;

         for (;ub<um;ub++)
         {
            ix=(*b).imb[*ipp];
            set_ud2u(pud[ix][0]+(ifc^0x1),ub);
            ipp+=1;
         }

         bb+=1;
      }
   }
}


void assign_ud2ubgr(blk_grid_t grid)
{
   int nb,isw;
   block_t *b,*bm;

   b=blk_list(grid,&nb,&isw);
   bm=b+nb;

   error(check_parms(grid,nb)!=0,1,
         "assign_ud2ubgr [blk_umap.c]","Parameters are not global");

   error(check_blk_flags(grid,ASSIGN_UD2UBGR,nb),1,
         "assign_ud2ubgr [blk_umap.c]","Fields are not in proper condition");

   for (;b<bm;b++)
      assign_ud2ub(b);

   set_blk_flags(grid,ASSIGNED_UD2UBGR,nb);
}


static void cp_udbb(bndry_t *bb)
{
   int ifc,n;
   int np,nbf,saddr,raddr,tag;
   double *sbuf,*rbuf;
   MPI_Status stat;

   ifc=(*bb).ifc;
   n=(*bb).vol/2;
   np=(cpr[0]+cpr[1]+cpr[2]+cpr[3]+ifc)&0x1;
   saddr=npr[ifc^0x1];
   raddr=npr[ifc];

   sbuf=(double*)((*bb).ud);
   rbuf=(double*)((*bb).ud+n);
   nbf=18*n;
   tag=mpi_tag();

   if (np==0)
   {
      MPI_Send(sbuf,nbf,MPI_DOUBLE,saddr,tag,MPI_COMM_WORLD);
      MPI_Recv(rbuf,nbf,MPI_DOUBLE,raddr,tag,MPI_COMM_WORLD,&stat);
   }
   else
   {
      MPI_Recv(rbuf,nbf,MPI_DOUBLE,raddr,tag,MPI_COMM_WORLD,&stat);
      MPI_Send(sbuf,nbf,MPI_DOUBLE,saddr,tag,MPI_COMM_WORLD);
   }
}


static void assign_ud2udb(block_t *b)
{
   int mu,ifc,n;
   int ix,iy,*imb,*ipp;
   su3_dble *ub,*u,*um;
   bndry_t *bb;

   ub=(*b).ud[0];

   for (ix=(*b).volh;ix<(*b).vol;ix++)
   {
      iy=(*b).imb[ix];
      u=pud[iy][0];

      for (mu=0;mu<4;mu++)
      {
         if ((*b).iup[ix][mu]<(*b).vol)
            *ub=*u;
         ub+=1;
         u+=1;

         if ((*b).idn[ix][mu]<(*b).vol)
            *ub=*u;
         ub+=1;
         u+=1;
      }
   }

   bb=(*b).bb;

   if (bb!=NULL)
   {
      for (ifc=0;ifc<8;ifc++)
      {
         n=(*bb).vol/2;

         imb=(*bb).imb+n;
         ub=(*bb).ud;
         if ((*bb).ibn==0)
            ub+=n;
         um=ub+n;

         for (;ub<um;ub++)
         {
            *ub=*(pud[*imb][0]+ifc);
            imb+=1;
         }

         if ((*bb).ibn==1)
            cp_udbb(bb);

         ipp=(*bb).ipp;
         ub=(*bb).ud;
         um=ub+n;

         for (;ub<um;ub++)
         {
            ix=(*b).imb[*ipp];
            *ub=*(pud[ix][0]+(ifc^0x1));
            ipp+=1;
         }

         bb+=1;
      }
   }
}


void assign_ud2udbgr(blk_grid_t grid)
{
   int nb,isw;
   block_t *b,*bm;

   b=blk_list(grid,&nb,&isw);
   bm=b+nb;

   error(check_parms(grid,nb)!=0,1,
         "assign_ud2udbgr [blk_umap.c]","Parameters are not global");

   error(check_blk_flags(grid,ASSIGN_UD2UDBGR,nb),1,
         "assign_ud2udbgr [blk_umap.c]","Fields are not in proper condition");

   for (;b<bm;b++)
      assign_ud2udb(b);

   set_blk_flags(grid,ASSIGNED_UD2UDBGR,nb);
}


static int is_on_bnd(block_t *b,int ix)
{
   int vol,*up,*dn;

   vol=(*b).vol;

   if (ix>=vol)
      return 1;

   up=(*b).iup[ix];

   if ((up[0]>=vol)||(up[1]>=vol)||(up[2]>=vol)||(up[3]>=vol))
      return 1;

   dn=(*b).idn[ix];

   if ((dn[0]>=vol)||(dn[1]>=vol)||(dn[2]>=vol)||(dn[3]>=vol))
      return 1;

   return 0;
}


static void swap_ud(block_t *b)
{
   int ix,iy,mu;
   int *up,*dn,ibx;
   su3_dble *u0,*ub,*u;

   ub=(*b).ud[0];
   u0=pud[VOLUME/2][0];

   for (ix=(*b).volh;ix<(*b).vol;ix++)
   {
      ibx=is_on_bnd(b,ix);
      up=(*b).iup[ix];
      dn=(*b).idn[ix];

      iy=(*b).imb[ix];
      u=u0+8*(iy-(VOLUME/2));

      for (mu=0;mu<4;mu++)
      {
         if ((ibx==0)||(is_on_bnd(b,up[mu])==0))
         {
            ws=*ub;
            *ub=*u;
            *u=ws;
         }
         u+=1;
         ub+=1;

         if ((ibx==0)||(is_on_bnd(b,dn[mu])==0))
         {
            ws=*ub;
            *ub=*u;
            *u=ws;
         }
         u+=1;
         ub+=1;
      }
   }
}


void swap_active(blk_grid_t grid)
{
   int nb,isw;
   block_t *b,*bm;

   b=blk_list(grid,&nb,&isw);
   bm=b+nb;

   error(check_parms(grid,nb)!=0,1,
         "swap_active [blk_umap.c]","Parameters are not global");

   error(check_blk_flags(grid,SWAP_ACTIVE,nb),1,
         "swap_active [blk_umap.c]","Fields are not in proper condition");

   for (;b<bm;b++)
      swap_ud(b);

   set_blk_flags(grid,SWAPPED_ACTIVE,nb);
}


static void cp_udb2ub(block_t *b)
{
   int ix,mu,ifc;
   su3 *u;
   su3_dble *ud;
   bndry_t *bb;

   u=(*b).u[0];
   ud=(*b).ud[0];

   for (ix=(*b).volh;ix<(*b).vol;ix++)
   {
      for (mu=0;mu<4;mu++)
      {
         if ((*b).iup[ix][mu]<(*b).vol)
            set_ud2u(ud,u);
         u+=1;
         ud+=1;

         if ((*b).idn[ix][mu]<(*b).vol)
            set_ud2u(ud,u);
         u+=1;
         ud+=1;
      }
   }

   bb=(*b).bb;

   if (bb!=NULL)
   {
      for (ifc=0;ifc<8;ifc++)
      {
         u=(*bb).u;
         ud=(*bb).ud;

         for (ix=0;ix<(*bb).vol;ix++)
         {
            set_ud2u(ud,u);
            u+=1;
            ud+=1;
         }

         bb+=1;
      }
   }
}


void assign_udb2ub(blk_grid_t grid,int n)
{
   int nb,isw;
   block_t *b,*bm;

   if (error_loc(check_blk_flags(grid,ASSIGN_UD2U,n)!=0,1,
                 "assign_udb2ub [blk_umap.c]",
                 "Fields are not in proper condition"))
      return;

   b=blk_list(grid,&nb,&isw);

   if (n<nb)
      cp_udb2ub(b+n);
   else
   {
      bm=b+nb;

      for (;b<bm;b++)
         cp_udb2ub(b);
   }

   set_blk_flags(grid,ASSIGNED_UD2U,n);
}
