
/**********************************************************************************************************************************/
/*****************************  Useful routines for generating and initializing computational cells  ******************************/
/**********************************************************************************************************************************/


#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "cbem_cell_util.h"

double fmaxval(int m, int n, double **x, int dim, int k);
double fminval(int m, int n, double **x, int dim, int k);
double sqr_dist_pt_box(const double x[3], double box[3][2]);
void add_elt_in_cell(int Cell_id, int elt_id);


void cbem_cell_init(void)
{

/*===purpose=== */

/**********************************************************************************************************************************/
/*************************************  Initialization of the computational parallelepiped  ***************************************/
/**********************  Determine computational cells containing boundary elements, i.e., non-empty cells  ***********************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata, Oak Ridge National Laboratory */
/* Version:   V.1.0                                                */
/* Released:  Apr 21, 2011                                         */

 
   extern int nbnd,nbelt;   /* nbnd == Total number of boundary nodes */
                            /* nbelt == Total number of boundary elements */
   extern double **bnd;     /* bnd[k][0:2] == Cartesian coordinates of the k-th boundary node */
   extern double **repnd;   /* repnd[i][0:2] == Cartesian coordinates of the centroid of the i-th boundary triangle */
   extern int ncx,ncy,ncz;  /* ncx,ncy,ncz == Total number of cells in the computational box in the x,y,z-coordinate */
			    /* directions respectively                                                               */
   extern int ncell,ncwelt; /* ncell == Total number of cells in the homogeneous computational box: ncell = ncx*ncy*ncz */
			    /* ncwelt == Number of computional Cells containing boundary elements <==> number of non-empty cells */
   extern int *cwelt;       /* cwelt[k] == Global index of the k-th non-empty cell */
   extern cellinfo_t *cell; /* cell[k] == Structure containing information about the k-th cell */

   int i1,i2,i3,j,k,l1,l2,l3,cell_count,mxy;
   ilst_t *ptr; /* ptr == temporary pointer to an element of the list referenced to by ptr_list */
   const double tol = 1.0E-14;
   double xmin,xmax,ymin,ymax,zmin,zmax;
   double diam_box; /* diam_box == Diameter of the computational box */
   double cell_length_x,cell_length_y,cell_length_z;
   double cell_side_l[3]; /* cell_side_l[i] == Left-end of the current cell segment in the i-th coordinate direction  */
   double cell_side_r[3]; /* cell_side_r[i] == Right-end of the current cell segment in the i-th coordinate direction */
   double epsbx,epsby,epsbz; /* epsbx,epsby,epsbz = Small pertubations of the computational Box in x-,y-,and z-coordinate directions */



    xmin = fminval(nbnd,3,bnd,2,0); xmax = fmaxval(nbnd,3,bnd,2,0);
    ymin = fminval(nbnd,3,bnd,2,1); ymax = fmaxval(nbnd,3,bnd,2,1);
    zmin = fminval(nbnd,3,bnd,2,2); zmax = fmaxval(nbnd,3,bnd,2,2);

    epsbx = xmax-xmin; epsby = ymax-ymin; epsbz = zmax-zmin;
    diam_box = sqrt(epsbx*epsbx + epsby*epsby + epsbz*epsbz); /* diam_box == Diameter of the computational box */
    diam_box = 1000.0*diam_box; /* diam_box == 1000 times the actual diameter of the computational box */
    epsbx = epsbx/diam_box; epsby = epsby/diam_box; epsbz = epsbz/diam_box;
    
    xmin = xmin - epsbx; xmax = xmax + epsbx;
    ymin = ymin - epsby; ymax = ymax + epsby;
    zmin = zmin - epsbz; zmax = zmax + epsbz;
    
    cell_length_x = (xmax-xmin)/ncx; /* cell_length_x == Length of a single cell in the x-coordinate direction */
    cell_length_y = (ymax-ymin)/ncy; /* cell_length_y == Length of a single cell in the y-coordinate direction */
    cell_length_z = (zmax-zmin)/ncz; /* cell_length_z == Length of a single cell in the z-coordinate direction */

    mxy = ncx*ncy;
    ncell = mxy*ncz; /* ncell == Total number of cells in the homogeneous computational box: ncell = ncx*ncy*ncz */
    
    if ( (cell = (cellinfo_t *)malloc(ncell*sizeof(cellinfo_t))) == NULL ) /* if cell is not successfully allocated */
    {printf("cell_init: Allocate array \"cell\" failed\n"); exit(1);}
    
    for (k=0; k<ncell; k++)
    {
       cell[k].nec = 0; /* cell(k)%nec == Number of boundary elements in the k-th cell */
       cell[k].ptr_list = NULL; /* Empty list */
    }
    
    cell_side_r[0] = xmin; cell_side_r[1] = ymin; cell_side_r[2] = zmin; /* Collapsing cell */
    for (i3=0; i3<ncz; i3++) /* Loop over all cells in the 3-rd coordinate direction (z-direction) */
    {
       cell_side_l[2] = cell_side_r[2];
       cell_side_r[2] = cell_side_l[2] + cell_length_z;
       l3 = mxy*i3;
       for (i2=0; i2<ncy; i2++) /* Loop over all cells in the 2-nd coordinate direction (y-direction) */
       {
          cell_side_l[1] = cell_side_r[1];
	  cell_side_r[1] = cell_side_l[1] + cell_length_y;
	  l2 = ncx*i2;
	  for (i1=0; i1<ncx; i1++) /* Loop over all cells in the 1-st coordinate direction (x-direction) */
	  {
	     cell_side_l[0] = cell_side_r[0];
	     cell_side_r[0] = cell_side_l[0] + cell_length_x;
	     l1 = l3+l2+i1; /* l1 == Linear index of the current computational cell */
	      /* cell[l1]%bbox == Bounding Box of the current cell */
	      cell[l1].bbox[0][0] = cell_side_l[0]; cell[l1].bbox[0][1] = cell_side_r[0]; /* Bounding box in the x-direction */
	      cell[l1].bbox[1][0] = cell_side_l[1]; cell[l1].bbox[1][1] = cell_side_r[1]; /* Bounding box in the y-direction */
	      cell[l1].bbox[2][0] = cell_side_l[2]; cell[l1].bbox[2][1] = cell_side_r[2]; /* Bounding box in the z-direction */

   	      if (i1 == (ncx-1))
	      {
	         cell_side_r[0] = xmin; /* Collapsing cell in the 1-st coordinate direction */
	         if (i2 == (ncy-1)) cell_side_r[1] = ymin; /* Collapsing cell in the 2-nd coordinate direction */
	      }
	  } /* Loop over all cells in the 1-st coordinate direction (x-direction) */
       } /* Loop over all cells in the 2-nd coordinate direction (y-direction) */
    } /* Loop over all cells in the 3-rd coordinate direction (z-direction) */

    for (k=0; k<nbelt; k++) /* Loop over all boundary elements: k == Global index of the k-th boundary element */
    {
       for (l1=0; l1<ncell; l1++) /* Loop over all cells in the computational parallelepiped */
       { /* repnd[k][0:2] = Baricenter (geometric center) of the current boundary element */
	  if (sqr_dist_pt_box(repnd[k],cell[l1].bbox) < tol) /* If repnd[k][0:2] falls in the current cell, then   */
	  {                                                  /* sqr_dist_pt_box(repnd[k],cell[l1].bbox) = 0.0      */
	  						     /* Tolerance "tol" is used to resolve the situation   */
	     add_elt_in_cell(l1,k);			     /* when repnd(:,k) falls on a corner, edge or side of */
	     break;					     /* the current cell. This call add_elt_in_cell(l1,k)  */
	     						     /* adds boundary element k into a cell with index l1  */
	  }
       
       } /* Loop over all cells in the computational parallelepiped */
    } /* Loop over all boundary elements: k == Global index of the k-th boundary element */
    
/* Initialization in the cell structure of the number boundary elements in non-empty cells */
    cell_count = 0; /* cell_count == Number of cell with boundary elements, i.e., non-empty cells */
    for (k=0; k<ncell; k++) /* Loop over all cells in the computational parallelepiped */
    {
       if (cell[k].nec != 0) /* if the k-th cell is not empty */
       {
          cell_count++;
          if ( (cell[k].elt_id = (int *)malloc(cell[k].nec*sizeof(int))) == NULL ) /* If cell[k].elt_id is not successfully allocated */
	  {printf("cell_init: Allocate array \"cell[%d].elt_id\" failed\n",k); exit(1);}
	  
	   for (j=cell[k].nec-1; j>=0; j--)
	   {
              ptr = cell[k].ptr_list;
               cell[k].elt_id[j] = ptr->id;
               cell[k].ptr_list = ptr->next;
              free(ptr);
           }
       } /* if the k-th cell is not empty */
    } /* Loop over all cells in the computational parallelepiped */
/* End Initialization in the cell structure of the number boundary elements in non-empty cells */

    ncwelt = cell_count; /* ncwelt == Number of cell with boundary elements, i.e., non-empty cells */

    if ( (cwelt = (int *)malloc(ncwelt*sizeof(int))) == NULL ) /* If cwelt is not successfully allocated */
    {printf("cell_init: Allocate array \"cwelt\" failed\n"); exit(1);}

    for (cell_count=0, k=0; k<ncell; k++)
       if (cell[k].nec != 0) /* if the k-th cell is not empty */
         cwelt[cell_count++] = k;

}


