
/**********************************************************************************************************************************/
/***************************************************  Useful routines for GBEM  ***************************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


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


double gammai(double p1, double p2, double qet, double cr1, double cr2, double zn, double zd)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/***************************************************  Determine gamma_i  **********************************************************/
/**********************************************************************************************************************************/

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


   extern double pix2; /* pix2 = 2*pi, pi = 3.1415926535897932 */



	     if ( (p1<0.0) && (p2>0.0) ) /* 1-----x----------2 <==> if Projection of collocation point falls in side k */
	     {
		         if ( qet<0.0 )
			 {
	                      if ( (cr1>0.0) && (cr2<0.0) && (zn>0.0) && (zd<0.0) )
		                return atan2(zn,zd) - pix2;
		              else if ( (cr1<0.0) && (cr2>0.0) && (zn>0.0) && (zd<0.0) )
		                return atan2(zn,zd) - pix2;
		              else if ( (cr1<0.0) && (cr2<0.0) && (zn>0.0) )
		                return atan2(zn,zd) - pix2;
		              else
		                return atan2(zn,zd);
		         }
			 else if ( qet>0.0 )
			 {
	                      if ( (cr1>0.0) && (cr2<0.0) && (zn<0.0) && (zd<0.0) )
		                 return atan2(zn,zd) + pix2;
		              else if ( (cr1<0.0) && (cr2>0.0) && (zn<0.0) && (zd<0.0) )
		                 return atan2(zn,zd) + pix2;
		              else if ( (cr1<0.0) && (cr2<0.0) && (zn<0.0) )
		                 return atan2(zn,zd) + pix2;
		              else
		                return atan2(zn,zd);
		         }
			 else
		            return 0.0;    /* gammai = atan2(zn,zd) = 0.0 */
	     }
	     else /* x 1----------------2 or 1----------------2 x <==> if Projection of collocation point does not fall in side k */
	       return atan2(zn,zd);
}


double pt_tria_tst(double w, double u, double v, const double *theta, int *theta_id)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/*************************************************  Point in Triangle Test  *******************************************************/
/***********************  Given a point with barycentric coordinates (u,v) on a plane containing a triangle  **********************/
/************  Determine whether the point (u,v) is outside, or inside, or on an edge, or at a vertex of the triangle  ************/
/**************************  Set the flag theta_id characterizing the position of the query point (u,v)  **************************/
/**********************************************************************************************************************************/

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


/* Triangle with vertices {y1,y2,y3}

*           y3
*	    *
*	   / \
*	  /   \
*	 /     \
*       /       \
*      /	 \
*     /		  \
*    *-------------*
*  y1               y2

* Oriented edge L1 = [y1,y2] <== Edge1
* Oriented edge L2 = [y2,y3] <== Edge2
* Oriented edge L3 = [y3,y1] <== Edge3
*/


/* Note: (u,v) == Barycentric coordinates of the query point on the plane containing the triangle
 * Note: w = 1-(u+v)
 * Note: The length of vector theta is 3, i.e. theta[3] */
   extern double pi,pix2; /* pi = 3.1415926535897932, pix2 = 2*pi */



     *theta_id = -1;

     if ( u<0.0 || v<0.0 || w<0.0 )
     {
	 *theta_id = 7; return 0.0; /* Query point is Outside the triangle */
     }
     else if ( u==0.0 )
     {
           if ( v==0.0 )
	   {
              *theta_id = 4; return theta[0]; /* Query point is at Vertex1 */
	   }
	   else if ( v==1.0 )
	   {
              *theta_id = 6; return theta[2]; /* Query point is at Vertex3 */
	   }
	   else if ( v<0.0 || v>1.0)
	   {
	      *theta_id = 7; return 0.0; /* Query point is Outside the triangle */
	   }
	   else /* v>0.0 && v<1.0 */
	   {
	      *theta_id = 3; return  pi; /* Query point is on Edge3 of the triangle */
	   }   
     }
     else if ( v==0.0 )
     {
	   if ( u==1.0 )
	   {
              *theta_id = 5; return theta[1]; /* Query point is at Vertex2 */
	   }
	   else if ( u<0.0 || u>1.0 )
	   {
	      *theta_id = 7; return 0.0; /* Query point is Outside the triangle */
	   }
	   else /* u>0.0 && u<1.0 */
	   {
	      *theta_id = 1; return pi; /* Query point is on Edge1 of the triangle */
	   }
     }
     else if ( w==0.0 )
     {
	   if ( u<0.0 || v<0.0 )
	   {
	      *theta_id = 7; return 0.0; /* Query point is Outside the triangle */
	   }
	   else /* u>0.0 && v>0.0 */
	   {
	      *theta_id = 2; return pi; /* Query point is on Edge2 of the triangle */
	   }
     }
     else /* u>0.0 && v>0.0 && w>0.0 */
     {
	*theta_id = 0; return pix2; /* Query point is Inside the triangle */
     }

}


