
/*******************************************************************************
*
* File swinit.c
*
* Copyright (C) 2006 Martin Luescher
*
* This software is distributed under the terms of the GNU General Public
* License (GPL)
*
* Utility programs for the global and the block Pauli fields
*
* The externally accessible functions are
*
*   void alloc_sw(void)
*     Allocates the single-precision Pauli field sw and initializes
*     the matrices to unity
*
*   void alloc_swd(void)
*     Allocates the double-precision Pauli field swd and initializes
*     the matrices to unity
*
*   void free_sw(void)
*     Frees the single-precision Pauli field sw
*
*   void free_swd(void)
*     Frees the double-precision Pauli field swd
*
*   void assign_swd2sw(void)
*     Assigns the double-precision Pauli field swd to the single-precision
*     field sw
*
*   int invert_swd(ptset_t set)
*     Inverts the Pauli matrices in the field swd[] on the specified point
*     set. The program returns 0 if all matrices could be safely inverted
*     and 1 otherwise (see pauli_dble.c)
*
*   int invert_blk_swd(grid_t blk_grid,int n,ptset_t set)
*     Inverts the Pauli matrices in the field b.swd[] on the specified point
*     set, where b is the n'th block in the specified block grid. The program
*     returns 0 if all matrices could be safely inverted and 1 otherwise (see
*     pauli_dble.c)
*     
* Notes:
*
* All these programs except the last one act globally and must be called
* simultaneously on all processes. The value returned by invert_swd() is
* the same on all processes, while the flag returned by invert_blk_swd()
* refers to the local process only
*
*******************************************************************************/

#define SWINIT_C

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

#if (defined SSE)
#include "sse.h"
#endif

static const pauli sw0={{0.0f}};
static const pauli_dble swd0={{0.0}};


void alloc_sw(void)
{
   pauli *pa,*pam,unity;

   if (sw!=NULL)
      return;

   error_root(sizeof(pauli)!=(36*sizeof(float)),1,"alloc_sw [swinit.c]",
              "The pauli structures are not properly packed");
   
   sw=amalloc(2*VOLUME*sizeof(pauli),ALIGN);
   error(sw==NULL,1,"alloc_sw [swinit.c]",
         "Unable to allocate the global sw array");

   unity=sw0;
   unity.u[0]=1.0f;
   unity.u[1]=1.0f;
   unity.u[2]=1.0f;
   unity.u[3]=1.0f;
   unity.u[4]=1.0f;
   unity.u[5]=1.0f;

   pa=sw;
   pam=pa+2*VOLUME;

   for (;pa<pam;pa++)
      *pa=unity;
}


void alloc_swd(void)
{
   pauli_dble *pa,*pam,unity;

   if (swd!=NULL)
      return;

   error_root(sizeof(pauli_dble)!=(36*sizeof(double)),1,"alloc_swd [swinit.c]",
              "The pauli_dble structures are not properly packed");
   
   swd=amalloc(2*VOLUME*sizeof(pauli_dble),ALIGN);
   error(swd==NULL,1,"alloc_swd [swinit.c]",
         "Unable to allocate the global swd array");

   unity=swd0;
   unity.u[0]=1.0;
   unity.u[1]=1.0;
   unity.u[2]=1.0;
   unity.u[3]=1.0;
   unity.u[4]=1.0;
   unity.u[5]=1.0;

   pa=swd;
   pam=pa+2*VOLUME;

   for (;pa<pam;pa++)
      *pa=unity;
}


void free_sw(void)
{
   if (sw!=NULL)
   {
      afree(sw);
      sw=NULL;
      set_flags(FREED_SW);
   }
}


void free_swd(void)
{
   if (swd!=NULL)
   {
      afree(swd);
      swd=NULL;
      set_flags(FREED_SWD);
   }
}


void assign_swd2sw(void)
{
   error((sw==NULL)||(swd==NULL),1,"assign_swd2sw [swinit.c]",
          "sw or swd is not allocated");

   assign_pauli(2*VOLUME,swd,sw);
   set_flags(ASSIGNED_SWD2SW);
}