void cbem_cell_neighbor(void)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/************************************************  Find "Near-neighboring" cells  *************************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata, Oak Ridge National Laboratory */
/* Version:   V.1.0                                                */
/* Released:  Apr 21, 2011                                         */

 
   extern int ncwelt; 	    /* ncwelt == Number of computional Cells containing boundary elements <==> number of non-empty cells */
   extern int *cwelt;       /* cwelt[k] == Global index of the k-th non-empty cell */
   extern double nbfac;     /* nbfac == Factor characterizing the near-neighboring region of a giving cell */
   extern cellinfo_t *cell; /* cell[k] == Structure containing information about the k-th cell */

   int i,j,ii;
   double cnt_cl[3],z[3],dist,z_c[3];
   double hlf_cl_len[3]; /* hlf_cl_len[i] == Half-Length of a cell in the i-th axis direction */
   double r_nbor; /* r_nbor == nbfac*(D/2), D = diameter of a cell */

   typedef struct { ilst_t *p; } pt_ilst_t;
   pt_ilst_t *ptr_list; /* ptr_list[k].p == pointer to the list of near-neighboring cells to the k-th cell */
   ilst_t *ptr1, *ptr2; /* ptr1, ptr2 == temporary pointers to an element of the list referenced to by ptr_list */



    if ( (ptr_list = (pt_ilst_t *)malloc(ncwelt*sizeof(pt_ilst_t))) == NULL )
    {printf("cell_neighbor: Allocate array \"ptr_list\" failed\n"); exit(1);}

    for (i=0; i<ncwelt; i++) /* Loop over all non-empty cells */
    {
       ptr_list[i].p = NULL; /* Empty list for every non-empty cell */
       cell[cwelt[i]].nnnc = 1; /* cell[cwelt[i]].nnnc == Number of near-neighboring cells to the i-th cell */
                                /* A cell is a near-neighbor to itself                                      */
    }

    /* hlf_cl_len[0] == Half-Length of the 1st non-empty cell in the x-coordinate (or 1st-axis) direction */
    hlf_cl_len[0] = 0.5*(cell[cwelt[0]].bbox[0][1]-cell[cwelt[0]].bbox[0][0]); 
    
    /* hlf_cl_len[1] == Half-Length of the 1st non-empty cell in the y-coordinate (or 2nd-axis) direction */
    hlf_cl_len[1] = 0.5*(cell[cwelt[0]].bbox[1][1]-cell[cwelt[0]].bbox[1][0]);
     
    /* hlf_cl_len[2] == Half-Length of the 1st non-empty cell in the z-coordinate (or 3rd-axis) direction */
    hlf_cl_len[2] = 0.5*(cell[cwelt[0]].bbox[2][1]-cell[cwelt[0]].bbox[2][0]);

    r_nbor = nbfac*sqrt(hlf_cl_len[0]*hlf_cl_len[0] + hlf_cl_len[1]*hlf_cl_len[1] + hlf_cl_len[2]*hlf_cl_len[2]);

    for (i=0; i<ncwelt-1; i++) /* Loop over all non-empty cells */
    {
       for (ii=0; ii<3; ii++) /* Loop over the coordinate directions */
          cnt_cl[ii] = cell[cwelt[i]].bbox[ii][0] + hlf_cl_len[ii]; /* cnt_cl == Center of the i-th cell */

         for (j=i+1; j<ncwelt; j++) /* Loop over the remaining non-empty cells */
	 {
	       for (ii=0; ii<3; ii++) /* Loop over the coordinate directions */
	       {
	          z[ii] = cell[cwelt[j]].bbox[ii][0] + hlf_cl_len[ii]; /* z == Center of the j-th cell */
	          z_c[ii] = z[ii]-cnt_cl[ii];
	       }
	       dist = sqrt(z_c[0]*z_c[0] + z_c[1]*z_c[1] + z_c[2]*z_c[2]); /* dist == Distance between the centers */

/* Near-neighboring region of the i-th cell is characterized by a Ball centered at cnt_cl with radius r_nbor */
	       if ( dist < r_nbor) /* If the Center of the j-th cell is in the Near-neighboring region of the i-th cell */
	       {
	          cell[cwelt[i]].nnnc++; cell[cwelt[j]].nnnc++;
		  
		  if ( (ptr1 = (ilst_t *)malloc(sizeof(ilst_t))) == NULL )
		  {printf("cell_neighbor: Allocate a node \"ptr1\" for the list of near-neighbors failed\n"); exit(1);}

		  if ( (ptr2 = (ilst_t *)malloc(sizeof(ilst_t))) == NULL )
		  {printf("cell_neighbor: Allocate a node \"ptr2\" for the list of near-neighbors failed\n"); exit(1);}

                  ptr1->id = cwelt[j]; /* Adding an element to a linked list of the i-th cell */
                  ptr1->next = ptr_list[i].p; ptr_list[i].p = ptr1;
                  ptr2->id = cwelt[i]; /* Adding an element to a linked list of the j-th cell */
                  ptr2->next = ptr_list[j].p; ptr_list[j].p = ptr2;
	       }
         }
    }

