
/*******************************************************************************
*
* File Qbnd_dble.c
*
* Copyright (C) 2005 Martin Luescher
*
* This software is distributed under the terms of the GNU General Public
* License (GPL)
*
* Application of the boundary terms Q_dLambda and Q_dLambda* of the hermitian
* Wilson-Dirac operator
*
* The externally accessible functions are
*
*   void Qbnd_dble(block_t *b,int k,int l)
*     Applies the boundary term Q_dLambda to the Weyl field number k on
*     the boundary of the block b and *subtracts* the result from the
*     Dirac field number l on b
*
*   void Qbnds_dble(block_t *b,int k,int l)
*     Applies the boundary term Q_dLambda* to the Dirac field number k on
*     the block b and *assigns* the result to the Weyl field number l on
*     the boundary of b
*
*   void assign_sd2sdbb(block_t *b,int k,int l)
*     Applies the projector theta_dLambda to the global field number k
*     and assigns the result to the Weyl field number l on the boundary
*     of the block b
*
*   void sub_assign_sdbb2sd(block_t *b,int k,int l)
*     Expands the Weyl field number k on the boundary of the block b to
*     a Dirac field psi satisfying theta_dLambda*psi=psi and subtracts
*     psi from the global field number l
*
*   void mulg5_sub_assign_sdbb2sd(block_t *b,int k,int l)
*     Expands the Weyl field number k on the boundary of the block b to
*     a Dirac field psi satisfying theta_dLambda*psi=psi and subtracts
*     dirac5*psi from the global field number l
*
* Notes:
*
* In all cases the Weyl fields at the boundary are the upper two components
* of a Dirac field psi that satisfies theta*psi=psi (see Pbnd.c)
*
* The boundary operators are local to the block and can therefore be called
* locally. A communication may, however, be required in the case of the other
* programs. They act globally and require the block b to have exactly the
* same size and local position on all processes
*
*******************************************************************************/

#define QBND_DBLE_C

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "mpi.h"
#include "su3.h"
#include "start.h"
#include "block.h"
#include "dirac.h"
#include "global.h"

#if (defined SSE)
static weyl_dble chi __attribute__ ((aligned (16)));
#else
static weyl_dble chi;
#endif

static void mul_umat(weyl_dble *s,su3_dble *u,weyl_dble *r)
{
   _su3_multiply((*r).c1,*u,(*s).c1);
   _su3_multiply((*r).c2,*u,(*s).c2);
}

static void mul_uinv(weyl_dble *s,su3_dble *u,weyl_dble *r)
{
   _su3_inverse_multiply((*r).c1,*u,(*s).c1);
   _su3_inverse_multiply((*r).c2,*u,(*s).c2);
}


