/*----------------------------radic.c---------------------------------
  This file contains routines for calculating Tij and Fij, as defined
  in DW Brenner J. Phys Cond Mat 14 (2002) 783-802.  This is
  accomplished through the use of tricubic interpolation.  This
  software was written by Stephen Jordan and modeled on code from
  Zyvex as well as DW Brenner's original Fortran.
  ---------------------------------------------------------------------*/

#include "radic.h"
#include <math.h>
#include <stdio.h>


/* The first coordinate of in3 is an index into CLMN or TLMN.
   The second coordinate is either 1, 2, or 3 for L (XINT1), M (XINT2), or N
   (CONJUG). The value od in3 is the power to raise XINT1, XINT2, or CONJUG
   to before multiplying by the corresponding element of CLMN or TLMN and
   then adding to evaluate the cubic spline.  CLMN contains the coefficients
   for the polynomial interpolation in RADIC.  Similarly, TLMN contains the
   coefficients for polynomial interpolation in TOR.  In3_initialized keeps
   track of whether the coefficients, etc. been loaded yet.*/

static int IN3[64+1][3+1];
static double CLMN[3+1][10+1][10+1][10+1][64+1];
static double TLMN[10+1][10+1][10+1][64+1];
static int in3_initialized = 0;


static void init_in3()
{
  int IC=0;
  int I3D,ITD;
  int i, I, J, K, L, M, N;
  double val;
  FILE *inter3Dtors;
  for (I=1; I<5; I++) {
    for (J=1; J<5; J++) {
      for (K=1; K<5; K++) {
        IC=IC+1;
        /* It's weird that IN3 and dIN3 are the same as each other. */
        IN3[IC][1]=I-1;
        IN3[IC][2]=J-1;
        IN3[IC][3]=K-1;
      }
    }
  }
  /* Read the tricubic spline coefficients for RADIC (that is, F). */
  for(i = 0; i < 3; ++i) {
    static const char *inter_file[]={"inter3d_iv_new.d","inter3d_ch.d","inter3d_h.d"};
    FILE *inter3Divnew;
    if((inter3Divnew = fopen(inter_file[i],"r"))==NULL) {
      fprintf(stderr,"opening %s failed", inter_file[i]);
      return;
    }
    fscanf(inter3Divnew, "%d\n",&I3D);
    while(1) {
      if(fscanf(inter3Divnew, "%d %d %d\n",&L,&M,&N) != 3) break;
      for(I=1; I<=64; ++I) {
	double val;
	if(!fscanf(inter3Divnew,"%lf\n", &val)) {
	  fprintf(stderr, "parse error in file %s, %d %d %d %d\n",
		  inter_file[i], L, M, N, I);
	  return;
	}
        CLMN[i+1][L][M][N][I] = val;
      }
    }
    fclose(inter3Divnew);
  }   
  for (L=1; L<11; L++) {
    for (M=1; M<11; M++) {
      for (I=1; I<65; I++) {
	CLMN[1][L][M][10][I]=CLMN[1][L][M][9][I];
	CLMN[2][L][M][10][I]=CLMN[2][L][M][9][I];
	for (N=6; N<11; N++)
	CLMN[3][L][M][N][I]=CLMN[3][L][M][5][I];
      }
    }
  }
  // Read tricubic spline coefficients for torsional potential
  if ((inter3Dtors = fopen("inter3dtors.d","r"))==NULL)
    printf("opening inter3dtors failed");
  fscanf(inter3Dtors, "%d\n",&ITD);
  for (J=1; J<109; J++) {
    fscanf(inter3Dtors, "%d %d %d\n",&L,&M,&N);
    for (I=1; I<64; I+=3) {
      double i0, i1, i2;
      fscanf(inter3Dtors, "%lf %lf %lf\n", &i0, &i1, &i2);
      TLMN[L][M][N][I] = i0;
      TLMN[L][M][N][I+1] = i1;
      TLMN[L][M][N][I+2] = i2;
    }
    fscanf(inter3Dtors, "%lf\n",&val);
    TLMN[L][M][N][64] = val;
  }
  fclose(inter3Dtors);
  for (L=1; L<11; L++)
    for (M=1; M<11; M++)
      for (N=4; N<11; N++)
	for (I=1; I<65; I++) {
	  TLMN[L][M][N][I]=TLMN[L][M][3][I];
	}
  in3_initialized = 1;
}