/* Read (to a usual dynamic array) and delete a linked list associated to a cell */
    for(i=0; i<ncwelt; i++) /* Loop over all non-empty cells */
    {
       /* If cell[cwelt[i]].nbor is not successfully allocated */
       if ( (cell[cwelt[i]].nbor = (int *)malloc(cell[cwelt[i]].nnnc*sizeof(int))) == NULL )
       {printf("cell_neighbor: Allocate array \"cell[cwelt[%d]].nbor\" failed\n",i); exit(1);}
       /* cell[i].nbor[k] == Global index of the k-th near-neighbor to the i-th cell */
       
        for (j=cell[cwelt[i]].nnnc-1; j>=1; j--)
	{
           ptr1 = ptr_list[i].p;
           cell[cwelt[i]].nbor[j] = ptr1->id;
           ptr_list[i].p = ptr1->next;
           free(ptr1);
        }
       cell[cwelt[i]].nbor[0] = cwelt[i]; /* Adding the i-th cell to the array of its neighbor, since a cell is near-neighbor to itself */
    }
    free(ptr_list);

}


double sqr_dist_pt_box(const double x[3], double box[3][2])
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/*****************************  Return the SQUARE DISTANCE between a POINT and an AXIS-PARALLEL BOX  ******************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata, Oak Ridge National Laboratory */
/* Version:   V.1.0                                                */
/* Released:  Apr 21, 2011                                         */