void Qbnd_dble(block_t *b,int k,int l)
{
   int *ipp;
   su3_dble *u,*um;
   weyl_dble *rsk;
   spinor_dble *psl,*rsl;
   bndry_t *bb;

   if (error_loc((k<0)||(k>=(*(*b).bb).nsd)||(l<0)||(l>=(*b).nsd),1,
                 "Qbnd_dble [Qbnd_dble.c]",
                 "Attempt to access unallocated memory space")!=0)
      return;

/********************************** face -0 ***********************************/

   bb=(*b).bb;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsk=(*bb).sd[k];
   psl=(*b).sd[l];

   for (;u<um;u++)
   {
      mul_uinv(rsk,u,&chi);
      rsk+=1;
      rsl=psl+(*ipp);
      ipp+=1;
      _vector_add_assign((*rsl).c1,chi.c1);
      _vector_add_assign((*rsl).c2,chi.c2);
      _vector_add_assign((*rsl).c3,chi.c1);
      _vector_add_assign((*rsl).c4,chi.c2);
   }

/********************************** face +0 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsk=(*bb).sd[k];

   for (;u<um;u++)
   {
      mul_umat(rsk,u,&chi);
      rsk+=1;
      rsl=psl+(*ipp);
      ipp+=1;
      _vector_add_assign((*rsl).c1,chi.c1);
      _vector_add_assign((*rsl).c2,chi.c2);
      _vector_sub_assign((*rsl).c3,chi.c1);
      _vector_sub_assign((*rsl).c4,chi.c2);
   }

/********************************** face -1 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsk=(*bb).sd[k];

   for (;u<um;u++)
   {
      mul_uinv(rsk,u,&chi);
      rsk+=1;
      rsl=psl+(*ipp);
      ipp+=1;
      _vector_add_assign((*rsl).c1,chi.c1);
      _vector_add_assign((*rsl).c2,chi.c2);
      _vector_i_sub_assign((*rsl).c3,chi.c2);
      _vector_i_sub_assign((*rsl).c4,chi.c1);
   }

/********************************** face +1 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsk=(*bb).sd[k];

   for (;u<um;u++)
   {
      mul_umat(rsk,u,&chi);
      rsk+=1;
      rsl=psl+(*ipp);
      ipp+=1;
      _vector_add_assign((*rsl).c1,chi.c1);
      _vector_add_assign((*rsl).c2,chi.c2);
      _vector_i_add_assign((*rsl).c3,chi.c2);
      _vector_i_add_assign((*rsl).c4,chi.c1);
   }

/********************************** face -2 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsk=(*bb).sd[k];

   for (;u<um;u++)
   {
      mul_uinv(rsk,u,&chi);
      rsk+=1;
      rsl=psl+(*ipp);
      ipp+=1;
      _vector_add_assign((*rsl).c1,chi.c1);
      _vector_add_assign((*rsl).c2,chi.c2);
      _vector_sub_assign((*rsl).c3,chi.c2);
      _vector_add_assign((*rsl).c4,chi.c1);
   }

/********************************** face +2 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsk=(*bb).sd[k];

   for (;u<um;u++)
   {
      mul_umat(rsk,u,&chi);
      rsk+=1;
      rsl=psl+(*ipp);
      ipp+=1;
      _vector_add_assign((*rsl).c1,chi.c1);
      _vector_add_assign((*rsl).c2,chi.c2);
      _vector_add_assign((*rsl).c3,chi.c2);
      _vector_sub_assign((*rsl).c4,chi.c1);
   }


/********************************** face -3 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsk=(*bb).sd[k];

   for (;u<um;u++)
   {
      mul_uinv(rsk,u,&chi);
      rsk+=1;
      rsl=psl+(*ipp);
      ipp+=1;
      _vector_add_assign((*rsl).c1,chi.c1);
      _vector_add_assign((*rsl).c2,chi.c2);
      _vector_i_sub_assign((*rsl).c3,chi.c1);
      _vector_i_add_assign((*rsl).c4,chi.c2);
   }

/********************************** face +3 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsk=(*bb).sd[k];

   for (;u<um;u++)
   {
      mul_umat(rsk,u,&chi);
      rsk+=1;
      rsl=psl+(*ipp);
      ipp+=1;
      _vector_add_assign((*rsl).c1,chi.c1);
      _vector_add_assign((*rsl).c2,chi.c2);
      _vector_i_add_assign((*rsl).c3,chi.c1);
      _vector_i_sub_assign((*rsl).c4,chi.c2);
   }
}


void Qbnds_dble(block_t *b,int k,int l)
{
   int *ipp;
   double r1;
   su3_dble *u,*um;
   weyl_dble *rsl;
   spinor_dble *psk,*rsk;
   bndry_t *bb;

   if (error_loc((k<0)||(k>=(*b).nsd)||(l<0)||(l>=(*(*b).bb).nsd),1,
                 "Qbnds_dble [Qbnd_dble.c]",
                 "Attempt to access unallocated memory space")!=0)
      return;

   r1=-0.5;

/********************************** face -0 ***********************************/

   bb=(*b).bb;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsl=(*bb).sd[l];
   psk=(*b).sd[k];

   for (;u<um;u++)
   {
      rsk=psk+(*ipp);
      ipp+=1;
      _vector_add(chi.c1,(*rsk).c1,(*rsk).c3);
      _vector_add(chi.c2,(*rsk).c2,(*rsk).c4);
      _vector_mul(chi.c1,r1,chi.c1);
      _vector_mul(chi.c2,r1,chi.c2);
      mul_umat(&chi,u,rsl);
      rsl+=1;
   }

/********************************** face +0 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsl=(*bb).sd[l];

   for (;u<um;u++)
   {
      rsk=psk+(*ipp);
      ipp+=1;
      _vector_sub(chi.c1,(*rsk).c1,(*rsk).c3);
      _vector_sub(chi.c2,(*rsk).c2,(*rsk).c4);
      _vector_mul(chi.c1,r1,chi.c1);
      _vector_mul(chi.c2,r1,chi.c2);
      mul_uinv(&chi,u,rsl);
      rsl+=1;
   }

/********************************** face -1 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsl=(*bb).sd[l];

   for (;u<um;u++)
   {
      rsk=psk+(*ipp);
      ipp+=1;
      _vector_i_add(chi.c1,(*rsk).c1,(*rsk).c4);
      _vector_i_add(chi.c2,(*rsk).c2,(*rsk).c3);
      _vector_mul(chi.c1,r1,chi.c1);
      _vector_mul(chi.c2,r1,chi.c2);
      mul_umat(&chi,u,rsl);
      rsl+=1;
   }

/********************************** face +1 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsl=(*bb).sd[l];

   for (;u<um;u++)
   {
      rsk=psk+(*ipp);
      ipp+=1;
      _vector_i_sub(chi.c1,(*rsk).c1,(*rsk).c4);
      _vector_i_sub(chi.c2,(*rsk).c2,(*rsk).c3);
      _vector_mul(chi.c1,r1,chi.c1);
      _vector_mul(chi.c2,r1,chi.c2);
      mul_uinv(&chi,u,rsl);
      rsl+=1;
   }

/********************************** face -2 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsl=(*bb).sd[l];

   for (;u<um;u++)
   {
      rsk=psk+(*ipp);
      ipp+=1;
      _vector_add(chi.c1,(*rsk).c1,(*rsk).c4);
      _vector_sub(chi.c2,(*rsk).c2,(*rsk).c3);
      _vector_mul(chi.c1,r1,chi.c1);
      _vector_mul(chi.c2,r1,chi.c2);
      mul_umat(&chi,u,rsl);
      rsl+=1;
   }

/********************************** face +2 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsl=(*bb).sd[l];

   for (;u<um;u++)
   {
      rsk=psk+(*ipp);
      ipp+=1;
      _vector_sub(chi.c1,(*rsk).c1,(*rsk).c4);
      _vector_add(chi.c2,(*rsk).c2,(*rsk).c3);
      _vector_mul(chi.c1,r1,chi.c1);
      _vector_mul(chi.c2,r1,chi.c2);
      mul_uinv(&chi,u,rsl);
      rsl+=1;
   }


/********************************** face -3 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsl=(*bb).sd[l];

   for (;u<um;u++)
   {
      rsk=psk+(*ipp);
      ipp+=1;
      _vector_i_add(chi.c1,(*rsk).c1,(*rsk).c3);
      _vector_i_sub(chi.c2,(*rsk).c2,(*rsk).c4);
      _vector_mul(chi.c1,r1,chi.c1);
      _vector_mul(chi.c2,r1,chi.c2);
      mul_umat(&chi,u,rsl);
      rsl+=1;
   }

/********************************** face +3 ***********************************/

   bb+=1;
   ipp=(*bb).ipp;
   u=(*bb).ud;
   um=u+(*bb).vol;
   rsl=(*bb).sd[l];

   for (;u<um;u++)
   {
      rsk=psk+(*ipp);
      ipp+=1;
      _vector_i_sub(chi.c1,(*rsk).c1,(*rsk).c3);
      _vector_i_add(chi.c2,(*rsk).c2,(*rsk).c4);
      _vector_mul(chi.c1,r1,chi.c1);
      _vector_mul(chi.c2,r1,chi.c2);
      mul_uinv(&chi,u,rsl);
      rsl+=1;
   }
}


