add another way of calculating tersoff + small moldyn mods
[physik/posic.git] / potentials / tersoff.c
index 16f771c..531d292 100644 (file)
@@ -85,10 +85,9 @@ int tersoff_mult_1bp(t_moldyn *moldyn,t_atom *ai) {
 int tersoff_mult_2bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
 
        t_tersoff_mult_params *params;
-       t_tersoff_exchange *exchange;
        t_3dvec dist_ij,force;
        double d_ij,d_ij2;
-       double A,B,R,S,S2,lambda,mu;
+       double A,R,S,S2,lambda;
        double f_r,df_r;
        double f_c,df_c;
        int brand;
@@ -96,31 +95,10 @@ int tersoff_mult_2bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
        double arg;
 
        /* use newtons third law */
-       //if(ai<aj) return 0;
+       if(ai<aj) return 0;
 
        params=moldyn->pot_params;
        brand=aj->brand;
-       exchange=&(params->exchange);
-
-       /* clear 3bp and 2bp post run */
-       exchange->run3bp=0;
-       exchange->run2bp_post=0;
-
-       /* reset S > r > R mark */
-       exchange->d_ij_between_rs=0;
-       
-       /*
-        * calc of 2bp contribution of V_ij and dV_ij/ji
-        *
-        * for Vij and dV_ij we need:
-        * - f_c_ij, df_c_ij
-        * - f_r_ij, df_r_ij
-        *
-        * for dV_ji we need:
-        * - f_c_ji = f_c_ij, df_c_ji = df_c_ij
-        * - f_r_ji = f_r_ij; df_r_ji = df_r_ij
-        *
-        */
 
        /* determine cutoff square */
        if(brand==ai->brand)
@@ -128,71 +106,41 @@ int tersoff_mult_2bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
        else
                S2=params->S2mixed;
 
-       /* dist_ij, d_ij */
+       /* dist_ij, d_ij2 */
        v3_sub(&dist_ij,&(aj->r),&(ai->r));
        if(bc) check_per_bound(moldyn,&dist_ij);
        d_ij2=v3_absolute_square(&dist_ij);
 
        /* if d_ij2 > S2 => no force & potential energy contribution */
-       if(d_ij2>S2)
+       if(d_ij2>S2) {
                return 0;
+       }
 
        /* now we will need the distance */
-       //d_ij=v3_norm(&dist_ij);
        d_ij=sqrt(d_ij2);
 
-       /* save for use in 3bp */
-       exchange->d_ij=d_ij;
-       exchange->d_ij2=d_ij2;
-       exchange->dist_ij=dist_ij;
-
        /* more constants */
-       exchange->beta_j=&(params->beta[brand]);
-       exchange->n_j=&(params->n[brand]);
-       exchange->c_j=&(params->c[brand]);
-       exchange->d_j=&(params->d[brand]);
-       exchange->h_j=&(params->h[brand]);
        if(brand==ai->brand) {
                S=params->S[brand];
                R=params->R[brand];
                A=params->A[brand];
-               B=params->B[brand];
                lambda=params->lambda[brand];
-               mu=params->mu[brand];
-               exchange->chi=1.0;
-               exchange->betajnj=exchange->betaini;
-               exchange->cj2=exchange->ci2;
-               exchange->dj2=exchange->di2;
-               exchange->cj2dj2=exchange->ci2di2;
        }
        else {
                S=params->Smixed;
                R=params->Rmixed;
                A=params->Amixed;
-               B=params->Bmixed;
                lambda=params->lambda_m;
-               mu=params->mu_m;
-               params->exchange.chi=params->chi;
-               exchange->betajnj=pow(*(exchange->beta_j),*(exchange->n_j));
-               exchange->cj2=params->c[brand]*params->c[brand];
-               exchange->dj2=params->d[brand]*params->d[brand];
-               exchange->cj2dj2=exchange->cj2/exchange->dj2;
        }
 
-       /* f_r_ij = f_r_ji, df_r_ij = df_r_ji */
+       /* f_r_ij, df_r_ij */
        f_r=A*exp(-lambda*d_ij);
        df_r=lambda*f_r/d_ij;
 
-       /* f_a, df_a calc (again, same for ij and ji) | save for later use! */
-       exchange->f_a=-B*exp(-mu*d_ij);
-       exchange->df_a=mu*exchange->f_a/d_ij;
-
-       /* f_c, df_c calc (again, same for ij and ji) */
+       /* f_c, df_c */
        if(d_ij<R) {
-               /* f_c = 1, df_c = 0 */
                f_c=1.0;
                df_c=0.0;
-               /* two body contribution (ij, ji) */
                v3_scale(&force,&dist_ij,-df_r);
        }
        else {
@@ -200,18 +148,27 @@ int tersoff_mult_2bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
                arg=M_PI*(d_ij-R)/s_r;
                f_c=0.5+0.5*cos(arg);
                df_c=0.5*sin(arg)*(M_PI/(s_r*d_ij));
-               /* two body contribution (ij, ji) */
                v3_scale(&force,&dist_ij,-df_c*f_r-df_r*f_c);
-               /* tell 3bp that S > r > R */
-               exchange->d_ij_between_rs=1;
        }
 
-       /* add forces of 2bp (ij, ji) contribution
-        * dVij = dVji and we sum up both: no 1/2) */
+       /* add forces */
        v3_add(&(ai->f),&(ai->f),&force);
+       v3_sub(&(aj->f),&(aj->f),&force);
+
+#ifdef DEBUG
+       if((ai==&(moldyn->atom[0]))|(aj==&(moldyn->atom[0]))) {
+               printf("force 2bp: [%d %d]\n",ai->tag,aj->tag);
+               printf("adding %f %f %f\n",force.x,force.y,force.z);
+               if(ai==&(moldyn->atom[0]))
+                       printf("total i: %f %f %f\n",ai->f.x,ai->f.y,ai->f.z);
+               if(aj==&(moldyn->atom[0]))
+                       printf("total j: %f %f %f\n",aj->f.x,aj->f.y,aj->f.z);
+       }
+#endif
 
        /* virial */
-       virial_calc(ai,&force,&dist_ij);
+       //virial_calc(ai,&force,&dist_ij);
+       //virial_calc(aj,&force,&dist_ij);
        //ai->virial.xx-=force.x*dist_ij.x;
        //ai->virial.yy-=force.y*dist_ij.y;
        //ai->virial.zz-=force.z*dist_ij.z;
@@ -219,270 +176,86 @@ int tersoff_mult_2bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
        //ai->virial.xz-=force.x*dist_ij.z;
        //ai->virial.yz-=force.y*dist_ij.z;
 
-#ifdef DEBUG
-if(ai==&(moldyn->atom[0])) {
-       printf("dVij, dVji (2bp) contrib:\n");
-       printf("%f | %f\n",force.x,ai->f.x);
-       printf("%f | %f\n",force.y,ai->f.y);
-       printf("%f | %f\n",force.z,ai->f.z);
-}
-#endif
-#ifdef VDEBUG
-if(ai==&(moldyn->atom[0])) {
-       printf("dVij, dVji (2bp) contrib:\n");
-       printf("%f | %f\n",force.x*dist_ij.x,ai->virial.xx);
-       printf("%f | %f\n",force.y*dist_ij.y,ai->virial.yy);
-       printf("%f | %f\n",force.z*dist_ij.z,ai->virial.zz);
-}
-#endif
-
-       /* energy 2bp contribution (ij, ji) is 0.5 f_r f_c ... */
-       moldyn->energy+=(0.5*f_r*f_c);
-
-       /* save for use in 3bp */
-       exchange->f_c=f_c;
-       exchange->df_c=df_c;
-
-       /* enable the run of 3bp function and 2bp post processing */
-       exchange->run3bp=1;
-       exchange->run2bp_post=1;
-
-       /* reset 3bp sums */
-       exchange->zeta_ij=0.0;
-       exchange->zeta_ji=0.0;
-       v3_zero(&(exchange->dzeta_ij));
-       v3_zero(&(exchange->dzeta_ji));
+       /* energy 2bp contribution */
+       moldyn->energy+=f_r*f_c;
 
        return 0;
 }
 
