
/*******************************************************************************
*
* File blk_grid.c
*
* Copyright (C) 2005, 2007 Martin Luescher, 2011 Andrea Nobile
*
* This software is distributed under the terms of the GNU General Public
* License (GPL)
*
* Block grid allocation
*
* The externally accessible functions are
*
*   void alloc_bgr(blk_grid_t grid,int *bs)
*     Allocates the specified block grid using blocks of size bs[4]. The
*     block array and the block fields are put in the static memory of this
*     module and are properly initialized
*
*   void free_bgr(blk_grid_t grid)
*     Frees the arrays in the specified block grid if they are currently
*     allocated and resets all grid parameters to zero
*
*   block_t *blk_list(blk_grid_t grid,int *nb,int *isw)
*     Returns the pointer to the block array of the specified grid or NULL
*     if the grid is not allocated. In the first case the number of blocks
*     on the local lattice is assigned to nb and isw is set to 0 or 1
*     depending on whether the first block is black or white (by definition
*     it is black on the first process)
*
* Notes:
*
* All of these programs operate globally. In particular, the parameters of
* alloc_bgr must be the same on all processes
*
* The block sizes bs[4] and the lattice sizes must be such that the lattice
* can be covered by the blocks without overlaps. Moreover it is required
* that the blocks can be chessboard coloured and that there is an even number
* of blocks on the local lattices
*
* On all processes the blocks with the same number have the same positions
* relative to the local lattice. They are ordered such that the first half of
* the blocks have the same colour. The blocks are allocated with boundaries
* and the protection flags are set on all of them
*
* An already allocated block grid can be reallocated with a different block
* size simply by calling alloc_bgr() again. The old arrays and structures
* are freed in this case and the new fields are initialized to their default
* values
*
*******************************************************************************/

#define BLK_GRID_C

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

typedef struct
{
   int nb,isw;
   block_t *b;
} bgrid_t;

static bgrid_t bgr[BLK_GRIDS]={{0,0,NULL}};


static void clone_blk(block_t *bn,block_t *b,int *bo,int shu,int shud,
                      int shs,int shsd,int shsb,int shsbd)
{
   int n,ifc,iu,iud,ns,nsd,nsb,nsbd;
   bndry_t *bb,*bbn;

   iu=((shu==0)&&(b->u!=NULL));
   iud=((shud==0)&&(b->ud!=NULL));
   ns=(shs==0)*b->ns;
   nsd=(shsd==0)*b->nsd;
   alloc_blk(bn,bo,b->bs,iu,iud,ns,nsd);
   bn->uidx = b->uidx;
   bb=b->bb;

   if (bb!=NULL)
   {
      nsb=(shsb==0)*(*bb).ns;
      nsbd=(shsbd==0)*(*bb).nsd;
      alloc_bnd(bn,nsb,nsbd);
   }

   if (shu==1)
   {
      bn->u=b->u;
      bn->sw=b->sw;
   }
   if (shud==1)
   {
      bn->ud=b->ud;
      bn->swd=b->swd;
      bn->swb=b->swb;
   }
   if (shs==1)
   {
      bn->ns=b->ns;
      bn->s=b->s;
   }
   if (shsd==1)
   {
      bn->nsd=b->nsd;
      bn->sd=b->sd;
   }

   if (bb!=NULL)
   {
      bbn=bn->bb;

      for (ifc=0;ifc<8;ifc++)
      {
         if (shu==1)
            (*bbn).u=(*bb).u;
         if (shud==1)
            (*bbn).ud=(*bb).ud;
         if (shsb==1)
         {
            (*bbn).ns=(*bb).ns;
            (*bbn).s=(*bb).s;
         }
         if (shsbd==1)
         {
            (*bbn).nsd=(*bb).nsd;
            (*bbn).sd=(*bb).sd;
         }

         bb+=1;
         bbn+=1;
      }
   }

   n=0;
   n|=0x1;
   n|=0x20;

   if (shu==1)
   {
      n|=0x2;
      n|=0x40;
   }
   if (shud==1)
   {
      n|=0x4;
      n|=0x80;
   }
   if (shs==1)
      n|=0x8;
   if (shsd==1)
      n|=0x10;
   if (shsb==1)
      n|=0x100;
   if (shsbd==1)
      n|=0x200;

   bn->shf=n;
}