static void cp_wd2wd(int ifc,int vol,weyl_dble *pk,weyl_dble *pl)
{
   int np,nbf,saddr,raddr,tag;
   double *sbuf,*rbuf;
   MPI_Status stat;

   np=(cpr[0]+cpr[1]+cpr[2]+cpr[3]+ifc)&0x1;
   saddr=npr[ifc^0x1];
   raddr=npr[ifc];

   sbuf=(double*)(pk);
   rbuf=(double*)(pl);
   nbf=12*vol;
   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);
   }
}


void assign_sd2sdbb(block_t *b,int k,int l)
{
   int ifc,mu,iprms[10];
   weyl_dble *pl,*pw;
   spinor_dble *pk;
   bndry_t *bb;

   if (NPROC>1)
   {
      for (mu=0;mu<4;mu++)
      {
         iprms[mu]=(*b).bo[mu];
         iprms[mu+4]=(*b).bs[mu];
      }

      iprms[8]=k;
      iprms[9]=l;

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

      for (mu=0;mu<4;mu++)
         if ((iprms[mu]!=(*b).bo[mu])||(iprms[mu+4]!=(*b).bs[mu]))
            break;

      error((mu!=4)||(iprms[8]!=k)||(iprms[9]!=l),1,
             "assign_sd2sdbb [Qbnd_dble.c]",
             "Block positions and sizes, or other parameters are not global");

      if (error_loc((k<0)||(k>=no_sd)||(l<0)||(l>=(*(*b).bb).nsd),1,
          "assign_sd2sdbb [Qbnd_dble.c]",
          "Attempt to access unallocated memory space")!=0)
         return;
   }

   pk=psd[k][0];
   bb=(*b).bb;

   for (ifc=0;ifc<8;ifc++)
   {
      pl=(*bb).sd[l];

      if ((*bb).ibn==0)
         assign_sd2wd[ifc]((*bb).imb,(*bb).vol,pk,pl);
      else
      {
         pw=(weyl_dble*)(pk+VOLUME);
         assign_sd2wd[ifc]((*bb).imb,(*bb).vol,pk,pw);
         cp_wd2wd(ifc,(*bb).vol,pw,pl);
      }

      bb+=1;
   }
}


