
/*******************************************************************************
*
* File blk_swmap.c
*
* Copyright (C) 2005 Martin Luescher
*
* This software is distributed under the terms of the GNU General Public
* License (GPL)
*
* Copying of the SW fields to and from the blocks in a block grid
*
* The externally accessible functions are
*
*   int assign_swd2swbgr(blk_grid_t grid,ptset_t set)
*     Assigns the global double-precision SW field to the corresponding
*     single-precision fields in the specified grid. On the given point
*     set the copied Pauli matrices are inverted before assignment and
*     the program returns 0 or 1 depending on whether the inversions were
*     safe or not
*
*   int assign_swd2swdbgr(blk_grid_t grid,ptset_t set)
*     Assigns the global double-precision SW field to the corresponding
*     double-precision fields in the specified grid. On the given point
*     set the copied Pauli matrices are inverted before assignment and
*     the program returns 0 or 1 depending on whether the inversions were
*     safe or not
*
*   int assign_swdb2swb(blk_grid_t grid,int n,ptset_t set)
*     Assigns the double-precision SW field on the block number n of the
*     specified block grid to the corresponding single-precision field.
*     The operation is applied to all blocks in the grid if n is set to
*     the number of blocks in the local lattice. On the given point set
*     the copied Pauli matrices are inverted before assignment and the
*     program returns 0 or 1 depending on whether the inversions were safe
*     or not
*
* Notes:
*
* Except for assign_swdb2swb(), all programs in this module perform global
* operations and must be called on all processes simultaneously
*
* The possible point sets are defined in start.h. Independently of the
* specified set, the source field is left unchanged, i.e. the inversions
* are performed "on the fly". No inversions are performed if set=NO_PTS
*
* A matrix inversion is considered unsafe if the Frobenius condition
* number of the matrix exceeds 100 (see pauli_dble.c)
*
*******************************************************************************/

#define BLK_SWMAP_C

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

#if (defined SSE)
static pauli_dble m[2] __attribute__ ((aligned (16)));
#else
static pauli_dble m[2];
#endif


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

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

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

   if ((iprms[0]!=(int)(grid))||(iprms[1]!=(int)(set)))
      return 1;

   if ((grid<0)||(grid>=BLK_GRIDS)||(set<0)||(set>=PT_SETS))
      return 1;

   return 0;
}


static int cp_swd2swb(block_t *b,ptset_t set)
{
   int *imb,ifail;
   pauli *pb,*pm;
   pauli_dble *p;

   pb=(*b).sw;
   pm=pb+(*b).vol;
   imb=(*b).imb;
   ifail=0;

   for (;pb<pm;pb+=2)
   {
      p=swd+2*(*imb);

      if ((set==ALL_PTS)||(set==EVEN_PTS))
      {
         ifail|=inv_pauli_dble(p,m);
         ifail|=inv_pauli_dble(p+1,m+1);
         assign_pauli(2,m,pb);
      }
      else
         assign_pauli(2,p,pb);

      imb+=1;
   }

   pm+=(*b).vol;

   for (;pb<pm;pb+=2)
   {
      p=swd+2*(*imb);

      if ((set==ALL_PTS)||(set==ODD_PTS))
      {
         ifail|=inv_pauli_dble(p,m);
         ifail|=inv_pauli_dble(p+1,m+1);
         assign_pauli(2,m,pb);
      }
      else
         assign_pauli(2,p,pb);

      imb+=1;
   }

   return ifail;
}


int assign_swd2swbgr(blk_grid_t grid,ptset_t set)
{
   int n,nb,isw,ifail;
   block_t *b,*bm;

   error(check_parms(grid,set)!=0,1,
         "assign_swd2swbgr [blk_swmap.c]","Improper parameters");

   b=blk_list(grid,&nb,&isw);
   bm=b+nb;
   ifail=0;
      
   error(check_blk_flags(grid,ASSIGN_SWD2SWBGR,nb)!=0,1,
         "assign_swd2swbgr [blk_swmap.c]",
         "Fields are not in proper condition");
   
   for (;b<bm;b++)
      ifail|=cp_swd2swb(b,set);

   set_blk_flags(grid,ASSIGNED_SWD2SWBGR,nb);

   if ((set==ALL_PTS)||(set==EVEN_PTS))
      set_blk_flags(grid,INVERTED_SW_E,nb);
   if ((set==ALL_PTS)||(set==ODD_PTS))
      set_blk_flags(grid,INVERTED_SW_O,nb);

   n=ifail;
   MPI_Reduce(&n,&ifail,1,MPI_INT,MPI_MAX,0,MPI_COMM_WORLD); 
   MPI_Bcast(&ifail,1,MPI_INT,0,MPI_COMM_WORLD);
   
   return ifail;
}