/* Fij(NiT,NjT,Nijconj) */
/* See equation 14 in the paper */
double Fij(int KI, int KJ, double XNT1, double XNT2, double CONJUG) {
  int J,L,M,N,KIKJ;
  double X, RAD;
  const double *clmn_ptr;
  double xnt1_pow[4];
  double xnt2_pow[4];
  double conj_pow[4];
  //certain integer values of the parameters are very common (at least
  //in graphene). Thus, computing time can be saved by simply spitting
  //out the known result for these cases.
  if(KI==1&&KJ==1&&XNT1==2.0&&XNT2==2.0&&CONJUG==6.0)    return  1.98529352964515112823e-02;

  if((KI==2&&KJ==1&&XNT1==0.0&&XNT2==2.0&&CONJUG==5.0)||
     (KI==1&&KJ==2&&XNT1==2.0&&XNT2==0.0&&CONJUG==5.0))  return -9.04778751612156373341e-03;      

  if(KI==1&&KJ==1&&XNT1==2.0&&XNT2==2.0&&CONJUG==9.0)    return -9.16863029942760476843e-10;

  if(KI==1&&KJ==1&&XNT1==2.0&&XNT2==2.0&&CONJUG==3.0)    return  3.97058706111343417966e-02;

  //  printf("Fij: %i\t%i\t%.20e\t%.20e\t%.20e\t", KI, KJ, XNT1, XNT2, CONJUG);
  XNT1++; //SPJ: to make it consistent with the paper
  XNT2++; //SPJ: likewise
  if(!in3_initialized) init_in3();
  L=(int)floor(XNT1);
  M=(int)floor(XNT2);
  N=(int)floor(CONJUG);
  RAD = 0;
  KIKJ = KI + KJ - 1;
  if(L>=4) {
    L =4;
    XNT1 = 4;
  }
  if(M>=4){
    M=4;
    XNT2=4;
  }
  if(M < 1) {
    M = 1;
    XNT2 = 1;
  }
  if(L < 1) {
    L = 1;
    XNT1 = 1;
  }
  if(N>=9){
    N=9;
    CONJUG=9;
  }
  xnt1_pow[0] = 1.0;
  xnt1_pow[1] = XNT1;
  xnt1_pow[2] = XNT1*XNT1;
  xnt1_pow[3] = XNT1*XNT1*XNT1;
  xnt2_pow[0] = 1.0;
  xnt2_pow[1] = XNT2;
  xnt2_pow[2] = XNT2*XNT2;
  xnt2_pow[3] = XNT2*XNT2*XNT2;
  conj_pow[0] = 1.0;
  conj_pow[1] = CONJUG; 
  conj_pow[2] = CONJUG*CONJUG;
  conj_pow[3] = CONJUG*CONJUG*CONJUG;
  clmn_ptr = &CLMN[KIKJ][L][M][N][0];
  for (J=1; J<65; ++J) {
    X=*++clmn_ptr*xnt1_pow[IN3[J][1]]*xnt2_pow[IN3[J][2]]*conj_pow[IN3[J][3]];
    RAD += X;
  }
  //  printf("%.20e\n", RAD);
  return RAD;
}

/*Tij(NiT, NjT, Nijconj))*/
/* Tricubic spline for the torsional rotation.  It's Tij as in equation
   18 of the paper. */
double Tij(double XNT1, double XNT2, double CONJUG) {
  int J,L,M,N;
  double X;
  double ATOR;
  //certain integer values of the parameters are very common (at least
  //in graphene). Thus, computing time can be saved by simply spitting
  //out the known result for these cases.
  if(((XNT1==0.0&&XNT2==2.0)||(XNT1==2.0&&XNT2==0.0))&&CONJUG==5.0) return 0.0;
  if(XNT1==2.0&&XNT2==2.0&&(CONJUG==3.0||CONJUG==6.0||CONJUG==9.0)) return -8.09674999997861410961e-03;
  //  printf("Tij:%.20e\t%.20e\t%.20e\t", XNT1, XNT2, CONJUG);
  XNT1++;  //SPJ: to make it consistent with the paper
  XNT2++;  //SPJ: likewise
  if(!in3_initialized) init_in3();
  ATOR = 0;
  if(XNT1<4 || XNT2<4) {
    const double *tlmn_ptr;
    double xnt1_pow[4];
    double xnt2_pow[4];
    double conj_pow[4];
    xnt1_pow[0] = 1.0;
    xnt1_pow[1] = XNT1;
    xnt1_pow[2] = XNT1*XNT1;
    xnt1_pow[3] = XNT1*XNT1*XNT1;
    xnt2_pow[0] = 1.0;
    xnt2_pow[1] = XNT2;
    xnt2_pow[2] = XNT2*XNT2;
    xnt2_pow[3] = XNT2*XNT2*XNT2;
    conj_pow[0] = 1.0;
    conj_pow[1] = CONJUG;
    conj_pow[2] = CONJUG*CONJUG;
    conj_pow[3] = CONJUG*CONJUG*CONJUG;
    L = (int)floor(XNT1);
    M = (int)floor(XNT2);
    N = (int)floor(CONJUG);
    tlmn_ptr = &TLMN[L][M][N][0];
    for(J=1; J<65; J++) {
      X = *++tlmn_ptr*xnt1_pow[IN3[J][1]]*xnt2_pow[IN3[J][2]]*conj_pow[IN3[J][3]];
      ATOR += X;
    }
  }
  //  printf("%.20e\n", ATOR);
  return ATOR;
}