/* Note: x == Arbitrary Point in 3D space */
/* Note: box == Axis-parallel box */
/* Note: [[box[i][0],box[i][1]] = Segment forming the box in the i-th axis direction */
   double dx,dy,dz; /* dx = Distance in the x-coordinate direction
                       dy = Distance in the y-coordinate direction
		       dz = Distance in the z-coordinate direction */



    dx = fmax(fmax(box[0][0] - x[0], x[0] - box[0][1]), 0.0);
    dy = fmax(fmax(box[1][0] - x[1], x[1] - box[1][1]), 0.0);
    dz = fmax(fmax(box[2][0] - x[2], x[2] - box[2][1]), 0.0);
    return dx*dx + dy*dy + dz*dz;
}



void add_elt_in_cell(int Cell_id, int elt_id)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/***************  Add a single element with global index elt_id in a computational cell with global index Cell_id  ****************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata, Oak Ridge National Laboratory */
/* Version:   V.1.0                                                */
/* Released:  Apr 21, 2011                                         */


   extern cellinfo_t *cell; /* cell[k] == Structure containing information about the k-th cell */

   ilst_t *ptr; /* ptr == temporary pointer to an element of the list referenced to by cell[Cell_id].ptr_list */



    cell[Cell_id].nec++;
    if ( (ptr = (ilst_t *)malloc(sizeof(ilst_t))) == NULL )
    {printf("add_elt_in_cell: Allocate a node for the cell list failed\n"); exit(1);}
    
    ptr->id = elt_id;    /* Adding a boundary element to a linked list */
    ptr->next = cell[Cell_id].ptr_list; cell[Cell_id].ptr_list = ptr;
}


