/*************************************************************************/
/*                                                                       */
/*  Copyright (c) 1994 Stanford University                               */
/*                                                                       */
/*  All rights reserved.                                                 */
/*                                                                       */
/*  Permission is given to use, copy, and modify this software for any   */
/*  non-commercial purpose as long as this copyright notice is not       */
/*  removed.  All other uses, including redistribution in whole or in    */
/*  part, are forbidden without prior written permission.                */
/*                                                                       */
/*  This software is provided with absolutely no warranty and no         */
/*  support.                                                             */
/*                                                                       */
/*************************************************************************/

EXTERN_ENV

#include "mdvar.h"
#include "frcnst.h"
#include "water.h"
#include "wwpot.h"
#include <math.h>
#include "parameters.h"
#include "mddata.h"
#include "split.h"
#include "global.h"

POTENG(POTA,POTR,PTRF,ProcID)
  double *POTA, *POTR, *PTRF;     /* some shared sums computed by POTENG */
  unsigned ProcID;
  
{
    
    /*
      this routine calculates the potential energy of the system.  it is only
      called at periods specified by the user, typically in those
      time-steps when the user wants to print output.
      FC11 ,FC12, FC13, and FC33 are the quardratic force constants
      */
    
    int KC, K;
    int XBOX, YBOX, ZBOX;
    int X_NUM, Y_NUM, Z_NUM;
    int i, j, k;
    double R1, R2, RX, COS, DT, DR1, DR2, DR1S, DR2S, DRP, DRS;
    double XL[15], YL[15], ZL[15], RS[15], RL[15];
    double DTS;
    double LPOTA, LPOTR, LPTRF;
    struct link *curr_ptr, *neighbor_ptr;
    struct list_of_boxes *curr_box;
    double *tx_p, *ty_p, *tz_p;
    double tempa, tempb, tempc;
    
    /*  compute intra-molecular potential energy */
    
    LPOTA=0.0;
    curr_box = my_boxes[ProcID];
    while (curr_box) {
        
        i = curr_box->coord[XDIR];  /* X coordinate of box */
        j = curr_box->coord[YDIR];  /* Y coordinate of box */
        k = curr_box->coord[ZDIR];  /* Z coordinate of box */
        
        /* Go through the molecules in a box */
        
        curr_ptr = BOX[i][j][k].list;
        while (curr_ptr) {
            
            tx_p = curr_ptr->mol.F[DISP][XDIR];
            ty_p = curr_ptr->mol.F[DISP][YDIR];
            tz_p = curr_ptr->mol.F[DISP][ZDIR];
            
            curr_ptr->mol.VM[XDIR] = C1 * tx_p[O] +
                C2 * (tx_p[H1] + 
                      tx_p[H2]);
            curr_ptr->mol.VM[YDIR] = C1*ty_p[O] +
                C2*(ty_p[H1] + 
                    ty_p[H2]);
            curr_ptr->mol.VM[ZDIR] = C1*tz_p[O] +
                C2*(tz_p[H1] + 
                    tz_p[H2]);
            tempa = tx_p[O] - tx_p[H1];
            tempb = ty_p[O] - ty_p[H1];
            tempc = tz_p[O] - tz_p[H1];
            R1 = tempa * tempa + tempb * tempb + tempc * tempc;
            tempa = tx_p[O] - tx_p[H2];
            tempb = ty_p[O] - ty_p[H2];
            tempc = tz_p[O] - tz_p[H2];
            R2 = tempa * tempa + tempb * tempb + tempc * tempc;
            
            RX = ((tx_p[O] - tx_p[H1]) *
                  (tx_p[O] - tx_p[H2])) +
                      ((ty_p[O] - ty_p[H1]) *
                       (ty_p[O] - ty_p[H2])) +
                           ((tz_p[O] - tz_p[H1]) *
                            (tz_p[O] - tz_p[H2]));
            
            R1=sqrt(R1);
            R2=sqrt(R2);
            COS=RX/(R1*R2);
            DT=(acos(COS)-ANGLE)*ROH;
            DR1=R1-ROH;
            DR2=R2-ROH;
            DR1S=DR1*DR1;
            DR2S=DR2*DR2;
            DRP=DR1+DR2;
            DTS=DT*DT;
            LPOTA += (FC11*(DR1S+DR2S)+FC33*DTS)*0.5
                +FC12*DR1*DR2+FC13*DRP*DT
                    +(FC111*(DR1S*DR1+DR2S*DR2)+FC333*DTS*DT+FC112*DRP*DR1*DR2+
                      FC113*(DR1S+DR2S)*DT+FC123*DR1*DR2*DT+FC133*DRP*DTS)*ROHI;
            
            LPOTA += (FC1111*(DR1S*DR1S+DR2S*DR2S)+FC3333*DTS*DTS+
                      FC1112*(DR1S+DR2S)*DR1*DR2+FC1122*DR1S*DR2S+
                      FC1113*(DR1S*DR1+DR2S*DR2)*DT+FC1123*DRP*DR1*DR2*DT+
                      FC1133*(DR1S+DR2S)*DTS+FC1233*DR1*DR2*DTS+
                      FC1333*DRP*DTS*DT)*ROHI2;
            
            curr_ptr = curr_ptr->next_mol;
        } /* while curr_ptr */
        curr_box = curr_box->next_box;
    } /* while curr_box */
    
    
    BARRIER(gl->PotengBar, NumProcs);
    
    /*  compute inter-molecular potential energy */
    
    LPOTR=0.0;
    LPTRF=0.0;
    curr_box = my_boxes[ProcID];
    while (curr_box) {
        
        i = curr_box->coord[XDIR];  /* X coordinate of box */
        j = curr_box->coord[YDIR];  /* Y coordinate of box */
        k = curr_box->coord[ZDIR];  /* Z coordinate of box */
        
        /* loop over nearest neighbor boxes */
        
        for (XBOX=i-1; XBOX<=i+1; XBOX++) {
            for (YBOX=j-1; YBOX<=j+1; YBOX++) {
                for (ZBOX=k-1; ZBOX<=k+1; ZBOX++) {
                    
                    X_NUM = XBOX;
                    Y_NUM = YBOX;
                    Z_NUM = ZBOX;
                    
                    if ((BOX_PER_SIDE == 2) && ((XBOX < 0) || (XBOX == 2) ||
                                                (YBOX < 0) || (YBOX == 2) || 
                                                (ZBOX < 0) || (ZBOX == 2)))
                        continue;
                    
                    /* Make box number valid if out of box */
                    
                    if (X_NUM == -1)
                        X_NUM += BOX_PER_SIDE;
                    else if (X_NUM >= BOX_PER_SIDE)
                        X_NUM -= BOX_PER_SIDE;
                    if (Y_NUM == -1)
                        Y_NUM += BOX_PER_SIDE;
                    else if (Y_NUM >= BOX_PER_SIDE)
                        Y_NUM -= BOX_PER_SIDE;
                    if (Z_NUM == -1)
                        Z_NUM += BOX_PER_SIDE;
                    else if (Z_NUM >= BOX_PER_SIDE)
                        Z_NUM -= BOX_PER_SIDE;
                    
                    
                    /* Don't do current box more than once */
                    if ((X_NUM == i) && (Y_NUM == j) && (Z_NUM == k) &&
                        ((XBOX != i) || (YBOX != j) || (ZBOX !=k))) {
                        continue;
                    }
                    
                    neighbor_ptr = BOX[X_NUM][Y_NUM][Z_NUM].list;
                    while (neighbor_ptr) {
                        /* go through current box list */
                        curr_ptr = BOX[i][j][k].list;
                        while (curr_ptr) {
                            
                            /* Don't do interaction with same molecule */
                            
                            if (curr_ptr == neighbor_ptr) {
                                curr_ptr = curr_ptr->next_mol;
                                continue;
                            }
                            
                            /*  compute some intermolecular distances */
                            CSHIFT(curr_ptr->mol.F[DISP][XDIR],neighbor_ptr->mol.F[DISP][XDIR],
                                   curr_ptr->mol.VM[XDIR],neighbor_ptr->mol.VM[XDIR],XL,BOXH,BOXL);
                            CSHIFT(curr_ptr->mol.F[DISP][YDIR],neighbor_ptr->mol.F[DISP][YDIR],
                                   curr_ptr->mol.VM[YDIR],neighbor_ptr->mol.VM[YDIR],YL,BOXH,BOXL);
                            CSHIFT(curr_ptr->mol.F[DISP][ZDIR],neighbor_ptr->mol.F[DISP][ZDIR],
                                   curr_ptr->mol.VM[ZDIR],neighbor_ptr->mol.VM[ZDIR],ZL,BOXH,BOXL);
                            
                            
                            KC=0;
                            for (K = 0; K < 9; K++) {
                                RS[K]=XL[K]*XL[K]+YL[K]*YL[K]+ZL[K]*ZL[K];
                                if (RS[K] > CUT2) 
                                    KC++;
                            } /* for K */
                            
                            
                            if (KC != 9) {
                                for (K = 0; K < 9; K++) {
                                    if (RS[K] <= CUT2) {
                                        RL[K]=sqrt(RS[K]);
                                    }
                                    else {
                                        RL[K]=CUTOFF;
                                        RS[K]=CUT2;
                                    } /* else */
                                } /* for K */
                                
                                LPOTR= LPOTR-QQ2/RL[1]-QQ2/RL[2]-QQ2/RL[3]-QQ2/RL[4]
                                    + QQ/RL[5]+ QQ/RL[6]+ QQ/RL[7]+ QQ/RL[8]
                                        + QQ4/RL[0];
                                
                                LPTRF= LPTRF-REF2*RS[0]-REF1*((RS[5]+RS[6]+RS[7]+RS[8])*0.5
                                                              -RS[1]-RS[2]-RS[3]-RS[4]);
                                
                                if (KC <= 0) {
                                    for (K = 9; K <  14; K++)  {
                                        RL[K]=sqrt(XL[K]*XL[K]+YL[K]*YL[K]+ZL[K]*ZL[K]);
                                    }
                                    
                                    LPOTR= LPOTR+A1* exp(-B1*RL[9])
                                        +A2*(exp(-B2*RL[ 5])+exp(-B2*RL[ 6])
                                             +exp(-B2*RL[ 7])+exp(-B2*RL[ 8]))
                                            +A3*(exp(-B3*RL[10])+exp(-B3*RL[11])
                                                 +exp(-B3*RL[12])+exp(-B3*RL[13]))
                                                -A4*(exp(-B4*RL[10])+exp(-B4*RL[11])
                                                     +exp(-B4*RL[12])+exp(-B4*RL[13]));
                                } /* if KC <= 0 */
                                
                                
                            } /* if KC != 9 */
                            curr_ptr = curr_ptr->next_mol;
                        }
                        neighbor_ptr = neighbor_ptr->next_mol;
                    }
                }
            }
        } /* neighbor boxes for loops */
        curr_box = curr_box->next_box;
    } /* while curr_box */
    
    LPOTR = LPOTR/2.0;
    LPTRF = LPTRF/2.0;
    
    /* update shared sums from computed private sums */
    
    LOCK(gl->PotengSumLock);
    *POTA = *POTA + LPOTA;
    *POTR = *POTR + LPOTR;
    *PTRF = *PTRF + LPTRF;
    UNLOCK(gl->PotengSumLock);
    
} /* end of subroutine POTENG */