-/* tersoff 2 body post part */
-
-int tersoff_mult_post_2bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
-
-       /*
-        * here we have to allow for the 3bp sums
-        *
-        * that is:
-        * - zeta_ij, dzeta_ij
-        * - zeta_ji, dzeta_ji
-        *
-        * to compute the 3bp contribution to:
-        * - Vij, dVij
-        * - dVji
-        *
-        */
+/* tersoff 3 body potential function (first ij loop) */
+int tersoff_mult_3bp_j1(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
 
        t_tersoff_mult_params *params;
        t_tersoff_exchange *exchange;
-
-       t_3dvec force,temp;
-       t_3dvec *dist_ij;
-       double b,db,tmp;
-       double f_c,df_c,f_a,df_a;
-       double chi,ni,betaini,nj,betajnj;
-       double zeta;
+       unsigned char brand;
+       double S2;
+       t_3dvec dist_ij;
+       double d_ij2,d_ij;
 
        params=moldyn->pot_params;
        exchange=&(params->exchange);
 
-       /* we do not run if f_c_ij was detected to be 0! */
-       if(!(exchange->run2bp_post))
-               return 0;
-
-       f_c=exchange->f_c;
-       df_c=exchange->df_c;
-       f_a=exchange->f_a;
-       df_a=exchange->df_a;
-       betaini=exchange->betaini;
-       betajnj=exchange->betajnj;
-       ni=*(exchange->n_i);
-       nj=*(exchange->n_j);
-       chi=exchange->chi;
-       dist_ij=&(exchange->dist_ij);
-       
-       /* Vij and dVij */
-       zeta=exchange->zeta_ij;
-       if(zeta==0.0) {
-               moldyn->debug++;                /* just for debugging ... */
-               b=chi;
-               v3_scale(&force,dist_ij,df_a*b*f_c);
-       }
-       else {
-               tmp=betaini*pow(zeta,ni-1.0);           /* beta^n * zeta^n-1 */
-               b=(1+zeta*tmp);                         /* 1 + beta^n zeta^n */
-               db=chi*pow(b,-1.0/(2*ni)-1);            /* x(...)^(-1/2n - 1) */
-               b=db*b;                                 /* b_ij */
-               db*=-0.5*tmp;                           /* db_ij */
-               v3_scale(&force,&(exchange->dzeta_ij),f_a*db);
-               v3_scale(&temp,dist_ij,df_a*b);
-               v3_add(&force,&force,&temp);
-               v3_scale(&force,&force,f_c);
-       }
-       v3_scale(&temp,dist_ij,df_c*b*f_a);
-       v3_add(&force,&force,&temp);
-       v3_scale(&force,&force,-0.5);
-
-       /* add force */
-       v3_add(&(ai->f),&(ai->f),&force);
+       /* reset zeta sum */
+       exchange->zeta_ij=0.0;
 
-       /* virial */
-       virial_calc(ai,&force,dist_ij);
-       //ai->virial.xx-=force.x*dist_ij->x;
-       //ai->virial.yy-=force.y*dist_ij->y;
-       //ai->virial.zz-=force.z*dist_ij->z;
-       //ai->virial.xy-=force.x*dist_ij->y;
-       //ai->virial.xz-=force.x*dist_ij->z;
-       //ai->virial.yz-=force.y*dist_ij->z;
+       /*
+        * set ij depending values
+        */
 
-#ifdef DEBUG
-if(ai==&(moldyn->atom[0])) {
-       printf("dVij (3bp) contrib:\n");
-       printf("%f | %f\n",force.x,ai->f.x);
-       printf("%f | %f\n",force.y,ai->f.y);
-       printf("%f | %f\n",force.z,ai->f.z);
-}
-#endif
-#ifdef VDEBUG
-if(ai==&(moldyn->atom[0])) {
-       printf("dVij (3bp) contrib:\n");
-       printf("%f | %f\n",force.x*dist_ij->x,ai->virial.xx);
-       printf("%f | %f\n",force.y*dist_ij->y,ai->virial.yy);
-       printf("%f | %f\n",force.z*dist_ij->z,ai->virial.zz);
-}
-#endif
+       brand=ai->brand;
+       
+       if(brand==aj->brand)
+               S2=params->S2[brand];
+       else
+               S2=params->S2mixed;
 
-       /* add energy of 3bp sum */
-       moldyn->energy+=(0.5*f_c*b*f_a);
+       /* dist_ij, d_ij2 */
+       v3_sub(&dist_ij,&(aj->r),&(ai->r));
+       if(bc) check_per_bound(moldyn,&dist_ij);
+       d_ij2=v3_absolute_square(&dist_ij);
 
-       /* dVji */
-       zeta=exchange->zeta_ji;
-       if(zeta==0.0) {
-               moldyn->debug++;
-               b=chi;
-               v3_scale(&force,dist_ij,df_a*b*f_c);
-       }
-       else {
-               tmp=betajnj*pow(zeta,nj-1.0);           /* beta^n * zeta^n-1 */
-               b=(1+zeta*tmp);                         /* 1 + beta^n zeta^n */
-               db=chi*pow(b,-1.0/(2*nj)-1);            /* x(...)^(-1/2n - 1) */
-               b=db*b;                                 /* b_ij */
-               db*=-0.5*tmp;                           /* db_ij */
-               v3_scale(&force,&(exchange->dzeta_ji),f_a*db);
-               v3_scale(&temp,dist_ij,df_a*b);
-               v3_add(&force,&force,&temp);
-               v3_scale(&force,&force,f_c);
+       /* if d_ij2 > S2 => no force & potential energy contribution */
+       if(d_ij2>S2) {
+               moldyn->run3bp=0;
+               return 0;
        }
-       v3_scale(&temp,dist_ij,df_c*b*f_a);
-       v3_add(&force,&force,&temp);
-       v3_scale(&force,&force,-0.5);
 
-       /* add force */
-       v3_add(&(ai->f),&(ai->f),&force);
-
-       /* virial - plus sign, as dist_ij = - dist_ji - (really??) */
-// TEST ... with a minus instead
-       virial_calc(ai,&force,dist_ij);
-       //ai->virial.xx-=force.x*dist_ij->x;
-       //ai->virial.yy-=force.y*dist_ij->y;
-       //ai->virial.zz-=force.z*dist_ij->z;
-       //ai->virial.xy-=force.x*dist_ij->y;
-       //ai->virial.xz-=force.x*dist_ij->z;
-       //ai->virial.yz-=force.y*dist_ij->z;
+       /* d_ij */
+       d_ij=sqrt(d_ij2);
 
-#ifdef DEBUG
-if(ai==&(moldyn->atom[0])) {
-       printf("dVji (3bp) contrib:\n");
-       printf("%f | %f\n",force.x,ai->f.x);
-       printf("%f | %f\n",force.y,ai->f.y);
-       printf("%f | %f\n",force.z,ai->f.z);
-}
-#endif
-#ifdef VDEBUG
-if(ai==&(moldyn->atom[0])) {
-       printf("dVji (3bp) contrib:\n");
-       printf("%f | %f\n",force.x*dist_ij->x,ai->virial.xx);
-       printf("%f | %f\n",force.y*dist_ij->y,ai->virial.yy);
-       printf("%f | %f\n",force.z*dist_ij->z,ai->virial.zz);
-}
-#endif
+       /* store values */
+       exchange->dist_ij=dist_ij;
+       exchange->d_ij2=d_ij2;
+       exchange->d_ij=d_ij;
 
+       /* reset k counter for first k loop */
+       exchange->kcount=0;
+               
        return 0;
 }
 
-/* tersoff 3 body part */
-
-int tersoff_mult_3bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,t_atom *ak,u8 bc) {
+/* tersoff 3 body potential function (first k loop) */
+int tersoff_mult_3bp_k1(t_moldyn *moldyn,
+                        t_atom *ai,t_atom *aj,t_atom *ak,u8 bc) {
 
        t_tersoff_mult_params *params;
        t_tersoff_exchange *exchange;
-       t_3dvec dist_ij,dist_ik,dist_jk;
-       t_3dvec temp1,temp2;
-       t_3dvec *dzeta;
-       double R,S,S2,s_r;
-       double B,mu;
-       double d_ij,d_ik,d_jk,d_ij2,d_ik2,d_jk2;
-       double rr,dd;
-       double f_c,df_c;
-       double f_c_ik,df_c_ik,arg;
-       double f_c_jk;
-       double n,c,d,h;
-       double c2,d2,c2d2;
-       double cos_theta,d_costheta1,d_costheta2;
-       double h_cos,d2_h_cos2;
-       double frac,g,zeta,chi;
-       double tmp;
-       int brand;
+       unsigned char brand;
+       double R,S,S2;
+       t_3dvec dist_ij,dist_ik;
+       double d_ik2,d_ik,d_ij;
+       double cos_theta,h_cos,d2_h_cos2,frac,g,dg,s_r,arg;
+       double f_c_ik,df_c_ik;
+       int kcount;
 
        params=moldyn->pot_params;
        exchange=&(params->exchange);
+       kcount=exchange->kcount;
 
-       if(!(exchange->run3bp))
-               return 0;
-
-       /*
-        * calc of 3bp contribution of V_ij and dV_ij/ji/jk &
-        * 2bp contribution of dV_jk
-        *
-        * for Vij and dV_ij we still need:
-        * - b_ij, db_ij (zeta_ij)
-        *   - f_c_ik, df_c_ik, constants_i, cos_theta_ijk, d_costheta_ijk
-        *
-        * for dV_ji we still need:
-        * - b_ji, db_ji (zeta_ji)
-        *   - f_c_jk, d_c_jk, constants_j, cos_theta_jik, d_costheta_jik
-        *
-        * for dV_jk we need:
-        * - f_c_jk
-        * - f_a_jk
-        * - db_jk (zeta_jk)
-        *   - f_c_ji, df_c_ji, constants_j, cos_theta_jki, d_costheta_jki
-        *
-        */
-
-       /*
-        * get exchange data 
-        */
-
-       /* dist_ij, d_ij - this is < S_ij ! */
-       dist_ij=exchange->dist_ij;
-       d_ij=exchange->d_ij;
-       d_ij2=exchange->d_ij2;
-
-       /* f_c_ij, df_c_ij (same for ji) */
-       f_c=exchange->f_c;
-       df_c=exchange->df_c;
-
-       /*
-        * calculate unknown values now ...
-        */
-
-       /* V_ij and dV_ij stuff (in b_ij there is f_c_ik) */
-
-       /* dist_ik, d_ik */
-       v3_sub(&dist_ik,&(ak->r),&(ai->r));
-       if(bc) check_per_bound(moldyn,&dist_ik);
-       d_ik2=v3_absolute_square(&dist_ik);
+       if(kcount>TERSOFF_MAXN) {
+               printf("FATAL: neighbours = %d\n",kcount);
+               printf("  -> %d %d %d\n",ai->tag,aj->tag,ak->tag);
+       }
 
        /* ik constants */
        brand=ai->brand;
@@ -497,215 +270,298 @@ int tersoff_mult_3bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,t_atom *ak,u8 bc) {
                S2=params->S2mixed;
        }
 
-       /* zeta_ij/dzeta_ij contribution only for d_ik < S */
-       if(d_ik2<S2) {
-
-               /* now we need d_ik */
-               d_ik=sqrt(d_ik2);
-
-               /* get constants_i from exchange data */
-               n=*(exchange->n_i);
-               c=*(exchange->c_i);
-               d=*(exchange->d_i);
-               h=*(exchange->h_i);
-               c2=exchange->ci2;
-               d2=exchange->di2;
-               c2d2=exchange->ci2di2;
-
-               /* cosine of theta_ijk by scalaproduct */
-               rr=v3_scalar_product(&dist_ij,&dist_ik);
-               dd=d_ij*d_ik;
-               cos_theta=rr/dd;
-
-               /* d_costheta */
-               tmp=1.0/dd;
-               d_costheta1=cos_theta/d_ij2-tmp;
-               d_costheta2=cos_theta/d_ik2-tmp;
-
-               /* some usefull values */
-               h_cos=(h-cos_theta);
-               d2_h_cos2=d2+(h_cos*h_cos);
-               frac=c2/(d2_h_cos2);
-
-               /* g(cos_theta) */
-               g=1.0+c2d2-frac;
-
-               /* d_costheta_ij and dg(cos_theta) - needed in any case! */
-               v3_scale(&temp1,&dist_ij,d_costheta1);
-               v3_scale(&temp2,&dist_ik,d_costheta2);
-               v3_add(&temp1,&temp1,&temp2);
-               v3_scale(&temp1,&temp1,-2.0*frac*h_cos/d2_h_cos2); /* dg */
-
-               /* f_c_ik & df_c_ik + {d,}zeta contribution */
-               dzeta=&(exchange->dzeta_ij);
-               if(d_ik<R) {
-                       /* {d,}f_c_ik */
-                       // => f_c_ik=1.0;
-                       // => df_c_ik=0.0; of course we do not set this!
-
-                       /* zeta_ij */
-                       exchange->zeta_ij+=g;
-
-                       /* dzeta_ij */
-                       v3_add(dzeta,dzeta,&temp1);
-               }
-               else {
-                       /* {d,}f_c_ik */
-                       s_r=S-R;
-                       arg=M_PI*(d_ik-R)/s_r;
-                       f_c_ik=0.5+0.5*cos(arg);
-                       df_c_ik=0.5*sin(arg)*(M_PI/(s_r*d_ik));
-
-                       /* zeta_ij */
-                       exchange->zeta_ij+=f_c_ik*g;
-
-                       /* dzeta_ij */
-                       v3_scale(&temp1,&temp1,f_c_ik);
-                       v3_scale(&temp2,&dist_ik,g*df_c_ik);
-                       v3_add(&temp1,&temp1,&temp2);
-                       v3_add(dzeta,dzeta,&temp1);
-               }
+       /* dist_ik, d_ik2 */
+       v3_sub(&dist_ik,&(ak->r),&(ai->r));
+       if(bc) check_per_bound(moldyn,&dist_ik);
+       d_ik2=v3_absolute_square(&dist_ik);
+
+       /* store data for second k loop */
+       exchange->dist_ik[kcount]=dist_ik;
+       exchange->d_ik2[kcount]=d_ik2;
+
+       /* return if not within cutoff */
+       if(d_ik2>S2) {
+               exchange->kcount++;
+               return 0;
+       }
+
+       /* d_ik */
+       d_ik=sqrt(d_ik2);
+
+       /* dist_ij, d_ij */
+       dist_ij=exchange->dist_ij;
+       d_ij=exchange->d_ij;
+
+       /* cos theta */
+       cos_theta=v3_scalar_product(&dist_ij,&dist_ik)/(d_ij*d_ik);
+
+       /* g_ijk */
+       h_cos=*(exchange->h_i)-cos_theta;
+       d2_h_cos2=exchange->di2+(h_cos*h_cos);
+       frac=exchange->ci2/d2_h_cos2;
+       g=1.0+exchange->ci2di2-frac;
+       dg=-2.0*frac*h_cos/d2_h_cos2;
+
+       /* zeta sum += f_c_ik * g_ijk */
+       if(d_ik<=R) {
+               exchange->zeta_ij+=g;
+               f_c_ik=1.0;
+               df_c_ik=0.0;
+       }
+       else {
+               s_r=S-R;
+               arg=M_PI*(d_ik-R)/s_r;
+               f_c_ik=0.5+0.5*cos(arg);
+               df_c_ik=0.5*sin(arg)*(M_PI/(s_r*d_ik));
+               exchange->zeta_ij+=f_c_ik*g;
        }
 
-       /* dV_ji stuff (in b_ji there is f_c_jk) + dV_jk stuff! */
+       /* store even more data for second k loop */
+       exchange->g[kcount]=g;
+       exchange->dg[kcount]=dg;
+       exchange->d_ik[kcount]=d_ik;
+       exchange->cos_theta[kcount]=cos_theta;
+       exchange->f_c_ik[kcount]=f_c_ik;
+       exchange->df_c_ik[kcount]=df_c_ik;
 
-       /* dist_jk, d_jk */
-       v3_sub(&dist_jk,&(ak->r),&(aj->r));
-       if(bc) check_per_bound(moldyn,&dist_jk);
-       d_jk2=v3_absolute_square(&dist_jk);
+       /* increase k counter */
+       exchange->kcount++;
+
+       return 0;
+}
+
+int tersoff_mult_3bp_j2(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
+
+       t_tersoff_mult_params *params;
+       t_tersoff_exchange *exchange;
+       t_3dvec force;
+       double f_a,df_a,b,db,f_c,df_c;
+       double mu,B,chi;
+       double d_ij;
+       unsigned char brand;
+       double ni,tmp;
+       double S,R,s_r,arg;
+
+       params=moldyn->pot_params;
+       exchange=&(params->exchange);
 
-       /* jk constants */
        brand=aj->brand;
-       if(brand==ak->brand) {
-               R=params->R[brand];
+       if(brand==ai->brand) {
                S=params->S[brand];
-               S2=params->S2[brand];
+               R=params->R[brand];
                B=params->B[brand];
                mu=params->mu[brand];
                chi=1.0;
        }
        else {
-               R=params->Rmixed;
                S=params->Smixed;
-               S2=params->S2mixed;
+               R=params->Rmixed;
                B=params->Bmixed;
                mu=params->mu_m;
                chi=params->chi;
        }
 
-       /* zeta_ji/dzeta_ji contribution only for d_jk < S_jk */
-       if(d_jk2<S2) {
-
-               /* now we need d_ik */
-               d_jk=sqrt(d_jk2);
-
-               /* constants_j from exchange data */
-               n=*(exchange->n_j);
-               c=*(exchange->c_j);
-               d=*(exchange->d_j);
-               h=*(exchange->h_j);
-               c2=exchange->cj2;
-               d2=exchange->dj2;
-               c2d2=exchange->cj2dj2;
-
-               /* cosine of theta_jik by scalaproduct */
-               rr=-v3_scalar_product(&dist_ij,&dist_jk); /* -1, as ij -> ji */
-               dd=d_ij*d_jk;
-               cos_theta=rr/dd;
-
-               /* d_costheta */
-               d_costheta1=1.0/dd;
-               d_costheta2=cos_theta/d_ij2;
-
-               /* some usefull values */
-               h_cos=(h-cos_theta);
-               d2_h_cos2=d2+(h_cos*h_cos);
-               frac=c2/(d2_h_cos2);
-
-               /* g(cos_theta) */
-               g=1.0+c2d2-frac;
-
-               /* d_costheta_jik and dg(cos_theta) - needed in any case! */
-               v3_scale(&temp1,&dist_jk,d_costheta1);
-               v3_scale(&temp2,&dist_ij,-d_costheta2); /* ji -> ij => -1 */
-               //v3_add(&temp1,&temp1,&temp2);
-               v3_sub(&temp1,&temp1,&temp2); /* there is a minus! */
-               v3_scale(&temp1,&temp1,-2.0*frac*h_cos/d2_h_cos2); /* dg */
-
-               /* store dg in temp2 and use it for dVjk later */
-               v3_copy(&temp2,&temp1);
-
-               /* f_c_jk + {d,}zeta contribution (df_c_jk = 0) */
-               dzeta=&(exchange->dzeta_ji);
-               if(d_jk<R) {
-                       /* f_c_jk */
-                       f_c_jk=1.0;
-
-                       /* zeta_ji */
-                       exchange->zeta_ji+=g;
-
-                       /* dzeta_ji */
-                       v3_add(dzeta,dzeta,&temp1);
-               }
-               else {
-                       /* f_c_jk */
-                       s_r=S-R;
-                       arg=M_PI*(d_jk-R)/s_r;
-                       f_c_jk=0.5+0.5*cos(arg);
-
-                       /* zeta_ji */
-                       exchange->zeta_ji+=f_c_jk*g;
-
-                       /* dzeta_ji */
-                       v3_scale(&temp1,&temp1,f_c_jk);
-                       v3_add(dzeta,dzeta,&temp1);
-               }
-
-               /* dV_jk stuff | add force contribution on atom i immediately */
-               if(exchange->d_ij_between_rs) {
-                       zeta=f_c*g;
-                       v3_scale(&temp1,&temp2,f_c);
-                       v3_scale(&temp2,&dist_ij,df_c*g);
-                       v3_add(&temp2,&temp2,&temp1); /* -> dzeta_jk in temp2 */
-               }
-               else {
-                       zeta=g;
-                       // dzeta_jk is simply dg, which is stored in temp2
-               }
-               /* betajnj * zeta_jk ^ nj-1 */
-               tmp=exchange->betajnj*pow(zeta,(n-1.0));
-               tmp=-chi/2.0*pow((1+tmp*zeta),(-1.0/(2.0*n)-1))*tmp;
-               v3_scale(&temp2,&temp2,tmp*B*exp(-mu*d_jk)*f_c_jk*0.5);
-               v3_add(&(ai->f),&(ai->f),&temp2); /* -1 skipped in f_a calc ^ */
-                                                 /* scaled with 0.5 ^ */
-
-               /* virial */
-               ai->virial.xx-=temp2.x*dist_jk.x;
-               ai->virial.yy-=temp2.y*dist_jk.y;
-               ai->virial.zz-=temp2.z*dist_jk.z;
-               ai->virial.xy-=temp2.x*dist_jk.y;
-               ai->virial.xz-=temp2.x*dist_jk.z;
-               ai->virial.yz-=temp2.y*dist_jk.z;
+       d_ij=exchange->d_ij;
+
+       /* f_c, df_c */
+       if(d_ij<R) {
+               f_c=1.0;
+               df_c=0.0;
+       }
+       else {
+               s_r=S-R;
+               arg=M_PI*(d_ij-R)/s_r;
+               f_c=0.5+0.5*cos(arg);
+               df_c=0.5*sin(arg)*(M_PI/(s_r*d_ij));
+       }
+
+       /* f_a, df_a */
+       f_a=-B*exp(-mu*d_ij);
+       df_a=mu*f_a/d_ij;
+
+       /* b, db */
+       if(exchange->zeta_ij==0.0) {
+               b=chi;
+               db=0.0;
+       }
+       else {
+               ni=*(exchange->n_i);
+               tmp=exchange->betaini*pow(exchange->zeta_ij,ni-1.0);
+               b=(1.0+exchange->zeta_ij*tmp);
+               db=chi*pow(b,-1.0/(2*ni)-1.0);
+               b=db*b;
+               db*=-0.5*tmp;
+       }
+
+       /* force contribution */
+       v3_scale(&force,&(exchange->dist_ij),df_a*f_c+f_a*df_c);
+       v3_scale(&force,&force,-0.5*b);
+       v3_add(&(ai->f),&(ai->f),&force);
+       v3_sub(&(aj->f),&(aj->f),&force);
 
 #ifdef DEBUG
-if(ai==&(moldyn->atom[0])) {
-       printf("dVjk (3bp) contrib:\n");
-       printf("%f | %f\n",temp2.x,ai->f.x);
-       printf("%f | %f\n",temp2.y,ai->f.y);
-       printf("%f | %f\n",temp2.z,ai->f.z);
-}
+       if((ai==&(moldyn->atom[0]))|(aj==&(moldyn->atom[0]))) {
+               printf("force 3bp (j2): [%d %d sum]\n",ai->tag,aj->tag);
+               printf("adding %f %f %f\n",force.x,force.y,force.z);
+               if(ai==&(moldyn->atom[0]))
+                       printf("total i: %f %f %f\n",ai->f.x,ai->f.y,ai->f.z);
+               if(aj==&(moldyn->atom[0]))
+                       printf("total j: %f %f %f\n",aj->f.x,aj->f.y,aj->f.z);
+       }
 #endif
-#ifdef VDEBUG
-if(ai==&(moldyn->atom[0])) {
-       printf("dVjk (3bp) contrib:\n");
-       printf("%f | %f\n",temp2.x*dist_jk.x,ai->virial.xx);
-       printf("%f | %f\n",temp2.y*dist_jk.y,ai->virial.yy);
-       printf("%f | %f\n",temp2.z*dist_jk.z,ai->virial.zz);
+
+       /* virial */
+       //virial_calc(ai,&force,&(exchange->dist_ij));
+       //virial_calc(aj,&force,&(exchange->dist_ij));
+
+       /* dzeta prefactor = - 0.5 f_c f_a db */
+       exchange->pre_dzeta=-0.5*f_a*f_c*db;
+
+       /* energy contribution */
+       moldyn->energy+=0.5*f_c*b*f_a;
+
+       /* reset k counter for second k loop */
+       exchange->kcount=0;
+               
+       return 0;
 }
+
+/* tersoff 3 body potential function (second k loop) */
+int tersoff_mult_3bp_k2(t_moldyn *moldyn,
+                        t_atom *ai,t_atom *aj,t_atom *ak,u8 bc) {
+
+       t_tersoff_mult_params *params;
+       t_tersoff_exchange *exchange;
+       int kcount;
+       t_3dvec dist_ik,dist_ij;
+       double d_ik2,d_ik,d_ij2,d_ij;
+       unsigned char brand;
+       double S2;
+       double g,dg,cos_theta;
+       double pre_dzeta;
+       double f_c_ik,df_c_ik;
+       double dijdik_inv,fcdg,dfcg;
+       t_3dvec dcosdri,dcosdrj,dcosdrk;
+       t_3dvec force,tmp;
+
+       params=moldyn->pot_params;
+       exchange=&(params->exchange);
+       kcount=exchange->kcount;
+
+       if(kcount>TERSOFF_MAXN)
+               printf("FATAL: neighbours!\n");
+
+       /* d_ik2 */
+       d_ik2=exchange->d_ik2[kcount];
+
+       brand=ak->brand;
+       if(brand==ai->brand)
+               S2=params->S2[brand];
+       else
+               S2=params->S2mixed;
+
+       /* return if d_ik > S */
+       if(d_ik2>S2) {
+               exchange->kcount++;
+               return 0;
+       }
+
+       /* prefactor dzeta */
+       pre_dzeta=exchange->pre_dzeta;
+
+       /* dist_ik, d_ik */
+       dist_ik=exchange->dist_ik[kcount];
+       d_ik=exchange->d_ik[kcount];
+
+       /* f_c_ik, df_c_ik */
+       f_c_ik=exchange->f_c_ik[kcount];
+       df_c_ik=exchange->df_c_ik[kcount];
+
+       /* dist_ij, d_ij2, d_ij */
+       dist_ij=exchange->dist_ij;
+       d_ij2=exchange->d_ij2;
+       d_ij=exchange->d_ij;
+
+       /* g, dg, cos_theta */
+       g=exchange->g[kcount];
+       dg=exchange->dg[kcount];
+       cos_theta=exchange->cos_theta[kcount];
+
+       /* cos_theta derivatives wrt i,j,k */
+       dijdik_inv=1.0/(d_ij*d_ik);
+       v3_scale(&dcosdrj,&dist_ik,dijdik_inv);
+       v3_scale(&tmp,&dist_ij,-cos_theta/d_ij2);
+       v3_add(&dcosdrj,&dcosdrj,&tmp);
+       v3_scale(&dcosdrk,&dist_ij,dijdik_inv);
+       v3_scale(&tmp,&dist_ik,-cos_theta/d_ik2);
+       v3_add(&dcosdrk,&dcosdrk,&tmp);
+       v3_add(&dcosdri,&dcosdrj,&dcosdrk);
+       v3_scale(&dcosdri,&dcosdri,-1.0);
+
+       /* f_c_ik * dg, df_c_ik * g */
+       fcdg=f_c_ik*dg;
+       dfcg=df_c_ik*g;
+
+       /* derivative wrt i */
+       v3_scale(&force,&dist_ik,dfcg);
+       v3_scale(&tmp,&dcosdri,fcdg);
+       v3_add(&force,&force,&tmp);
+       v3_scale(&force,&force,pre_dzeta);
+
+       /* force contribution */
+       v3_add(&(ai->f),&(ai->f),&force);
+       
+#ifdef DEBUG
+       if(ai==&(moldyn->atom[0])) {
+               printf("force 3bp (k2): [%d %d %d]\n",ai->tag,aj->tag,ak->tag);
+               printf("adding %f %f %f\n",force.x,force.y,force.z);
+               printf("total i: %f %f %f\n",ai->f.x,ai->f.y,ai->f.z);
+       }
+#endif
+
+       /* virial */
+       //virial_calc(ai,&force,&dist_ij);
+
+       /* derivatice wrt j */
+       v3_scale(&force,&dcosdrj,fcdg*pre_dzeta);
+
+       /* force contribution */
+       v3_add(&(aj->f),&(aj->f),&force);
+
+#ifdef DEBUG
+       if(aj==&(moldyn->atom[0])) {
+               printf("force 3bp (k2): [%d %d %d]\n",ai->tag,aj->tag,ak->tag);
+               printf("adding %f %f %f\n",force.x,force.y,force.z);
+               printf("total j: %f %f %f\n",aj->f.x,aj->f.y,aj->f.z);
+       }
 #endif
 
+       /* virial */
+       //virial_calc(aj,&force,&dist_ij);
+
+       /* derivative wrt k */
+       v3_scale(&force,&dist_ik,dfcg);
+       v3_scale(&tmp,&dcosdrk,fcdg);
+       v3_add(&force,&force,&tmp);
+       v3_scale(&force,&force,pre_dzeta);
+
+       /* force contribution */
+       v3_add(&(ak->f),&(ak->f),&force);
+
+#ifdef DEBUG
+       if(ak==&(moldyn->atom[0])) {
+               printf("force 3bp (k2): [%d %d %d]\n",ai->tag,aj->tag,ak->tag);
+               printf("adding %f %f %f\n",force.x,force.y,force.z);
+               printf("total k: %f %f %f\n",ak->f.x,ak->f.y,ak->f.z);
        }
+#endif
+
+       /* virial */
+       virial_calc(ak,&force,&dist_ik);
+       
+       /* increase k counter */
+       exchange->kcount++;     
 
        return 0;
-}
 
+}