double fmaxval(int m, int n, double **x, int dim, int k)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/********************************************  Return the maximum value in an array x *********************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata, Oak Ridge National Laboratory */
/* Version:   V.1.0                                                */
/* Released:  Apr 21, 2011                                         */


/* Note: m == Number of rows in x,
         n == Number of columns in x, i.e. x[m][n] */
/* Note: dim = 1 ==> return the maximum value in the k-th row of array x, k=0,...,m    */
/* Note: dim = 2 ==> return the maximum value in the k-th column of array x, k=0,...,n */
   int i,j;
   double mxval;
   


     switch(dim)
     {
        case 1: { /* return the maximum value in the k-th row of array x */
	          for (mxval=x[k][0], i=1; i<n; i++) mxval = fmax(mxval,x[k][i]);
	        }
	        break;
	       
	case 2: { /* return the maximum value in the k-th column of array x */
	           for (mxval=x[0][k], i=1; i<m; i++) mxval = fmax(mxval,x[i][k]);
	        }
		break;
	
	default:{ /* return the maximum value in the entire array x */
		   for (mxval=x[0][0], i=0; i<m; i++)
		      for (j=0; j<n; j++) 
		         mxval = fmax(mxval,x[i][j]);
	        }
     }
     return mxval;
}


double fminval(int m, int n, double **x, int dim, int k)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/********************************************  Return the minimum value in an array x *********************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata, Oak Ridge National Laboratory */
/* Version:   V.1.0                                                */
/* Released:  Apr 21, 2011                                         */


/* Note: m == Number of rows in x,
         n == Number of columns in x, i.e. x[m][n] */
/* Note: dim = 1 ==> return the minimum value in the k-th row of array x, k=0,...,m    */
/* Note: dim = 2 ==> return the minimum value in the k-th column of array x, k=0,...,n */
   int i,j;
   double mnval;
   


     switch(dim)
     {
        case 1: { /* return the minimum value in the k-th row of array x */
	          for (mnval=x[k][0], i=1; i<n; i++) mnval = fmin(mnval,x[k][i]);
	        }
	        break;
	       
	case 2: { /* return the minimum value in the k-th column of array x */
	           for (mnval=x[0][k], i=1; i<m; i++) mnval = fmin(mnval,x[i][k]);
	        }
		break;
	
	default:{ /* return the minimum value in the entire array x */
		   for (mnval=x[0][0], i=0; i<m; i++)
		      for (j=0; j<n; j++) 
		         mnval = fmin(mnval,x[i][j]);
	        }
     }
     return mnval;
}