void sub_assign_sdbb2sd(block_t *b,int k,int l)
{
   int ifc,mu,iprms[10];
   weyl_dble *pk,*pw;
   spinor_dble *pl;
   bndry_t *bb;

   if (NPROC>1)
   {
      for (mu=0;mu<4;mu++)
      {
         iprms[mu]=(*b).bo[mu];
         iprms[mu+4]=(*b).bs[mu];
      }

      iprms[8]=k;
      iprms[9]=l;

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

      for (mu=0;mu<4;mu++)
         if ((iprms[mu]!=(*b).bo[mu])||(iprms[mu+4]!=(*b).bs[mu]))
            break;

      error((mu!=4)||(iprms[8]!=k)||(iprms[9]!=l),1,
            "sub_assign_sdbb2sd [Qbnd_dble.c]",
            "Block positions and sizes, or other parameters are not global");
   }

   if (error_loc((k<0)||(k>=(*(*b).bb).nsd)||(l<0)||(l>=no_sd),1,
                 "sub_assign_sdbb2sd [Qbnd_dble.c]",
                 "Attempt to access unallocated memory space")!=0)
      return;

   pl=psd[l][0];
   bb=(*b).bb;

   for (ifc=0;ifc<8;ifc++)
   {
      pk=(*bb).sd[k];

      if ((*bb).ibn==0)
         sub_assign_wd2sd[ifc]((*bb).imb,(*bb).vol,pk,pl);
      else
      {
         pw=(weyl_dble*)(pl+VOLUME);
         cp_wd2wd(ifc^0x1,(*bb).vol,pk,pw);
         sub_assign_wd2sd[ifc]((*bb).imb,(*bb).vol,pw,pl);
      }

      bb+=1;
   }
}


void mulg5_sub_assign_sdbb2sd(block_t *b,int k,int l)
{
   int ifc,mu,iprms[10];
   weyl_dble *pk,*pw;
   spinor_dble *pl;
   bndry_t *bb;

   if (NPROC>1)
   {
      for (mu=0;mu<4;mu++)
      {
         iprms[mu]=(*b).bo[mu];
         iprms[mu+4]=(*b).bs[mu];
      }

      iprms[8]=k;
      iprms[9]=l;

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

      for (mu=0;mu<4;mu++)
         if ((iprms[mu]!=(*b).bo[mu])||(iprms[mu+4]!=(*b).bs[mu]))
            break;

      error((mu!=4)||(iprms[8]!=k)||(iprms[9]!=l),1,
            "mulg5_sub_assign_sdbb2sd [Qbnd_dble.c]",
            "Block positions and sizes, or other parameters are not global");
   }

   if (error_loc((k<0)||(k>=(*(*b).bb).nsd)||(l<0)||(l>=no_sd),1,
                 "mulg5_sub_assign_sdbb2sd [Qbnd_dble.c]",
                 "Attempt to access unallocated memory space")!=0)
      return;

   pl=psd[l][0];
   bb=(*b).bb;

   for (ifc=0;ifc<8;ifc++)
   {
      pk=(*bb).sd[k];

      if ((*bb).ibn==0)
         mulg5_sub_assign_wd2sd[ifc]((*bb).imb,(*bb).vol,pk,pl);
      else
      {
         pw=(weyl_dble*)(pl+VOLUME);
         cp_wd2wd(ifc^0x1,(*bb).vol,pk,pw);
         mulg5_sub_assign_wd2sd[ifc]((*bb).imb,(*bb).vol,pw,pl);
      }

      bb+=1;
   }
}