static int cp_swd2swdb(block_t *b,ptset_t set)
{
   int *imb,ifail;
   pauli_dble *pb,*p,*pm;

   pb=(*b).swd;
   pm=pb+(*b).vol;
   imb=(*b).imb;
   ifail=0;

   for (;pb<pm;pb+=2)
   {
      p=swd+2*(*imb);

      if ((set==ALL_PTS)||(set==EVEN_PTS))
      {
         ifail|=inv_pauli_dble(p,pb);
         ifail|=inv_pauli_dble(p+1,pb+1);
      }
      else
      {
         *pb=*p;
         *(pb+1)=*(p+1);
      }

      imb+=1;
   }

   pm+=(*b).vol;

   for (;pb<pm;pb+=2)
   {
      p=swd+2*(*imb);

      if ((set==ALL_PTS)||(set==ODD_PTS))
      {
         ifail|=inv_pauli_dble(p,pb);
         ifail|=inv_pauli_dble(p+1,pb+1);
      }
      else
      {
         *pb=*p;
         *(pb+1)=*(p+1);
      }

      imb+=1;
   }

   return ifail;
}


int assign_swd2swdbgr(blk_grid_t grid,ptset_t set)
{
   int n,nb,isw,ifail;
   block_t *b,*bm;

   error(check_parms(grid,set),1,
         "assign_swd2swdbgr [blk_swmap.c]","Improper parameters");

   b=blk_list(grid,&nb,&isw);
   bm=b+nb;
   ifail=0;   
   
   error(check_blk_flags(grid,ASSIGN_SWD2SWDBGR,nb)!=0,1,
         "assign_swd2swdbgr [blk_swmap.c]",
         "Fields are not in proper condition");

   error_root(((set==ALL_PTS)||(set==EVEN_PTS))&&(query_flags(SWD_E_INVERTED)),
              1,"assign_swd2swdbgr [blk_swmap.c]",
              "Attempt to invert swd more than once");

   error_root(((set==ALL_PTS)||(set==ODD_PTS))&&(query_flags(SWD_O_INVERTED)),
              1,"assign_swd2swdbgr [blk_swmap.c]",
              "Attempt to invert swd more than once");
   
   for (;b<bm;b++)
      ifail|=cp_swd2swdb(b,set);

   set_blk_flags(grid,ASSIGNED_SWD2SWDBGR,nb);

   if ((set==ALL_PTS)||(set==EVEN_PTS))
      set_blk_flags(grid,INVERTED_SWD_E,nb);
   if ((set==ALL_PTS)||(set==ODD_PTS))
      set_blk_flags(grid,INVERTED_SWD_O,nb);

   n=ifail;
   MPI_Reduce(&n,&ifail,1,MPI_INT,MPI_MAX,0,MPI_COMM_WORLD); 
   MPI_Bcast(&ifail,1,MPI_INT,0,MPI_COMM_WORLD);
   
   return ifail;
}


static int cp_swdb2swb(block_t *b,ptset_t set)
{
   int ifail;
   pauli *pb,*pm;
   pauli_dble *p;

   pb=(*b).sw;
   pm=pb+(*b).vol;
   p=(*b).swd;
   ifail=0;

   for (;pb<pm;pb+=2)
   {
      if ((set==ALL_PTS)||(set==EVEN_PTS))
      {
         ifail|=inv_pauli_dble(p,m);
         ifail|=inv_pauli_dble(p+1,m+1);
         assign_pauli(2,m,pb);
      }
      else
         assign_pauli(2,p,pb);

      p+=2;
   }

   pm+=(*b).vol;

   for (;pb<pm;pb+=2)
   {
      if ((set==ALL_PTS)||(set==ODD_PTS))
      {
         ifail|=inv_pauli_dble(p,m);
         ifail|=inv_pauli_dble(p+1,m+1);
         assign_pauli(2,m,pb);
      }
      else
         assign_pauli(2,p,pb);

      p+=2;
   }

   return ifail;
}


int assign_swdb2swb(blk_grid_t grid,int n,ptset_t set)
{
   int nb,isw,ifail;
   block_t *b,*bm;

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

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

   if (n<nb)
      ifail|=cp_swdb2swb(b+n,set);
   else
   {
      bm=b+nb;
      
      for (;b<bm;b++)
         ifail|=cp_swdb2swb(b,set);
   }

   set_blk_flags(grid,ASSIGNED_SWD2SW,n);

   if ((set==ALL_PTS)||(set==EVEN_PTS))
      set_blk_flags(grid,INVERTED_SW_E,n);
   if ((set==ALL_PTS)||(set==ODD_PTS))
      set_blk_flags(grid,INVERTED_SW_O,n);

   return ifail;
}