inline double asym_chi(double p1, double p2, double d)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/************************************  return ASYMTOTIC VALUE of chi when q^2 + et^2 = d << 1  ************************************/
/**********************************************************************************************************************************/

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


 double z1,z2;
 
 

     if ( fabs(p1)>0.0 && fabs(p2)>0.0 )
     {
           z1 = (d/p1)/p1; z2 = (d/p2)/p2;
           return log( fabs(p2/p1)*( (1.0-0.25*z1*(1.0-0.5*z1))/(1.0-0.25*z2*(1.0-0.5*z2)) ) );
     }
     else return 0.0;

}


void ortho_comp_basis(const double *t1, const double *t2, double *e1, double *e2, double *e3)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/************************************  Given two linearly independent vectors t1 and t2 in 3D  ************************************/
/** Compute the orthonormal triad {e1,e2,e3} such that e1=t1/norm(t1), norm(e2) = 1 and e2 is orthogonal to e1, and e3 = e1 x e2 **/
/**********************************************************************************************************************************/

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


/* Note: The length of vectors t1, t2, e1, e2, e3 is 3 */
   int i;
   double al,snrm_te,nrm_te,nrm_tx;



/*********  Orthonormal local basis: e1[3],e2[3],e3[3]  ***************************************************************************/
    snrm_te = t1[0]*t1[0] + t1[1]*t1[1] + t1[2]*t1[2];
    nrm_te = sqrt(snrm_te);

    al = (t1[0]*t2[0] + t1[1]*t2[1] + t1[2]*t2[2])/snrm_te;
    
    for (i=0; i<3; i++) /* Loop over all components */
       e2[i] = t2[i] - al*t1[i];

    nrm_tx = sqrt(e2[0]*e2[0] + e2[1]*e2[1] + e2[2]*e2[2]);

    for (i=0; i<3; i++) /* Loop over all components */
    {   
       e1[i] = t1[i]/nrm_te; e2[i] = e2[i]/nrm_tx;
    }   

/*** Unit Normal vector to the plane spanned by {e1,e2}: e3 = e1 x e2 */
    e3[0] = e1[1]*e2[2] - e2[1]*e1[2];
    e3[1] = e1[2]*e2[0] - e2[2]*e1[0];
    e3[2] = e1[0]*e2[1] - e2[0]*e1[1];

}


double fmin_val_loc(int m, int n, double **x, int dim, int k, int *rw, int *cl)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/*********************************  Return the first minimum value and its location in an array x *********************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


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


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


void int_type(int elP, int elQ, char *flag)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/*********************************************************  GALERKIN BEM  *********************************************************/
/*******************************************************  Integration type  *******************************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


/* Typical triangular element with vertices {y1,y2,y3}

*           y3
*	    *
*	   / \
*	  /   \
*	 /     \
*       /       \
*      /	 \
*     /		  \
*    *-------------*
*  y1               y2

* y1 = Node1, y2 = Node2, y3 = Node3
* Edge1 = [y1,y2] = Side1
* Edge2 = [y2,y3] = Side2
* Edge3 = [y3,y1] = Side3
*/


