void Valence_Angles( reax_system *system, control_params *control,
                     simulation_data *data, storage *workspace,
                     reax_list **lists, output_controls *out_control )
{
  int i, j, pi, k, pk, t;
  int type_i, type_j, type_k;
  int start_j, end_j, start_pk, end_pk;
  int cnt, num_thb_intrs;

  double temp, temp_bo_jt, pBOjt7;
  double p_val1, p_val2, p_val3, p_val4, p_val5;
  double p_val6, p_val7, p_val8, p_val9, p_val10;
  double p_pen1, p_pen2, p_pen3, p_pen4;
  double p_coa1, p_coa2, p_coa3, p_coa4;
  double trm8, expval6, expval7, expval2theta, expval12theta, exp3ij, exp3jk;
  double exp_pen2ij, exp_pen2jk, exp_pen3, exp_pen4, trm_pen34, exp_coa2;
  double dSBO1, dSBO2, SBO, SBO2, CSBO2, SBOp, prod_SBO, vlpadj;
  double CEval1, CEval2, CEval3, CEval4, CEval5, CEval6, CEval7, CEval8;
  double CEpen1, CEpen2, CEpen3;
  double e_ang, e_coa, e_pen;
  double CEcoa1, CEcoa2, CEcoa3, CEcoa4, CEcoa5;
  double Cf7ij, Cf7jk, Cf8j, Cf9j;
  double f7_ij, f7_jk, f8_Dj, f9_Dj;
  double Ctheta_0, theta_0, theta_00, theta, cos_theta, sin_theta;
  double BOA_ij, BOA_jk;
  rvec force, ext_press;

  // Tallying variables
  double eng_tmp, fi_tmp[3], fj_tmp[3], fk_tmp[3];
  double delij[3], delkj[3];

  three_body_header *thbh;
  three_body_parameters *thbp;
  three_body_interaction_data *p_ijk, *p_kji;
  bond_data *pbond_ij, *pbond_jk, *pbond_jt;
  bond_order_data *bo_ij, *bo_jk, *bo_jt;
  reax_list *bonds = (*lists) + BONDS;
  reax_list *thb_intrs =  (*lists) + THREE_BODIES;

  /* global parameters used in these calculations */
  p_val6 = system->reax_param.gp.l[14];
  p_val8 = system->reax_param.gp.l[33];
  p_val9 = system->reax_param.gp.l[16];
  p_val10 = system->reax_param.gp.l[17];
  num_thb_intrs = 0;


  for( j = 0; j < system->N; ++j ) {         // Ray: the first one with system->N
    type_j = system->my_atoms[j].type;
    if (type_j < 0) continue;
    start_j = Start_Index(j, bonds);
    end_j = End_Index(j, bonds);

    p_val3 = system->reax_param.sbp[ type_j ].p_val3;
    p_val5 = system->reax_param.sbp[ type_j ].p_val5;

    SBOp = 0, prod_SBO = 1;
    for( t = start_j; t < end_j; ++t ) {
      bo_jt = &(bonds->select.bond_list[t].bo_data);
      SBOp += (bo_jt->BO_pi + bo_jt->BO_pi2);
      temp = SQR( bo_jt->BO );
      temp *= temp;
      temp *= temp;
      prod_SBO *= exp( -temp );
    }

    if( workspace->vlpex[j] >= 0 ){
      vlpadj = 0;
      dSBO2 = prod_SBO - 1;
    }
    else{
      vlpadj = workspace->nlp[j];
      dSBO2 = (prod_SBO - 1) * (1 - p_val8 * workspace->dDelta_lp[j]);
    }

    SBO = SBOp + (1 - prod_SBO) * (-workspace->Delta_boc[j] - p_val8 * vlpadj);
    dSBO1 = -8 * prod_SBO * ( workspace->Delta_boc[j] + p_val8 * vlpadj );

    if( SBO <= 0 )
      SBO2 = 0, CSBO2 = 0;
    else if( SBO > 0 && SBO <= 1 ) {
        SBO2 = pow( SBO, p_val9 );
        CSBO2 = p_val9 * pow( SBO, p_val9 - 1 );
    }
    else if( SBO > 1 && SBO < 2 ) {
      SBO2 = 2 - pow( 2-SBO, p_val9 );
      CSBO2 = p_val9 * pow( 2 - SBO, p_val9 - 1 );
    }
    else
      SBO2 = 2, CSBO2 = 0;

    expval6 = exp( p_val6 * workspace->Delta_boc[j] );

    for( pi = start_j; pi < end_j; ++pi ) {
      Set_Start_Index( pi, num_thb_intrs, thb_intrs );
      pbond_ij = &(bonds->select.bond_list[pi]);
      bo_ij = &(pbond_ij->bo_data);
      BOA_ij = bo_ij->BO - control->thb_cut;


      if( BOA_ij/*bo_ij->BO*/ > 0.0 &&
          ( j < system->n || pbond_ij->nbr < system->n ) ) {
        i = pbond_ij->nbr;
        type_i = system->my_atoms[i].type;

        for( pk = start_j; pk < pi; ++pk ) {
          start_pk = Start_Index( pk, thb_intrs );
          end_pk = End_Index( pk, thb_intrs );

          for( t = start_pk; t < end_pk; ++t )
            if( thb_intrs->select.three_body_list[t].thb == i ) {
              p_ijk = &(thb_intrs->select.three_body_list[num_thb_intrs] );
              p_kji = &(thb_intrs->select.three_body_list[t]);

              p_ijk->thb = bonds->select.bond_list[pk].nbr;
              p_ijk->pthb  = pk;
              p_ijk->theta = p_kji->theta;
              rvec_Copy( p_ijk->dcos_di, p_kji->dcos_dk );
              rvec_Copy( p_ijk->dcos_dj, p_kji->dcos_dj );
              rvec_Copy( p_ijk->dcos_dk, p_kji->dcos_di );

              ++num_thb_intrs;
              break;
            }
        }

        for( pk = pi+1; pk < end_j; ++pk ) {
          pbond_jk = &(bonds->select.bond_list[pk]);
          bo_jk    = &(pbond_jk->bo_data);
          BOA_jk   = bo_jk->BO - control->thb_cut;
          k        = pbond_jk->nbr;
          type_k   = system->my_atoms[k].type;
          p_ijk    = &( thb_intrs->select.three_body_list[num_thb_intrs] );

          Calculate_Theta( pbond_ij->dvec, pbond_ij->d,
                           pbond_jk->dvec, pbond_jk->d,
                           &theta, &cos_theta );

          Calculate_dCos_Theta( pbond_ij->dvec, pbond_ij->d,
                                pbond_jk->dvec, pbond_jk->d,
                                &(p_ijk->dcos_di), &(p_ijk->dcos_dj),
                                &(p_ijk->dcos_dk) );
          p_ijk->thb = k;
          p_ijk->pthb = pk;
          p_ijk->theta = theta;

          sin_theta = sin( theta );
          if( sin_theta < 1.0e-5 )
            sin_theta = 1.0e-5;

          ++num_thb_intrs;


          if( (j < system->n) && (BOA_jk > 0.0) &&
              (bo_ij->BO > control->thb_cut) &&
              (bo_jk->BO > control->thb_cut) &&
              (bo_ij->BO * bo_jk->BO > control->thb_cutsq) ) {
            thbh = &( system->reax_param.thbp[ type_i ][ type_j ][ type_k ] );

            for( cnt = 0; cnt < thbh->cnt; ++cnt ) {
              if( fabs(thbh->prm[cnt].p_val1) > 0.001 ) {
                thbp = &( thbh->prm[cnt] );

                /* ANGLE ENERGY */
                p_val1 = thbp->p_val1;
                p_val2 = thbp->p_val2;
                p_val4 = thbp->p_val4;
                p_val7 = thbp->p_val7;
                theta_00 = thbp->theta_00;

                exp3ij = exp( -p_val3 * pow( BOA_ij, p_val4 ) );
                f7_ij = 1.0 - exp3ij;
                Cf7ij = p_val3 * p_val4 * pow( BOA_ij, p_val4 - 1.0 ) * exp3ij;

                exp3jk = exp( -p_val3 * pow( BOA_jk, p_val4 ) );
                f7_jk = 1.0 - exp3jk;
                Cf7jk = p_val3 * p_val4 * pow( BOA_jk, p_val4 - 1.0 ) * exp3jk;

                expval7 = exp( -p_val7 * workspace->Delta_boc[j] );
                trm8 = 1.0 + expval6 + expval7;
                f8_Dj = p_val5 - ( (p_val5 - 1.0) * (2.0 + expval6) / trm8 );
                Cf8j = ( (1.0 - p_val5) / SQR(trm8) ) *
                  ( p_val6 * expval6 * trm8 -
                    (2.0 + expval6) * ( p_val6*expval6 - p_val7*expval7 ) );

                theta_0 = 180.0 - theta_00 * (1.0 -
                                              exp(-p_val10 * (2.0 - SBO2)));
                theta_0 = DEG2RAD( theta_0 );

                expval2theta  = exp( -p_val2 * SQR(theta_0 - theta) );
                if( p_val1 >= 0 )
                  expval12theta = p_val1 * (1.0 - expval2theta);
                else // To avoid linear Me-H-Me angles (6/6/06)
                  expval12theta = p_val1 * -expval2theta;

                CEval1 = Cf7ij * f7_jk * f8_Dj * expval12theta;
                CEval2 = Cf7jk * f7_ij * f8_Dj * expval12theta;
                CEval3 = Cf8j  * f7_ij * f7_jk * expval12theta;
                CEval4 = -2.0 * p_val1 * p_val2 * f7_ij * f7_jk * f8_Dj *
                  expval2theta * (theta_0 - theta);

                Ctheta_0 = p_val10 * DEG2RAD(theta_00) *
                  exp( -p_val10 * (2.0 - SBO2) );

                CEval5 = -CEval4 * Ctheta_0 * CSBO2;
                CEval6 = CEval5 * dSBO1;
                CEval7 = CEval5 * dSBO2;
                CEval8 = -CEval4 / sin_theta;

                data->my_en.e_ang += e_ang =
                  f7_ij * f7_jk * f8_Dj * expval12theta;
                /* END ANGLE ENERGY*/

                /* PENALTY ENERGY */
                p_pen1 = thbp->p_pen1;
                p_pen2 = system->reax_param.gp.l[19];
                p_pen3 = system->reax_param.gp.l[20];
                p_pen4 = system->reax_param.gp.l[21];

                exp_pen2ij = exp( -p_pen2 * SQR( BOA_ij - 2.0 ) );
                exp_pen2jk = exp( -p_pen2 * SQR( BOA_jk - 2.0 ) );
                exp_pen3 = exp( -p_pen3 * workspace->Delta[j] );
                exp_pen4 = exp(  p_pen4 * workspace->Delta[j] );
                trm_pen34 = 1.0 + exp_pen3 + exp_pen4;
                f9_Dj = ( 2.0 + exp_pen3 ) / trm_pen34;
                Cf9j = ( -p_pen3 * exp_pen3 * trm_pen34 -
                         (2.0 + exp_pen3) * ( -p_pen3 * exp_pen3 +
                                              p_pen4 * exp_pen4 ) ) /
                  SQR( trm_pen34 );

                data->my_en.e_pen += e_pen =
                  p_pen1 * f9_Dj * exp_pen2ij * exp_pen2jk;

                CEpen1 = e_pen * Cf9j / f9_Dj;
                temp   = -2.0 * p_pen2 * e_pen;
                CEpen2 = temp * (BOA_ij - 2.0);
                CEpen3 = temp * (BOA_jk - 2.0);
                /* END PENALTY ENERGY */

                /* COALITION ENERGY */
                p_coa1 = thbp->p_coa1;
                p_coa2 = system->reax_param.gp.l[2];
                p_coa3 = system->reax_param.gp.l[38];
                p_coa4 = system->reax_param.gp.l[30];

                exp_coa2 = exp( p_coa2 * workspace->Delta_val[j] );
                data->my_en.e_coa += e_coa =
                  p_coa1 / (1. + exp_coa2) *
                  exp( -p_coa3 * SQR(workspace->total_bond_order[i]-BOA_ij) ) *
                  exp( -p_coa3 * SQR(workspace->total_bond_order[k]-BOA_jk) ) *
                  exp( -p_coa4 * SQR(BOA_ij - 1.5) ) *
                  exp( -p_coa4 * SQR(BOA_jk - 1.5) );

                CEcoa1 = -2 * p_coa4 * (BOA_ij - 1.5) * e_coa;
                CEcoa2 = -2 * p_coa4 * (BOA_jk - 1.5) * e_coa;
                CEcoa3 = -p_coa2 * exp_coa2 * e_coa / (1 + exp_coa2);
                CEcoa4 = -2 * p_coa3 *
                  (workspace->total_bond_order[i]-BOA_ij) * e_coa;
                CEcoa5 = -2 * p_coa3 *
                  (workspace->total_bond_order[k]-BOA_jk) * e_coa;
                /* END COALITION ENERGY */

                /* FORCES */
                bo_ij->Cdbo += (CEval1 + CEpen2 + (CEcoa1 - CEcoa4));
                bo_jk->Cdbo += (CEval2 + CEpen3 + (CEcoa2 - CEcoa5));
                workspace->CdDelta[j] += ((CEval3 + CEval7) + CEpen1 + CEcoa3);
                workspace->CdDelta[i] += CEcoa4;
                workspace->CdDelta[k] += CEcoa5;

                for( t = start_j; t < end_j; ++t ) {
                    pbond_jt = &( bonds->select.bond_list[t] );
                    bo_jt = &(pbond_jt->bo_data);
                    temp_bo_jt = bo_jt->BO;
                    temp = CUBE( temp_bo_jt );
                    pBOjt7 = temp * temp * temp_bo_jt;

                    bo_jt->Cdbo += (CEval6 * pBOjt7);
                    bo_jt->Cdbopi += CEval5;
                    bo_jt->Cdbopi2 += CEval5;
                }

                if( control->virial == 0 ) {
                  rvec_ScaledAdd( workspace->f[i], CEval8, p_ijk->dcos_di );
                  rvec_ScaledAdd( workspace->f[j], CEval8, p_ijk->dcos_dj );
                  rvec_ScaledAdd( workspace->f[k], CEval8, p_ijk->dcos_dk );
                }
                else {
                  rvec_Scale( force, CEval8, p_ijk->dcos_di );
                  rvec_Add( workspace->f[i], force );
                  rvec_iMultiply( ext_press, pbond_ij->rel_box, force );
                  rvec_Add( data->my_ext_press, ext_press );

                  rvec_ScaledAdd( workspace->f[j], CEval8, p_ijk->dcos_dj );

                  rvec_Scale( force, CEval8, p_ijk->dcos_dk );
                  rvec_Add( workspace->f[k], force );
                  rvec_iMultiply( ext_press, pbond_jk->rel_box, force );
                  rvec_Add( data->my_ext_press, ext_press );
                }

                /* tally into per-atom virials */
                if( system->pair_ptr->vflag_atom || system->pair_ptr->evflag) {

                  /* Acquire vectors */
                  rvec_ScaledSum( delij, 1., system->my_atoms[i].x,
                                        -1., system->my_atoms[j].x );
                  rvec_ScaledSum( delkj, 1., system->my_atoms[k].x,
                                        -1., system->my_atoms[j].x );

                  rvec_Scale( fi_tmp, -CEval8, p_ijk->dcos_di );
                  rvec_Scale( fj_tmp, -CEval8, p_ijk->dcos_dj );
                  rvec_Scale( fk_tmp, -CEval8, p_ijk->dcos_dk );

                  eng_tmp = e_ang + e_pen + e_coa;

                  if( system->pair_ptr->evflag)
                          system->pair_ptr->ev_tally(j,j,system->N,1,eng_tmp,0.0,0.0,0.0,0.0,0.0);
                  if( system->pair_ptr->vflag_atom)
                          system->pair_ptr->v_tally3(i,j,k,fi_tmp,fk_tmp,delij,delkj);
                }
              }
            }
          }
        }
      }

      Set_End_Index(pi, num_thb_intrs, thb_intrs );
    }
  }

  if( num_thb_intrs >= thb_intrs->num_intrs * DANGER_ZONE ) {
    workspace->realloc.num_3body = num_thb_intrs;
    if( num_thb_intrs > thb_intrs->num_intrs ) {
      fprintf( stderr, "step%d-ran out of space on angle_list: top=%d, max=%d",
               data->step, num_thb_intrs, thb_intrs->num_intrs );
      MPI_Abort( MPI_COMM_WORLD, INSUFFICIENT_MEMORY );
    }
  }

}
void Hydrogen_Bonds( reax_system *system, control_params *control,
                     simulation_data *data, storage *workspace,
                     reax_list **lists, output_controls *out_control )
{
  int  i, j, k, pi, pk;
  int  type_i, type_j, type_k;
  int  start_j, end_j, hb_start_j, hb_end_j;
  int  hblist[MAX_BONDS];
  int  itr, top;
  int  num_hb_intrs = 0;
  ivec rel_jk;
  real r_ij, r_jk, theta, cos_theta, sin_xhz4, cos_xhz1, sin_theta2;
  real e_hb, exp_hb2, exp_hb3, CEhb1, CEhb2, CEhb3;
  rvec dcos_theta_di, dcos_theta_dj, dcos_theta_dk;
  rvec dvec_jk, force, ext_press;
  // rtensor temp_rtensor, total_rtensor;
  hbond_parameters *hbp;
  bond_order_data *bo_ij;
  bond_data *pbond_ij;
  far_neighbor_data *nbr_jk;
  reax_list *bonds, *hbonds;
  bond_data *bond_list;
  hbond_data *hbond_list;

  // tally variables
  real fi_tmp[3], fk_tmp[3], delij[3], delkj[3];

  bonds = (*lists) + BONDS;
  bond_list = bonds->select.bond_list;
  hbonds = (*lists) + HBONDS;
  hbond_list = hbonds->select.hbond_list;

  /* loops below discover the Hydrogen bonds between i-j-k triplets.
     here j is H atom and there has to be some bond between i and j.
     Hydrogen bond is between j and k.
     so in this function i->X, j->H, k->Z when we map
     variables onto the ones in the handout.*/
  for( j = 0; j < system->n; ++j )
    /* j has to be of type H */
    if( system->reax_param.sbp[system->my_atoms[j].type].p_hbond == 1 ) {
      /*set j's variables */
      type_j     = system->my_atoms[j].type;
      start_j    = Start_Index(j, bonds);
      end_j      = End_Index(j, bonds);
      hb_start_j = Start_Index( system->my_atoms[j].Hindex, hbonds );
      hb_end_j   = End_Index( system->my_atoms[j].Hindex, hbonds );

      top = 0;
      for( pi = start_j; pi < end_j; ++pi )  {
        pbond_ij = &( bond_list[pi] );
        i = pbond_ij->nbr;
        bo_ij = &(pbond_ij->bo_data);
        type_i = system->my_atoms[i].type;

        if( system->reax_param.sbp[type_i].p_hbond == 2 &&
            bo_ij->BO >= HB_THRESHOLD )
          hblist[top++] = pi;
      }

      // fprintf( stderr, "j: %d, top: %d, hb_start_j: %d, hb_end_j:%d\n",
      //          j, top, hb_start_j, hb_end_j );

      for( pk = hb_start_j; pk < hb_end_j; ++pk ) {
        /* set k's varibles */
        k = hbond_list[pk].nbr;
        type_k = system->my_atoms[k].type;
        nbr_jk = hbond_list[pk].ptr;
        r_jk = nbr_jk->d;
        rvec_Scale( dvec_jk, hbond_list[pk].scl, nbr_jk->dvec );

        for( itr = 0; itr < top; ++itr ) {
          pi = hblist[itr];
          pbond_ij = &( bonds->select.bond_list[pi] );
          i = pbond_ij->nbr;

          if( system->my_atoms[i].orig_id != system->my_atoms[k].orig_id ) {
            bo_ij = &(pbond_ij->bo_data);
            type_i = system->my_atoms[i].type;
            r_ij = pbond_ij->d;
            hbp = &(system->reax_param.hbp[ type_i ][ type_j ][ type_k ]);
            ++num_hb_intrs;

            Calculate_Theta( pbond_ij->dvec, pbond_ij->d, dvec_jk, r_jk,
                             &theta, &cos_theta );
            /* the derivative of cos(theta) */
            Calculate_dCos_Theta( pbond_ij->dvec, pbond_ij->d, dvec_jk, r_jk,
                                  &dcos_theta_di, &dcos_theta_dj,
                                  &dcos_theta_dk );

            /* hyrogen bond energy*/
            sin_theta2 = sin( theta/2.0 );
            sin_xhz4 = SQR(sin_theta2);
            sin_xhz4 *= sin_xhz4;
            cos_xhz1 = ( 1.0 - cos_theta );
            exp_hb2 = exp( -hbp->p_hb2 * bo_ij->BO );
            exp_hb3 = exp( -hbp->p_hb3 * ( hbp->r0_hb / r_jk +
                                           r_jk / hbp->r0_hb - 2.0 ) );

            data->my_en.e_hb += e_hb =
              hbp->p_hb1 * (1.0 - exp_hb2) * exp_hb3 * sin_xhz4;

            CEhb1 = hbp->p_hb1 * hbp->p_hb2 * exp_hb2 * exp_hb3 * sin_xhz4;
            CEhb2 = -hbp->p_hb1/2.0 * (1.0 - exp_hb2) * exp_hb3 * cos_xhz1;
            CEhb3 = -hbp->p_hb3 *
              (-hbp->r0_hb / SQR(r_jk) + 1.0 / hbp->r0_hb) * e_hb;

            /*fprintf( stdout,
              "%6d%6d%6d%12.6f%12.6f%12.6f%12.6f%12.6f%12.6f%12.6f%12.6f%12.6f\n",
              system->my_atoms[i].orig_id, system->my_atoms[j].orig_id,
              system->my_atoms[k].orig_id,
              r_jk, theta, hbp->p_hb1, exp_hb2, hbp->p_hb3, hbp->r0_hb,
              exp_hb3, sin_xhz4, e_hb ); */

            /* hydrogen bond forces */
            bo_ij->Cdbo += CEhb1; // dbo term

            if( control->virial == 0 ) {
              // dcos terms
              rvec_ScaledAdd( workspace->f[i], +CEhb2, dcos_theta_di );
              rvec_ScaledAdd( workspace->f[j], +CEhb2, dcos_theta_dj );
              rvec_ScaledAdd( workspace->f[k], +CEhb2, dcos_theta_dk );
              // dr terms
              rvec_ScaledAdd( workspace->f[j], -CEhb3/r_jk, dvec_jk );
              rvec_ScaledAdd( workspace->f[k], +CEhb3/r_jk, dvec_jk );
            }
            else {
              /* for pressure coupling, terms that are not related to bond order
                 derivatives are added directly into pressure vector/tensor */
              rvec_Scale( force, +CEhb2, dcos_theta_di ); // dcos terms
              rvec_Add( workspace->f[i], force );
              rvec_iMultiply( ext_press, pbond_ij->rel_box, force );
              rvec_ScaledAdd( data->my_ext_press, 1.0, ext_press );

              rvec_ScaledAdd( workspace->f[j], +CEhb2, dcos_theta_dj );

              ivec_Scale( rel_jk, hbond_list[pk].scl, nbr_jk->rel_box );
              rvec_Scale( force, +CEhb2, dcos_theta_dk );
              rvec_Add( workspace->f[k], force );
              rvec_iMultiply( ext_press, rel_jk, force );
              rvec_ScaledAdd( data->my_ext_press, 1.0, ext_press );
              // dr terms
              rvec_ScaledAdd( workspace->f[j], -CEhb3/r_jk, dvec_jk );

              rvec_Scale( force, CEhb3/r_jk, dvec_jk );
              rvec_Add( workspace->f[k], force );
              rvec_iMultiply( ext_press, rel_jk, force );
              rvec_ScaledAdd( data->my_ext_press, 1.0, ext_press );
            }

            /* tally into per-atom virials */
            if (system->pair_ptr->vflag_atom || system->pair_ptr->evflag) {
              rvec_ScaledSum( delij, 1., system->my_atoms[i].x,
                                    -1., system->my_atoms[j].x );
              rvec_ScaledSum( delkj, 1., system->my_atoms[k].x,
                                     -1., system->my_atoms[j].x );

              rvec_Scale(fi_tmp, CEhb2, dcos_theta_di);
              rvec_Scale(fk_tmp, CEhb2, dcos_theta_dk);
              rvec_ScaledAdd(fk_tmp, CEhb3/r_jk, dvec_jk);

              system->pair_ptr->ev_tally3(i,j,k,e_hb,0.0,fi_tmp,fk_tmp,delij,delkj);
            }

#ifdef TEST_ENERGY
            /* fprintf( out_control->ehb,
               "%24.15e%24.15e%24.15e\n%24.15e%24.15e%24.15e\n%24.15e%24.15e%24.15e\n",
               dcos_theta_di[0], dcos_theta_di[1], dcos_theta_di[2],
               dcos_theta_dj[0], dcos_theta_dj[1], dcos_theta_dj[2],
               dcos_theta_dk[0], dcos_theta_dk[1], dcos_theta_dk[2]);
               fprintf( out_control->ehb, "%24.15e%24.15e%24.15e\n",
               CEhb1, CEhb2, CEhb3 ); */
            fprintf( out_control->ehb,
                     //"%6d%6d%6d%24.15e%24.15e%24.15e%24.15e%24.15e\n",
                     "%6d%6d%6d%12.4f%12.4f%12.4f%12.4f%12.4f\n",
                     system->my_atoms[i].orig_id, system->my_atoms[j].orig_id,
                     system->my_atoms[k].orig_id,
                     r_jk, theta, bo_ij->BO, e_hb, data->my_en.e_hb );
#endif
#ifdef TEST_FORCES
            Add_dBO( system, lists, j, pi, +CEhb1, workspace->f_hb ); //dbo term
            // dcos terms
            rvec_ScaledAdd( workspace->f_hb[i], +CEhb2, dcos_theta_di );
            rvec_ScaledAdd( workspace->f_hb[j], +CEhb2, dcos_theta_dj );
            rvec_ScaledAdd( workspace->f_hb[k], +CEhb2, dcos_theta_dk );
            // dr terms
            rvec_ScaledAdd( workspace->f_hb[j], -CEhb3/r_jk, dvec_jk );
            rvec_ScaledAdd( workspace->f_hb[k], +CEhb3/r_jk, dvec_jk );
#endif
          }
        }
      }
    }

#if defined(DEBUG)
  fprintf( stderr, "Number of hydrogen bonds: %d\n", num_hb_intrs );
  fprintf( stderr, "Hydrogen Bond Energy: %g\n", data->my_en.e_hb );
  fprintf( stderr, "hydbonds: ext_press (%24.15e %24.15e %24.15e)\n",
           data->ext_press[0], data->ext_press[1], data->ext_press[2] );
#endif
}
void Hydrogen_Bonds( reax_system *system, control_params *control,
                     simulation_data *data, storage *workspace,
                     reax_list **lists, output_controls *out_control )
{
  int  i, j, k, pi, pk;
  int  type_i, type_j, type_k;
  int  start_j, end_j, hb_start_j, hb_end_j;
  int  hblist[MAX_BONDS];
  int  itr, top;
  int  num_hb_intrs = 0;
  ivec rel_jk;
  real r_jk, theta, cos_theta, sin_xhz4, cos_xhz1, sin_theta2;
  real e_hb, exp_hb2, exp_hb3, CEhb1, CEhb2, CEhb3;
  rvec dcos_theta_di, dcos_theta_dj, dcos_theta_dk;
  rvec dvec_jk, force, ext_press;
  hbond_parameters *hbp;
  bond_order_data *bo_ij;
  bond_data *pbond_ij;
  far_neighbor_data *nbr_jk;
  reax_list *bonds, *hbonds;
  bond_data *bond_list;
  hbond_data *hbond_list;

  // tally variables
  real fi_tmp[3], fk_tmp[3], delij[3], delkj[3];

  bonds = (*lists) + BONDS;
  bond_list = bonds->select.bond_list;
  hbonds = (*lists) + HBONDS;
  hbond_list = hbonds->select.hbond_list;

  for( j = 0; j < system->n; ++j )
    if( system->reax_param.sbp[system->my_atoms[j].type].p_hbond == 1 ) {
      type_j     = system->my_atoms[j].type;
      start_j    = Start_Index(j, bonds);
      end_j      = End_Index(j, bonds);
      hb_start_j = Start_Index( system->my_atoms[j].Hindex, hbonds );
      hb_end_j   = End_Index( system->my_atoms[j].Hindex, hbonds );
      if (type_j < 0) continue;

      top = 0;
      for( pi = start_j; pi < end_j; ++pi )  {
        pbond_ij = &( bond_list[pi] );
        i = pbond_ij->nbr;
        type_i = system->my_atoms[i].type;
	if (type_i < 0) continue;
        bo_ij = &(pbond_ij->bo_data);

        if( system->reax_param.sbp[type_i].p_hbond == 2 &&
            bo_ij->BO >= HB_THRESHOLD )
          hblist[top++] = pi;
      }

      for( pk = hb_start_j; pk < hb_end_j; ++pk ) {
        /* set k's varibles */
        k = hbond_list[pk].nbr;
        type_k = system->my_atoms[k].type;
	if (type_k < 0) continue;
        nbr_jk = hbond_list[pk].ptr;
        r_jk = nbr_jk->d;
        rvec_Scale( dvec_jk, hbond_list[pk].scl, nbr_jk->dvec );

        for( itr = 0; itr < top; ++itr ) {
          pi = hblist[itr];
          pbond_ij = &( bonds->select.bond_list[pi] );
          i = pbond_ij->nbr;

          if( system->my_atoms[i].orig_id != system->my_atoms[k].orig_id ) {
            bo_ij = &(pbond_ij->bo_data);
            type_i = system->my_atoms[i].type;
	    if (type_i < 0) continue;
            hbp = &(system->reax_param.hbp[ type_i ][ type_j ][ type_k ]);
            ++num_hb_intrs;

            Calculate_Theta( pbond_ij->dvec, pbond_ij->d, dvec_jk, r_jk,
                             &theta, &cos_theta );
            /* the derivative of cos(theta) */
            Calculate_dCos_Theta( pbond_ij->dvec, pbond_ij->d, dvec_jk, r_jk,
                                  &dcos_theta_di, &dcos_theta_dj,
                                  &dcos_theta_dk );

            /* hyrogen bond energy*/
            sin_theta2 = sin( theta/2.0 );
            sin_xhz4 = SQR(sin_theta2);
            sin_xhz4 *= sin_xhz4;
            cos_xhz1 = ( 1.0 - cos_theta );
            exp_hb2 = exp( -hbp->p_hb2 * bo_ij->BO );
            exp_hb3 = exp( -hbp->p_hb3 * ( hbp->r0_hb / r_jk +
                                           r_jk / hbp->r0_hb - 2.0 ) );

            data->my_en.e_hb += e_hb =
              hbp->p_hb1 * (1.0 - exp_hb2) * exp_hb3 * sin_xhz4;

            CEhb1 = hbp->p_hb1 * hbp->p_hb2 * exp_hb2 * exp_hb3 * sin_xhz4;
            CEhb2 = -hbp->p_hb1/2.0 * (1.0 - exp_hb2) * exp_hb3 * cos_xhz1;
            CEhb3 = -hbp->p_hb3 *
              (-hbp->r0_hb / SQR(r_jk) + 1.0 / hbp->r0_hb) * e_hb;

            /* hydrogen bond forces */
            bo_ij->Cdbo += CEhb1; // dbo term

            if( control->virial == 0 ) {
              // dcos terms
              rvec_ScaledAdd( workspace->f[i], +CEhb2, dcos_theta_di );
              rvec_ScaledAdd( workspace->f[j], +CEhb2, dcos_theta_dj );
              rvec_ScaledAdd( workspace->f[k], +CEhb2, dcos_theta_dk );
              // dr terms
              rvec_ScaledAdd( workspace->f[j], -CEhb3/r_jk, dvec_jk );
              rvec_ScaledAdd( workspace->f[k], +CEhb3/r_jk, dvec_jk );
            }
            else {
              rvec_Scale( force, +CEhb2, dcos_theta_di ); // dcos terms
              rvec_Add( workspace->f[i], force );
              rvec_iMultiply( ext_press, pbond_ij->rel_box, force );
              rvec_ScaledAdd( data->my_ext_press, 1.0, ext_press );

              rvec_ScaledAdd( workspace->f[j], +CEhb2, dcos_theta_dj );

              ivec_Scale( rel_jk, hbond_list[pk].scl, nbr_jk->rel_box );
              rvec_Scale( force, +CEhb2, dcos_theta_dk );
              rvec_Add( workspace->f[k], force );
              rvec_iMultiply( ext_press, rel_jk, force );
              rvec_ScaledAdd( data->my_ext_press, 1.0, ext_press );
              // dr terms
              rvec_ScaledAdd( workspace->f[j], -CEhb3/r_jk, dvec_jk );

              rvec_Scale( force, CEhb3/r_jk, dvec_jk );
              rvec_Add( workspace->f[k], force );
              rvec_iMultiply( ext_press, rel_jk, force );
              rvec_ScaledAdd( data->my_ext_press, 1.0, ext_press );
            }

            /* tally into per-atom virials */
            if (system->pair_ptr->vflag_atom || system->pair_ptr->evflag) {
              rvec_ScaledSum( delij, 1., system->my_atoms[j].x,
                                    -1., system->my_atoms[i].x );
              rvec_ScaledSum( delkj, 1., system->my_atoms[j].x,
                                     -1., system->my_atoms[k].x );

              rvec_Scale(fi_tmp, CEhb2, dcos_theta_di);
              rvec_Scale(fk_tmp, CEhb2, dcos_theta_dk);
              rvec_ScaledAdd(fk_tmp, CEhb3/r_jk, dvec_jk);

              system->pair_ptr->ev_tally3(i,j,k,e_hb,0.0,fi_tmp,fk_tmp,delij,delkj);
            }
          }
        }
      }
    }
}