static block_t *blks(int *bs,int iu,int iud,
                     int ns,int nsd,int nsb,int nsbd,int *nb,int *isw)
{
   int bo[4];
   int n0,n1,n2,n3,m0,m1,m2,m3;
   int shu,shud,shs,shsd,shsb,shsbd;
   block_t *b,*rbe,*rbo;

   n0=L0/bs[0];
   n1=L1/bs[1];
   n2=L2/bs[2];
   n3=L3/bs[3];

   *nb=n0*n1*n2*n3;
   *isw=(cpr[0]*n0+cpr[1]*n1+cpr[2]*n2+cpr[3]*n3)%2;

   error_root((((NPROC0*n0)%2)!=0)||(((NPROC1*n1)%2)!=0)||
         (((NPROC2*n2)%2)!=0)||(((NPROC3*n3)%2)!=0)||(((*nb)%2)!=0),1,
   "blks [blk_grid.c]","Block grid does not form a checker board");

   shu=(iu<0);
   shud=(iud<0);
   shs=(ns<0);
   shsd=(nsd<0);
   shsb=(nsb<0);
   shsbd=(nsbd<0);

   b=amalloc((*nb)*sizeof(*b),3);
   error(b==NULL,1,"blks [blk_grid.c]","Unable to allocate block grid");

   rbe=b;
   rbo=b+(*nb)/2;

   for (m0=0;m0<n0;m0++)
   {
      for (m1=0;m1<n1;m1++)
      {
         for (m2=0;m2<n2;m2++)
         {
            for (m3=0;m3<n3;m3++)
            {
               bo[0]=m0*bs[0];
               bo[1]=m1*bs[1];
               bo[2]=m2*bs[2];
               bo[3]=m3*bs[3];

               if (((m0+m1+m2+m3)%2)==0)
               {
		 if (rbe==b)  //first block;
                  {
                     alloc_blk(rbe,bo,bs,abs(iu),abs(iud),abs(ns),abs(nsd));
                     alloc_bnd(rbe,abs(nsb),abs(nsbd));
		     alloc_uidx(rbe);
                  }
                  else
                     clone_blk(rbe,b,bo,shu,shud,shs,shsd,shsb,shsbd);

                  rbe+=1;
               }
               else
               {
                  clone_blk(rbo,b,bo,shu,shud,shs,shsd,shsb,shsbd);
                  rbo+=1;
               }
            }
         }
      }
   }

   b->shf|=0x21;

   return b;
}


void free_bgr(blk_grid_t grid)
{
   int iprms[1];
   block_t *b0,*b,*bm;

   if (NPROC>1)
   {
      iprms[0]=(int)(grid);

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

      error(iprms[0]!=(int)(grid),1,
            "free_bgr [blk_grid.c]","Parameters are not global");
   }

   error_root((grid<0)||(grid>=BLK_GRIDS),1,
               "free_bgr [blk_grid.c]","Unknown block grid");

   b0=bgr[grid].b;

   if (b0==NULL)
      return;

   if (grid==GCR_BLOCKS)
      free_msap_bufs();

   afree(b0->uidx);
   for (b=b0;b<bm;b++)
   {
      b->shf&=(~0x1);
      b->shf&=(~0x20);
      free_blk(b);
   }

   afree(b0);
   set_blk_flags(grid,FREED_BLOCK_GRID,bgr[grid].nb);

   bgr[grid].nb=0;
   bgr[grid].isw=0;
   bgr[grid].b=NULL;
}


void alloc_bgr(blk_grid_t grid,int *bs)
{
   int iprms[5],*obs;
   int iu=0,iud=0,ns=0,nsd=0,nsb=0,nsbd=0;
 

   if (NPROC>1)
   {
      iprms[0]=(int)(grid);
      iprms[1]=bs[0];
      iprms[2]=bs[1];
      iprms[3]=bs[2];
      iprms[4]=bs[3];

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

      error((iprms[0]!=(int)(grid))||(iprms[1]!=bs[0])||
            (iprms[2]!=bs[1])||(iprms[3]!=bs[2])||(iprms[4]!=bs[3]),1,
            "alloc_bgr [blk_grid.c]","Parameters are not global");
   }

   error_root((bs[0]<=0)||(bs[1]<=0)||(bs[2]<=0)||(bs[3]<=0)||
              (bs[0]>L0)||(bs[1]>L1)||(bs[2]>L2)||(bs[3]>L3),1,
              "alloc_bgr [blk_grid.c]","Improper block size");

   error_root(((L0%bs[0])!=0)||((L1%bs[1])!=0)||
              ((L2%bs[2])!=0)||((L3%bs[3])!=0),1,
              "alloc_bgr [blk_grid.c]",
              "Blocks do not fit in the local lattice");

   error_root((grid<0)||(grid>=BLK_GRIDS),1,
              "alloc_bgr [blk_grid.c]","Unknown block grid");

 
   if (bgr[grid].b!=NULL)
   {
      obs=(*(bgr[grid].b)).bs;

      if ((obs[0]!=bs[0])||(obs[1]!=bs[1])||(obs[2]!=bs[2])||(obs[3]!=bs[3]))
         free_bgr(grid);
  
      else
         return;
   }

   if (grid==GCR_BLOCKS)
   {
      iu=1;
      ns=-3;
   }
 
   bgr[grid].b=blks(bs,iu,iud,ns,nsd,nsb,nsbd,
                    &(bgr[grid].nb),&(bgr[grid].isw));

   set_blk_flags(grid,ALLOCATED_BLOCK_GRID,bgr[grid].nb);   

}


block_t *blk_list(blk_grid_t grid,int *nb,int *isw)
{
   if ((grid>=0)&&(grid<BLK_GRIDS))
   {
      *nb=bgr[grid].nb;
      *isw=bgr[grid].isw;
      return bgr[grid].b;
   }
   else
   {
      *nb=0;
      *isw=0;
      return NULL;
   }
}