static int iswd(int vol,int icom,pauli_dble *pa)
{
   int n,ifail;
   pauli_dble *pam;

   ifail=0;
   pam=pa+vol;

   for (;pa<pam;pa++)
   {
#if (defined SSE)
      pa+=2;
      _prefetch_pauli_dble(pa);
      pa-=2;
#endif      
      ifail|=inv_pauli_dble(pa,pa);
   }

   if (icom==1)
   {
      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;
}


int invert_swd(ptset_t set)
{
   int vol,iprms[1];
   pauli_dble *pa;

   iprms[0]=(int)(set);
   MPI_Bcast(iprms,1,MPI_INT,0,MPI_COMM_WORLD);

   error((iprms[0]!=(int)(set))||(swd==NULL),1,"invert_swd [swinit.c]",
         "Parameter is not global or swd is not allocated");

   if (set==EVEN_PTS)
   {
      error_root(query_flags(SWD_E_INVERTED)!=0,1,"invert_swd [swinit.c]",
                 "Attempt to invert swd more than once");
      vol=VOLUME;
      pa=swd;
      set_flags(INVERTED_SWD_E);
   }
   else if (set==ODD_PTS)
   {
      error_root(query_flags(SWD_O_INVERTED)!=0,1,"invert_swd [swinit.c]",
                 "Attempt to invert swd more than once");
      vol=VOLUME;
      pa=swd+VOLUME;
      set_flags(INVERTED_SWD_O);
   }
   else if (set==ALL_PTS)
   {
      error_root((query_flags(SWD_E_INVERTED)!=0)||
                 (query_flags(SWD_O_INVERTED)!=0),1,"invert_swd [swinit.c]",
                 "Attempt to invert swd more than once");
      vol=2*VOLUME;
      pa=swd;
      set_flags(INVERTED_SWD_E);
      set_flags(INVERTED_SWD_O);
   }
   else
      return 0;

   return iswd(vol,1,pa);
}


int invert_blk_swd(blk_grid_t grid,int n,ptset_t set)
{
   int nb,isw,vol;
   pauli_dble *pa;
   block_t *b;

   b=blk_list(grid,&nb,&isw);
   if (error_loc((b==NULL)||(n<0)||(n>=nb),1,"invert_blk_swd [swinit.c]",
                 "Block grid is not allocated, or block number out of range"))
      return 0;
   b+=n;
   
   if (set==EVEN_PTS)
   {
      if (error_loc((query_blk_flags(grid,SWD_E_INVERTED,n)!=0)||
                    ((*b).swd==NULL),1,"invert_blk_swd [swinit.c]",
                    "Block swd already inverted or not allocated"))
         return 0;
      
      vol=(*b).vol;
      pa=(*b).swd;
      set_blk_flags(grid,INVERTED_SWD_E,n);
   }
   else if (set==ODD_PTS)
   {
      if (error_loc((query_blk_flags(grid,SWD_O_INVERTED,n)!=0)||
                    ((*b).swd==NULL),1,"invert_blk_swd [swinit.c]",
                    "Block swd already inverted or not allocated"))
         return 0;
      
      vol=(*b).vol;
      pa=(*b).swd+(*b).vol;
      set_blk_flags(grid,INVERTED_SWD_O,n);
   }
   else if (set==ALL_PTS)
   {
      if (error_loc((query_blk_flags(grid,SWD_E_INVERTED,n)!=0)||
                    (query_blk_flags(grid,SWD_O_INVERTED,n)!=0)||
                    ((*b).swd==NULL),1,"invert_blk_swd [swinit.c]",
                    "Block swd already inverted or not allocated"))
         return 0;
      
      vol=2*(*b).vol;
      pa=(*b).swd;
      set_blk_flags(grid,INVERTED_SWD_E,n);
      set_blk_flags(grid,INVERTED_SWD_O,n);
   }
   else
      return 0;

   return iswd(vol,0,pa);
}