/******************************************************/
/* Parameter list:                                    */
/******************************************************/
/*
 * elP  == (input) Global index of the boundary element P
 * elQ  == (input) Global index of the boundary element Q
 * *flag == (output): *flag = 'C' <==> Coincident integration
 *                    *flag = 'E' <==> Edge-adjacent integration
 *                    *flag = 'V' <==> Vertex-adjacent integration
 *                    *flag = 'N' <==> Non-Singular (or Nearly-singular) integration 
 */

/* Global variables */
   extern const int mx_nd_elt; /* mx_nd_elt == Maximum number of nodes per element */
   extern int **belt;          /* belt[i][0:2] == Global indices of boundary nodes on the i-th element */
   extern double **bnd;        /* bnd[i][0:2] = Cartesian coordinates of the i-th boundary node */
   extern double DistPQ;       /* DistPQ == Distance between element P and element Q defined as                      */
			       /*           Minimum distance between vertices of element P and vertices of element Q */
   extern int PQmLocId[2];     /* PQmLocId[0] == Local index of the vertex of element P at which DistPQ is attained          */
			       /* PQmLocId[1] == Local index of the vertex of element Q at which DistPQ is attained          */
			       /* (PQmLocId[0],PQmLocId[1]) is also used to give vertex-adjacent pair and edge-adjacent pair */
   extern double **dwrk;       /* dwrk[k][j] == Square Distance of node k of element P and node j of element Q */

/* Local variables */
   int nc; /* nc == Number of common (coincident) vertices between two boundary elements */
	   /* nc=0 ==> non-singular elements                                             */
	   /* nc=1 ==> vertex-adjacent elements                                          */
	   /* nc=2 ==> edge-adjacent elements                                            */
   int i,j,k;
   int vtxP[3],vtxQ[3],sidek,sidel;
   const double tol = 1.0E-16;
   double xp[3],xpq[3]; //dwrk[mx_nd_elt][mx_nd_elt]



    if( elQ == elP ) /* If element P and element Q coincide ==> Coincident case */
    {  
       *flag = 'C'; /* Coincident integration */
       return;
    }
    else
    {  
         nc = 0; /* Non-Singular (Separated) elements */
/*** Check for common vertices *********************************************************************/
             for (k=0; k<mx_nd_elt; k++) /* Loop over all vertices of element P */
	     {
                   for (i=0; i<3; i++) /* Loop over all components */
	              xp[i] = bnd[belt[elP][k]][i]; /* Get the Cartesian coordinates of the k-th vertex of element P */
	      
	           for (j=0; j<mx_nd_elt; j++) /* Loop over all vertices of the Q element */
	           {
	              for (i=0; i<3; i++) /* Loop over all components */
		        xpq[i] = xp[i] - bnd[belt[elQ][j]][i]; 
			/* bnd[belt[elQ][j]][i] = Cartesian coordinates of the j-th vertex of element Q */

                      dwrk[k][j] = xpq[0]*xpq[0] + xpq[1]*xpq[1] + xpq[2]*xpq[2];

                      if ( dwrk[k][j] < tol )
		      {
	                 vtxP[nc] = k; vtxQ[nc] = j; /* Node k of element P coincides with Node j of element Q */
			 nc++;
		      }
                   } /* Loop over all vertices of the Q element */
	     } /* Loop over all vertices of element P */
/*** End check for common vertices *****************************************************************/
	 
           if (nc == 0) /* if element P and element Q are Separated elements ==> Non-Singular (or Nearly-singular) case */
	   {
	      *flag = 'N'; /* Non-Singular (or Nearly-singular) integration */
    	      DistPQ = sqrt(fmin_val_loc(mx_nd_elt,mx_nd_elt,      /* The Minimum distance between vertices of element P and  */
	                                 dwrk,3,0,&sidek,&sidel)); /* vertices of element Q is attained at vertex PQmLocId[0] */
	      PQmLocId[0] = sidek; PQmLocId[1] = sidel;            /* of element P and vertex PQmLocId[1] of element Q        */
	      return;
	   }
	   else if (nc == 1) /* If element P and element Q share a common vertex ==> Vertex-adjacent case */
	   {
	      *flag = 'V'; /* Vertex-adjacent integration */
    	      PQmLocId[0] = vtxP[0]; PQmLocId[1] = vtxQ[0]; /* Vertex vtxP[nc-1] of element P coincides with */
	      return;					    /* Vertex vtxQ[nc-1] of element Q                */
	   }   
           else if (nc == 2) /* If element P and element Q share a common edge ==> Edge-adjacent case */
	   {
	      *flag = 'E'; /* Edge-adjacent integration */

	      if (vtxP[0] == 0)
	      {   
	          if (vtxP[1] == 1) sidek = 0;
	          if (vtxP[1] == 2) sidek = 2;
	      }
	      else if(vtxP[0] == 1)
	      {
	          if (vtxP[1] == 0) sidek = 0;
	          if (vtxP[1] == 2) sidek = 1;
	      }
	      else /* vtxP[0] == 2 */
	      {
	          if (vtxP[1] == 0) sidek = 2;
	          if (vtxP[1] == 1) sidek = 1;
	      }

	      if (vtxQ[0] == 0)
	      {
	          if (vtxQ[1] == 1) sidel = 0;
	          if (vtxQ[1] == 2) sidel = 2;
	      }
	      else if(vtxQ[0] == 1)
	      {
	          if (vtxQ[1] == 0) sidel = 0;
	          if (vtxQ[1] == 2) sidel = 1;
	      }
	      else /* vtxQ[0] == 2 */
	      {
	          if (vtxQ[1] == 0) sidel = 2;
	          if (vtxQ[1] == 1) sidel = 1;
	      }
		 
	      PQmLocId[0] = sidek; PQmLocId[1] = sidel; /* sidek of element P coincides with sidel of element Q */
	      return;
           }
	   else if (nc > 2)
	   {printf("int_type: Problem with elements %d  %d: nc == %d\n",elP,elQ,nc); exit(1);}
    }
        
}


void mltpl_nd(void)
{

/* ===purpose=== */

/**********************************************************************************************************************************/
/*********************************************************  GALERKIN BEM  *********************************************************/
/*******************************************  Find Multiple Nodes on the boundary mesh  *******************************************/
/**********************************************************************************************************************************/

/* Developer: Sylvain Nintcheu Fata */
/* Version:   V.1.1                 */
/* Released:  Jun 14, 2012          */


/* Global variables */
   extern int nbnd;     /* nbnd == Total number of boundary nodes */
   extern double **bnd; /* bnd[i][0:2] = Cartesian coordinates of the i-th boundary node */
   extern int nmltplnd; /* nmltplnd == Number of Multiple nodes (or Number of class of Multiple Nodes) on the boundary mesh */
   extern mltndinfo_t *mltplnd; /* mltplnd[k] == Structure containing information about the k-th class of multiple nodes */

/* Local variables */
   int i,k,j,nlk;
   int *mntmp;
   const double tol = 1.0E-16;
   double d12,r12[3];
   ilst_t *ptr;  /* ptr == pointer to an entry of the class of Multiple nodes */
   ilst_t *ptr1; /* ptr1 == temporary pointer to an entry of the class of Multiple nodes */



/*** Find Multiple nodes *****************************************************************************/
/* A Multiple Node is a set of boundary nodes which represent the same geometric position (i.e. have the same Cartesian coordinates)
 *--------------  but are located at the intersection of smooth patches forming the surface mesh
 * A Multiple Node is, for example, a boundary node at a corner of a cube */

    if ( (mntmp = (int *)malloc(nbnd*sizeof(int))) != NULL ) /* If mntmp is successfully allocated */
    {
       for (k=0; k<nbnd; k++)
          mntmp[k] = -1; /* The k-th node is not linked to any boundary node or k is a smooth boundary node */
    }
    else
    {printf("mltpl_nd: Allocate array \"mntmp\" failed\n"); exit(1);}

    nmltplnd = 0; /* nmltplnd == Number of class of Multiple Nodes on the boundary mesh */
    ptr = NULL; /* Empty list of Multiple nodes */

    for (k=0; k<nbnd-1; k++) /* loop over boundary nodes */
    {
       nlk = 0; /* nlk == Number of boundary nodes linked with the k-th node */

       if (mntmp[k] == -1)
       {
          for (i=k+1; i<nbnd; i++) /* loop over the remaining boundary nodes */
	  {

             if (mntmp[i] != -2) /* If boundary node i is not yet linked with a representative */
	     {
		for (j=0; j<3; j++) /* Loop over all components */
 		   r12[j] = bnd[i][j]-bnd[k][j];
		d12 = r12[0]*r12[0] + r12[1]*r12[1] + r12[2]*r12[2];
		if ( d12 <= tol )
		{
 		   nlk++;
		   if ( (ptr1 = (ilst_t *)malloc(sizeof(ilst_t))) == NULL )
		   {printf("mltpl_nd: Allocate a node \"ptr1\" failed\n"); exit(1);}
 		   ptr1->id = i; /* Adding a node to the linked list */
 		   ptr1->next = ptr; ptr = ptr1; /* Pointer assignment */
		   mntmp[i] = -2;
 		} /* d12 <= tol */
             } /* mntmp[i] != -2 */

	  }
       } /* mntmp[k] == -1 */
       
       if (nlk != 0)
       {
         nmltplnd++;
	 mntmp[k] = nlk+1; /* Number of boundary nodes linked with the k-th node and including the k-th node itself */
       }
    }


    if (nmltplnd != 0) /* if the surface mesh has Multiple nodes */
    {
       if ( (mltplnd = (mltndinfo_t *)malloc(nmltplnd*sizeof(mltndinfo_t))) == NULL ) /* If mltplnd is not successfully allocated */
       {printf("mltpl_nd: Allocate array \"mltplnd\" failed\n"); exit(1);}
    
       k = nbnd-1;
       for (k=nbnd-1; ; k--)       /* k is a smooth node if mntmp(k) = -1; k is already linked with a representative if mntmp(k) = -2 */
          if (mntmp[k] > 0) break; /* if k is a multiple node (i.e. mntmp(k) > 0), exit the loop */

      for (i=nmltplnd-1; i>=0; i--) /* Loop over all multiple nodes */
      {
	 mltplnd[i].nn = mntmp[k]; /* Number of boundary nodes linked with the k-th node and including the k-th node itself */
	 if ( (mltplnd[i].id = (int *)malloc(mltplnd[i].nn*sizeof(int))) == NULL ) /* If mltplnd[i].id is not successfully allocated */
	 {printf("mltpl_nd: Allocate array \"mltplnd[%d].id\" failed\n",i); exit(1);}

	  for (j=mltplnd[i].nn-1; j>=1; j--) /* Loop over the boundary nodes linked with the k-th node */
	  {
             ptr1 = ptr;
	     mltplnd[i].id[j] = ptr1->id; /* Retrieve boundary node associated with the k-th node */
	     ptr = ptr1->next;
	     free(ptr1); /* ptr1 is disassociated */
	  }
	  mltplnd[i].id[0] = k; /* k is the representative boundary node for the current class */

	  for (k--; ; k--)            /* k is a smooth node if mntmp(k) = -1; k is already linked with a representative if mntmp(k) = -2 */
	     if (mntmp[k] > 0) break; /* if k is a multiple node (i.e. mntmp(k) > 0), exit the loop */
      }
    } /* nmltplnd != 0 */

    free(mntmp);
/*** End Find Multiple nodes *************************************************************************/

}
