Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
bond_orders.c 43.33 KiB
/*----------------------------------------------------------------------
  PuReMD - Purdue ReaxFF Molecular Dynamics Program

  Copyright (2010) Purdue University
  Hasan Metin Aktulga, haktulga@cs.purdue.edu
  Joseph Fogarty, jcfogart@mail.usf.edu
  Sagar Pandit, pandit@usf.edu
  Ananth Y Grama, ayg@cs.purdue.edu

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License as
  published by the Free Software Foundation; either version 2 of
  the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU General Public License for more details:
  <http://www.gnu.org/licenses/>.
  ----------------------------------------------------------------------*/

#if (defined(HAVE_CONFIG_H) && !defined(__CONFIG_H_))
  #define __CONFIG_H_
  #include "../../common/include/config.h"
#endif

#if defined(PURE_REAX)
  #include "bond_orders.h"

  #include "list.h"
  #include "vector.h"
#elif defined(LAMMPS_REAX)
  #include "reax_bond_orders.h"

  #include "reax_list.h"
  #include "reax_vector.h"
#endif

#include "index_utils.h"


static inline real Cf45( real p1, real p2 )
{
    return  -EXP(-p2 / 2.0) /
        ( SQR( EXP(-p1 / 2.0) + EXP(p1 / 2.0) ) * (EXP(-p2 / 2.0) + EXP(p2 / 2.0)) );
}


#if defined(TEST_FORCES)
void Add_dBO( reax_system *system, reax_list **lists,
        int i, int pj, real C, rvec *v )
{
    int start_pj, end_pj, k;
    reax_list *bonds, *dBOs;

    bonds = lists[BONDS];
    dBOs = lists[DBO];
    pj = bonds->bond_list[pj].dbond_index;
    start_pj = Start_Index( pj, dBOs );
    end_pj = End_Index( pj, dBOs );

#if defined(DEBUG_FOCUS)
    fprintf( stderr, "[Add_dBO] i: %d, j: %d, start: %d, end: %d\n", i, pj, start_pj, end_pj );
#endif

    for ( k = start_pj; k < end_pj; ++k )
    {
        rvec_ScaledAdd( v[dBOs->dbo_list[k].wrt],
                C, dBOs->dbo_list[k].dBO );
    }
}


void Add_dBOpinpi2( reax_system *system, reax_list **lists,
        int i, int pj, real Cpi, real Cpi2, rvec *vpi, rvec *vpi2 )
{
    int start_pj, end_pj, k;
    reax_list *bonds, *dBOs;
    dbond_data *dbo_k;

    bonds = lists[BONDS];
    dBOs = lists[DBO];
    pj = bonds->bond_list[pj].dbond_index;
    start_pj = Start_Index( pj, dBOs );
    end_pj = End_Index( pj, dBOs );

#if defined(DEBUG_FOCUS)
    fprintf( stderr, "[Add_dBOpinpi2] i: %d, j: %d, start: %d, end: %d\n", i, pj, start_pj, end_pj );
#endif

    for ( k = start_pj; k < end_pj; ++k )
    {
        dbo_k = &dBOs->dbo_list[k];
        rvec_ScaledAdd( vpi[dbo_k->wrt], Cpi, dbo_k->dBOpi );
        rvec_ScaledAdd( vpi2[dbo_k->wrt], Cpi2, dbo_k->dBOpi2 );
    }
}


void Add_dBO_to_Forces( reax_system *system, reax_list **lists,
                        int i, int pj, real C )
{
    reax_list *bonds;
    reax_list *dBOs;
    int start_pj, end_pj, k;

    bonds = lists[BONDS];
    dBOs = lists[DBO];
    pj = bonds->bond_list[pj].dbond_index;
    start_pj = Start_Index( pj, dBOs );
    end_pj = End_Index( pj, dBOs );

    for ( k = start_pj; k < end_pj; ++k )
    {
        rvec_ScaledAdd( system->atoms[dBOs->dbo_list[k].wrt].f,
                C, dBOs->dbo_list[k].dBO );
    }
}


void Add_dBOpinpi2_to_Forces( reax_system *system, reax_list **lists,
        int i, int pj, real Cpi, real Cpi2 )
{
    reax_list *bonds;
    reax_list *dBOs;
    dbond_data *dbo_k;
    int start_pj, end_pj, k;

    bonds = lists[BONDS];
    dBOs = lists[DBO];
    pj = bonds->bond_list[pj].dbond_index;
    start_pj = Start_Index(pj, dBOs);
    end_pj = End_Index(pj, dBOs);

    for ( k = start_pj; k < end_pj; ++k )
    {
        dbo_k = &dBOs->dbo_list[k];
        rvec_ScaledAdd( system->atoms[dbo_k->wrt].f, Cpi, dbo_k->dBOpi );
        rvec_ScaledAdd( system->atoms[dbo_k->wrt].f, Cpi2, dbo_k->dBOpi2 );
    }
}


void Add_dDelta( reax_system *system, reax_list **lists, int i, real C, rvec *v )
{
    int start, end, k;
    reax_list *dDeltas;

    dDeltas = lists[DDELTA];
    start = Start_Index( i, dDeltas );
    end = End_Index( i, dDeltas );

#if defined(DEBUG_FOCUS)
    fprintf( stderr, "[Add_dDelta] i: %d, start: %d, end: %d\n", i, start, end );
#endif

    for ( k = start; k < end; ++k )
    {
        rvec_ScaledAdd( v[dDeltas->dDelta_list[k].wrt],
                C, dDeltas->dDelta_list[k].dVal );
    }
}


void Add_dDelta_to_Forces( reax_system *system, reax_list **lists, int i, real C )
{
    reax_list *dDeltas;
    int start, end, k;

    dDeltas = lists[DDELTA];
    start = Start_Index( i, dDeltas );
    end = End_Index( i, dDeltas );

    for ( k = start; k < end; ++k )
    {
        rvec_ScaledAdd( system->atoms[dDeltas->dDelta_list[k].wrt].f,
                C, dDeltas->dDelta_list[k].dVal );
    }
}


void Calculate_dBO( int i, int pj, static_storage *workspace, reax_list **lists,
        int *top )
{
    int j, k, l, start_i, end_i, end_j;
    reax_list *bonds, *dBOs;
    bond_data *nbr_l, *nbr_k;
    bond_order_data *bo_ij;
    dbond_data *top_dbo;

    bonds = lists[BONDS];
    dBOs = lists[DBO];
    j = bonds->bond_list[pj].nbr;
    bo_ij = &bonds->bond_list[pj].bo_data;
    start_i = Start_Index( i, bonds );
    end_i = End_Index( i, bonds );
    l = Start_Index( j, bonds );
    end_j = End_Index( j, bonds );
    top_dbo = &dBOs->dbo_list[ *top ];

//    fprintf( stderr, "[Calculate_dBO] i: %d, pj: %d, start_i: %d, end_i: %d, start_j: %d, end_j: %d\n", i, pj, start_i, end_i, l, end_j );

    for ( k = start_i; k < end_i; ++k )
    {
        nbr_k = &bonds->bond_list[k];

        for ( ; l < end_j && bonds->bond_list[l].nbr < nbr_k->nbr; ++l )
        {
            /* These are the neighbors of j which aren't in the neighbor_list of i
             * Note that they might also include i! */
            nbr_l = &bonds->bond_list[l];
            top_dbo->wrt = nbr_l->nbr;

            rvec_Scale( top_dbo->dBO, -bo_ij->C3dbo, nbr_l->bo_data.dBOp );
            rvec_Scale( top_dbo->dBOpi, -bo_ij->C4dbopi, nbr_l->bo_data.dBOp );
            rvec_Scale( top_dbo->dBOpi2, -bo_ij->C4dbopi2, nbr_l->bo_data.dBOp );

            if ( nbr_l->nbr == i )
            {
                /* dBO */
                rvec_ScaledAdd( top_dbo->dBO, bo_ij->C1dbo, bo_ij->dBOp );
                rvec_ScaledAdd( top_dbo->dBO, bo_ij->C2dbo, workspace->dDeltap_self[i] );

                /* dBOpi */
                rvec_ScaledAdd( top_dbo->dBOpi, bo_ij->C1dbopi, bo_ij->dln_BOp_pi );
                rvec_ScaledAdd( top_dbo->dBOpi, bo_ij->C2dbopi, bo_ij->dBOp );
                rvec_ScaledAdd( top_dbo->dBOpi, bo_ij->C3dbopi, workspace->dDeltap_self[i] );

                /* dBOpp */
                rvec_ScaledAdd( top_dbo->dBOpi2, bo_ij->C1dbopi2, bo_ij->dln_BOp_pi2 );
                rvec_ScaledAdd( top_dbo->dBOpi2, bo_ij->C2dbopi2, bo_ij->dBOp );
                rvec_ScaledAdd( top_dbo->dBOpi2, bo_ij->C3dbopi2, workspace->dDeltap_self[i] );
            }

            ++(*top);
            ++top_dbo;
        }

        /* Now we are processing neighbor k of i. */
        top_dbo->wrt = nbr_k->nbr;

        rvec_Scale( top_dbo->dBO, -bo_ij->C2dbo, nbr_k->bo_data.dBOp );      //dBO-2
        rvec_Scale( top_dbo->dBOpi, -bo_ij->C3dbopi, nbr_k->bo_data.dBOp );  //dBOpi-3
        rvec_Scale( top_dbo->dBOpi2, -bo_ij->C3dbopi2, nbr_k->bo_data.dBOp );//dBOpp-3

        if ( l < end_j && bonds->bond_list[l].nbr == nbr_k->nbr )
        {
            /* common neighbor of i and j */
            nbr_l = &bonds->bond_list[l];
            rvec_Copy( dBOp, nbr_l->bo_data.dBOp );

            rvec_ScaledAdd( top_dbo->dBO, -bo_ij->C3dbo, nbr_l->bo_data.dBOp );      //dBO,3rd
            rvec_ScaledAdd( top_dbo->dBOpi, -bo_ij->C4dbopi, nbr_l->bo_data.dBOp );  //dBOpi,4th
            rvec_ScaledAdd( top_dbo->dBOpi2, -bo_ij->C4dbopi2, nbr_l->bo_data.dBOp );//dBOpp.4th
            ++l;
        }
        else if ( k == pj )
        {
            /* 1st, dBO */
            rvec_ScaledAdd( top_dbo->dBO, -bo_ij->C1dbo, bo_ij->dBOp );
            /* 3rd, dBO */
            rvec_ScaledAdd( top_dbo->dBO, bo_ij->C3dbo, workspace->dDeltap_self[j] );

            /* dBOpi, 1st */
            rvec_ScaledAdd( top_dbo->dBOpi, -bo_ij->C1dbopi, bo_ij->dln_BOp_pi );
            /* 2nd */
            rvec_ScaledAdd( top_dbo->dBOpi, -bo_ij->C2dbopi, bo_ij->dBOp );
            /* 4th */
            rvec_ScaledAdd( top_dbo->dBOpi, bo_ij->C4dbopi, workspace->dDeltap_self[j] );

            /* dBOpi2, 1st */
            rvec_ScaledAdd( top_dbo->dBOpi2, -bo_ij->C1dbopi2, bo_ij->dln_BOp_pi2 );
            /* 2nd */
            rvec_ScaledAdd( top_dbo->dBOpi2, -bo_ij->C2dbopi2, bo_ij->dBOp );
            /* 4th */
            rvec_ScaledAdd( top_dbo->dBOpi2, bo_ij->C4dbopi2, workspace->dDeltap_self[j] );
        }

        ++(*top);
        ++top_dbo;
    }

    for ( ; l < end_j; ++l )
    {
        /* These are the remaining neighbors of j which are not in the
           neighbor_list of i. Note that they might also include i!*/
        nbr_l = &bonds->bond_list[l];
        top_dbo->wrt = nbr_l->nbr;
        rvec_Copy( dBOp, nbr_l->bo_data.dBOp );

        rvec_Scale( top_dbo->dBO, -bo_ij->C3dbo, nbr_l->bo_data.dBOp );      //3rd, dBO
        rvec_Scale( top_dbo->dBOpi, -bo_ij->C4dbopi, nbr_l->bo_data.dBOp );  //4th, dBOpi
        rvec_Scale( top_dbo->dBOpi2, -bo_ij->C4dbopi2, nbr_l->bo_data.dBOp );//4th, dBOpp

        if ( nbr_l->nbr == i )
        {
            /* dBO, 1st */
            rvec_ScaledAdd( top_dbo->dBO, bo_ij->C1dbo, bo_ij->dBOp );
            rvec_ScaledAdd( top_dbo->dBO, bo_ij->C2dbo, workspace->dDeltap_self[i] ); //2nd, dBO

            /* dBOpi, 1st */
            rvec_ScaledAdd( top_dbo->dBOpi, bo_ij->C1dbopi, bo_ij->dln_BOp_pi );
            rvec_ScaledAdd( top_dbo->dBOpi, bo_ij->C2dbopi, bo_ij->dBOp );  //2nd
            rvec_ScaledAdd( top_dbo->dBOpi, bo_ij->C3dbopi, workspace->dDeltap_self[i] ); //3rd

            /* dBOpipi, 1st */
            rvec_ScaledAdd(top_dbo->dBOpi2, bo_ij->C1dbopi2, bo_ij->dln_BOp_pi2);
            rvec_ScaledAdd( top_dbo->dBOpi2, bo_ij->C2dbopi2, bo_ij->dBOp ); //2nd
            rvec_ScaledAdd( top_dbo->dBOpi2, bo_ij->C3dbopi2, workspace->dDeltap_self[i] );//3rd
        }

        ++(*top);
        ++top_dbo;
    }
}
#endif


void Add_dBond_to_Forces_NPT( int i, int pj, reax_system *system,
        simulation_data *data, storage *workspace, reax_list **lists )
{
    reax_list *bonds;
    bond_data *nbr_j, *nbr_k;
    bond_order_data *bo_ij, *bo_ji;
    dbond_coefficients coef;
    rvec temp, ext_press;
    ivec rel_box;
    int pk, k, j;

    bonds = lists[BONDS];
    nbr_j = &bonds->bond_list[pj];
    j = nbr_j->nbr;
    bo_ij = &nbr_j->bo_data;
    bo_ji = &bonds->bond_list[ nbr_j->sym_index ].bo_data;

    coef.C1dbo = bo_ij->C1dbo * (bo_ij->Cdbo + bo_ji->Cdbo);
    coef.C2dbo = bo_ij->C2dbo * (bo_ij->Cdbo + bo_ji->Cdbo);
    coef.C3dbo = bo_ij->C3dbo * (bo_ij->Cdbo + bo_ji->Cdbo);

    coef.C1dbopi = bo_ij->C1dbopi * (bo_ij->Cdbopi + bo_ji->Cdbopi);
    coef.C2dbopi = bo_ij->C2dbopi * (bo_ij->Cdbopi + bo_ji->Cdbopi);
    coef.C3dbopi = bo_ij->C3dbopi * (bo_ij->Cdbopi + bo_ji->Cdbopi);
    coef.C4dbopi = bo_ij->C4dbopi * (bo_ij->Cdbopi + bo_ji->Cdbopi);

    coef.C1dbopi2 = bo_ij->C1dbopi2 * (bo_ij->Cdbopi2 + bo_ji->Cdbopi2);
    coef.C2dbopi2 = bo_ij->C2dbopi2 * (bo_ij->Cdbopi2 + bo_ji->Cdbopi2);
    coef.C3dbopi2 = bo_ij->C3dbopi2 * (bo_ij->Cdbopi2 + bo_ji->Cdbopi2);
    coef.C4dbopi2 = bo_ij->C4dbopi2 * (bo_ij->Cdbopi2 + bo_ji->Cdbopi2);

    coef.C1dDelta = bo_ij->C1dbo * (workspace->CdDelta[i] + workspace->CdDelta[j]);
    coef.C2dDelta = bo_ij->C2dbo * (workspace->CdDelta[i] + workspace->CdDelta[j]);
    coef.C3dDelta = bo_ij->C3dbo * (workspace->CdDelta[i] + workspace->CdDelta[j]);

    /************************************
    * forces related to atom i          *
    * first neighbors of atom i         *
    ************************************/
    for ( pk = Start_Index(i, bonds); pk < End_Index(i, bonds); ++pk )
    {
        nbr_k = &bonds->bond_list[pk];
        k = nbr_k->nbr;

        /* 2nd, dBO */
        rvec_Scale( temp, -coef.C2dbo, nbr_k->bo_data.dBOp );
        /* dDelta */
        rvec_ScaledAdd( temp, -coef.C2dDelta, nbr_k->bo_data.dBOp );
        /* 3rd, dBOpi */
        rvec_ScaledAdd( temp, -coef.C3dbopi, nbr_k->bo_data.dBOp );
        /* 3rd, dBOpi2 */
        rvec_ScaledAdd( temp, -coef.C3dbopi2, nbr_k->bo_data.dBOp );

        /* force */
        rvec_Add( workspace->f[k], temp );
        /* pressure */
        rvec_iMultiply( ext_press, nbr_k->rel_box, temp );
        rvec_Add( data->my_ext_press, ext_press );
    }

    /* then atom i itself */
    /* 1st, dBO */
    rvec_Scale( temp, coef.C1dbo, bo_ij->dBOp );
    /* 2nd, dBO */
    rvec_ScaledAdd( temp, coef.C2dbo, workspace->dDeltap_self[i] );

    /* 1st, dBO */
    rvec_ScaledAdd( temp, coef.C1dDelta, bo_ij->dBOp );
    /* 2nd, dBO */
    rvec_ScaledAdd( temp, coef.C2dDelta, workspace->dDeltap_self[i] );

    /* 1st, dBOpi */
    rvec_ScaledAdd( temp, coef.C1dbopi, bo_ij->dln_BOp_pi );
    /* 2nd, dBOpi */
    rvec_ScaledAdd( temp, coef.C2dbopi, bo_ij->dBOp );
    /* 3rd, dBOpi */
    rvec_ScaledAdd( temp, coef.C3dbopi, workspace->dDeltap_self[i] );

    /* 1st, dBO_pi2 */
    rvec_ScaledAdd( temp, coef.C1dbopi2, bo_ij->dln_BOp_pi2 );
    /* 2nd, dBO_pi2 */
    rvec_ScaledAdd( temp, coef.C2dbopi2, bo_ij->dBOp );
    /* 3rd, dBO_pi2 */
    rvec_ScaledAdd( temp, coef.C3dbopi2, workspace->dDeltap_self[i] );

    /* force */
    rvec_Add( workspace->f[i], temp );
    /* ext pressure due to i is dropped, counting force on j will be enough */

    /******************************************************
     * forces and pressure related to atom j               *
     * first neighbors of atom j                           *
     ******************************************************/
    for ( pk = Start_Index(j, bonds); pk < End_Index(j, bonds); ++pk )
    {
        nbr_k = &bonds->bond_list[pk];
        k = nbr_k->nbr;

        /* 3rd, dBO */
        rvec_Scale( temp, -coef.C3dbo, nbr_k->bo_data.dBOp );
        /* dDelta */
        rvec_ScaledAdd( temp, -coef.C3dDelta, nbr_k->bo_data.dBOp );
        /* 4th, dBOpi */
        rvec_ScaledAdd( temp, -coef.C4dbopi, nbr_k->bo_data.dBOp );
        /* 4th, dBOpi2 */
        rvec_ScaledAdd( temp, -coef.C4dbopi2, nbr_k->bo_data.dBOp );

        /* force */
        rvec_Add( workspace->f[k], temp );
        /* pressure */
        if ( k != i )
        {
            ivec_Sum( rel_box, nbr_k->rel_box, nbr_j->rel_box ); //rel_box(k, i)
            rvec_iMultiply( ext_press, rel_box, temp );
            rvec_Add( data->my_ext_press, ext_press );
        }
    }

    /* then atom j itself */
    /* 1st, dBO */
    rvec_Scale( temp, -coef.C1dbo, bo_ij->dBOp );
    /* 2nd, dBO */
    rvec_ScaledAdd( temp, coef.C3dbo, workspace->dDeltap_self[j] );

    /* 1st, dBO */
    rvec_ScaledAdd( temp, -coef.C1dDelta, bo_ij->dBOp );
    /* 2nd, dBO */
    rvec_ScaledAdd( temp, coef.C3dDelta, workspace->dDeltap_self[j] );

    /* 1st, dBOpi */
    rvec_ScaledAdd( temp, -coef.C1dbopi, bo_ij->dln_BOp_pi );
    /* 2nd, dBOpi */
    rvec_ScaledAdd( temp, -coef.C2dbopi, bo_ij->dBOp );
    /* 3rd, dBOpi */
    rvec_ScaledAdd( temp, coef.C4dbopi, workspace->dDeltap_self[j] );

    /* 1st, dBOpi2 */
    rvec_ScaledAdd( temp, -coef.C1dbopi2, bo_ij->dln_BOp_pi2 );
    /* 2nd, dBOpi2 */
    rvec_ScaledAdd( temp, -coef.C2dbopi2, bo_ij->dBOp );
    /* 3rd, dBOpi2 */
    rvec_ScaledAdd( temp, coef.C4dbopi2, workspace->dDeltap_self[j] );

    /* force */
    rvec_Add( workspace->f[j], temp );
    /* pressure */
    rvec_iMultiply( ext_press, nbr_j->rel_box, temp );
    rvec_Add( data->my_ext_press, ext_press );
}



void Add_dBond_to_Forces( int i, int pj, reax_system *system,
        simulation_data *data, storage *workspace, reax_list **lists )
{
    reax_list *bonds;
    bond_data *nbr_j, *nbr_k;
    bond_order_data *bo_ij, *bo_ji;
    dbond_coefficients coef;
    int pk, k, j;

    bonds = lists[BONDS];
    nbr_j = &bonds->bond_list[pj];
    j = nbr_j->nbr;
    bo_ij = &nbr_j->bo_data;
    bo_ji = &bonds->bond_list[ nbr_j->sym_index ].bo_data;

    coef.C1dbo = bo_ij->C1dbo * (bo_ij->Cdbo + bo_ji->Cdbo);
    coef.C2dbo = bo_ij->C2dbo * (bo_ij->Cdbo + bo_ji->Cdbo);
    coef.C3dbo = bo_ij->C3dbo * (bo_ij->Cdbo + bo_ji->Cdbo);

    coef.C1dbopi = bo_ij->C1dbopi * (bo_ij->Cdbopi + bo_ji->Cdbopi);
    coef.C2dbopi = bo_ij->C2dbopi * (bo_ij->Cdbopi + bo_ji->Cdbopi);
    coef.C3dbopi = bo_ij->C3dbopi * (bo_ij->Cdbopi + bo_ji->Cdbopi);
    coef.C4dbopi = bo_ij->C4dbopi * (bo_ij->Cdbopi + bo_ji->Cdbopi);

    coef.C1dbopi2 = bo_ij->C1dbopi2 * (bo_ij->Cdbopi2 + bo_ji->Cdbopi2);
    coef.C2dbopi2 = bo_ij->C2dbopi2 * (bo_ij->Cdbopi2 + bo_ji->Cdbopi2);
    coef.C3dbopi2 = bo_ij->C3dbopi2 * (bo_ij->Cdbopi2 + bo_ji->Cdbopi2);
    coef.C4dbopi2 = bo_ij->C4dbopi2 * (bo_ij->Cdbopi2 + bo_ji->Cdbopi2);

    coef.C1dDelta = bo_ij->C1dbo * (workspace->CdDelta[i] + workspace->CdDelta[j]);
    coef.C2dDelta = bo_ij->C2dbo * (workspace->CdDelta[i] + workspace->CdDelta[j]);
    coef.C3dDelta = bo_ij->C3dbo * (workspace->CdDelta[i] + workspace->CdDelta[j]);

    for ( pk = Start_Index(i, bonds); pk < End_Index(i, bonds); ++pk )
    {
        nbr_k = &bonds->bond_list[pk];
        k = nbr_k->nbr;

        /* 2nd, dBO */
        rvec_ScaledAdd( workspace->f[k], -coef.C2dbo, nbr_k->bo_data.dBOp );
        /* dDelta */
        rvec_ScaledAdd( workspace->f[k], -coef.C2dDelta, nbr_k->bo_data.dBOp );
        /* 3rd, dBOpi */
        rvec_ScaledAdd( workspace->f[k], -coef.C3dbopi, nbr_k->bo_data.dBOp );
        /* 3rd, dBOpi2 */
        rvec_ScaledAdd( workspace->f[k], -coef.C3dbopi2, nbr_k->bo_data.dBOp );
    }

    /* 1st, dBO */
    rvec_ScaledAdd( workspace->f[i], coef.C1dbo, bo_ij->dBOp );
    /* 2nd, dBO */
    rvec_ScaledAdd( workspace->f[i], coef.C2dbo, workspace->dDeltap_self[i] );

    /* 1st, dBO */
    rvec_ScaledAdd( workspace->f[i], coef.C1dDelta, bo_ij->dBOp );
    /* 2nd, dBO */
    rvec_ScaledAdd( workspace->f[i], coef.C2dDelta, workspace->dDeltap_self[i] );

    /* 1st, dBOpi */
    rvec_ScaledAdd( workspace->f[i], coef.C1dbopi, bo_ij->dln_BOp_pi );
    /* 2nd, dBOpi */
    rvec_ScaledAdd( workspace->f[i], coef.C2dbopi, bo_ij->dBOp );
    /* 3rd, dBOpi */
    rvec_ScaledAdd( workspace->f[i], coef.C3dbopi, workspace->dDeltap_self[i] );

    /* 1st, dBO_pi2 */
    rvec_ScaledAdd( workspace->f[i], coef.C1dbopi2, bo_ij->dln_BOp_pi2 );
    /* 2nd, dBO_pi2 */
    rvec_ScaledAdd( workspace->f[i], coef.C2dbopi2, bo_ij->dBOp );
    /* 3rd, dBO_pi2 */
    rvec_ScaledAdd( workspace->f[i], coef.C3dbopi2, workspace->dDeltap_self[i] );

    for ( pk = Start_Index(j, bonds); pk < End_Index(j, bonds); ++pk )
    {
        nbr_k = &bonds->bond_list[pk];
        k = nbr_k->nbr;

        /* 3rd, dBO */
        rvec_ScaledAdd( workspace->f[k], -coef.C3dbo, nbr_k->bo_data.dBOp );
        /* dDelta */
        rvec_ScaledAdd( workspace->f[k], -coef.C3dDelta, nbr_k->bo_data.dBOp );
        /* 4th, dBOpi */
        rvec_ScaledAdd( workspace->f[k], -coef.C4dbopi, nbr_k->bo_data.dBOp );
        /* 4th, dBOpi2 */
        rvec_ScaledAdd( workspace->f[k], -coef.C4dbopi2, nbr_k->bo_data.dBOp );
    }

    /* 1st, dBO */
    rvec_ScaledAdd( workspace->f[j], -coef.C1dbo, bo_ij->dBOp );
    /* 2nd, dBO */
    rvec_ScaledAdd( workspace->f[j], coef.C3dbo, workspace->dDeltap_self[j] );

    /* 1st, dBO */
    rvec_ScaledAdd( workspace->f[j], -coef.C1dDelta, bo_ij->dBOp );
    /* 2nd, dBO */
    rvec_ScaledAdd( workspace->f[j], coef.C3dDelta, workspace->dDeltap_self[j] );

    /* 1st, dBOpi */
    rvec_ScaledAdd( workspace->f[j], -coef.C1dbopi, bo_ij->dln_BOp_pi );
    /* 2nd, dBOpi */
    rvec_ScaledAdd( workspace->f[j], -coef.C2dbopi, bo_ij->dBOp );
    /* 3rd, dBOpi */
    rvec_ScaledAdd( workspace->f[j], coef.C4dbopi, workspace->dDeltap_self[j] );

    /* 1st, dBOpi2 */
    rvec_ScaledAdd( workspace->f[j], -coef.C1dbopi2, bo_ij->dln_BOp_pi2 );
    /* 2nd, dBOpi2 */
    rvec_ScaledAdd( workspace->f[j], -coef.C2dbopi2, bo_ij->dBOp );
    /* 3rd, dBOpi2 */
    rvec_ScaledAdd( workspace->f[j], coef.C4dbopi2, workspace->dDeltap_self[j] );
}


static inline void Copy_Bond_Order_Data( bond_order_data *dest, bond_order_data *src )
{
    dest->BO = src->BO;
    dest->BO_s = src->BO_s;
    dest->BO_pi = src->BO_pi;
    dest->BO_pi2 = src->BO_pi2;

    rvec_Scale( dest->dBOp, -1.0, src->dBOp );
    rvec_Scale( dest->dln_BOp_s, -1.0, src->dln_BOp_s );
    rvec_Scale( dest->dln_BOp_pi, -1.0, src->dln_BOp_pi );
    rvec_Scale( dest->dln_BOp_pi2, -1.0, src->dln_BOp_pi2 );
}


/* Compute the bond order term between atoms i and j,
 * and if this term exceeds the cutoff bo_cut, then adds
 * BOTH atoms the bonds list (i.e., compute term once
 * and copy to avoid redundant computation) */
int BOp( storage * const workspace, reax_list * const bonds, real bo_cut,
         int i, int btop_i, int j, ivec * const rel_box, real d, rvec * const dvec,
         int far_nbr_list_format, single_body_parameters const * const sbp_i,
         single_body_parameters const * const sbp_j, two_body_parameters const * const twbp )
{
    real r2, C12, C34, C56;
    real Cln_BOp_s, Cln_BOp_pi, Cln_BOp_pi2;
    real BO, BO_s, BO_pi, BO_pi2;
    bond_data *ibond, *jbond;
    bond_order_data *bo_ij, *bo_ji;
    int btop_j;

    r2 = SQR( d );

    if ( sbp_i->r_s > 0.0 && sbp_j->r_s > 0.0 )
    {
        C12 = twbp->p_bo1 * POW( d / twbp->r_s, twbp->p_bo2 );
        BO_s = (1.0 + bo_cut) * exp( C12 );
    }
    else
    {
        C12 = 0.0;
        BO_s = 0.0;
    }

    if ( sbp_i->r_pi > 0.0 && sbp_j->r_pi > 0.0 )
    {
        C34 = twbp->p_bo3 * POW( d / twbp->r_p, twbp->p_bo4 );
        BO_pi = exp( C34 );
    }
    else
    {
        C34 = 0.0;
        BO_pi = 0.0;
    }

    if ( sbp_i->r_pi_pi > 0.0 && sbp_j->r_pi_pi > 0.0 )
    {
        C56 = twbp->p_bo5 * POW( d / twbp->r_pp, twbp->p_bo6 );
        BO_pi2 = exp( C56 );
    }
    else
    {
        C56 = 0.0;
        BO_pi2 = 0.0;
    }

    /* Initially BO values are the uncorrected ones, page 1 */
    BO = BO_s + BO_pi + BO_pi2;

    if ( BO >= bo_cut )
    {
        /****** bonds i-j and j-i ******/
        ibond = &bonds->bond_list[btop_i];
        btop_j = End_Index( j, bonds );
        jbond = &bonds->bond_list[btop_j];

        ibond->nbr = j;
        ibond->d = d;
        rvec_Copy( ibond->dvec, *dvec );
        ivec_Copy( ibond->rel_box, *rel_box );
        ibond->dbond_index = btop_i;
        ibond->sym_index = btop_j;
        jbond->nbr = i;
        jbond->d = d;
        rvec_Scale( jbond->dvec, -1.0, *dvec );
        ivec_Scale( jbond->rel_box, -1.0, *rel_box );
        jbond->dbond_index = btop_i;
        jbond->sym_index = btop_i;

        Set_End_Index( j, btop_j + 1, bonds );
        
        bo_ij = &ibond->bo_data;
        bo_ij->BO = BO;
        bo_ij->BO_s = BO_s;
        bo_ij->BO_pi = BO_pi;
        bo_ij->BO_pi2 = BO_pi2;
        bo_ji = &jbond->bo_data;
        bo_ji->BO = BO;
        bo_ji->BO_s = BO_s;
        bo_ji->BO_pi = BO_pi;
        bo_ji->BO_pi2 = BO_pi2;

        /* Bond Order page2-3, derivative of total bond order prime */
        Cln_BOp_s = twbp->p_bo2 * C12 / r2;
        Cln_BOp_pi = twbp->p_bo4 * C34 / r2;
        Cln_BOp_pi2 = twbp->p_bo6 * C56 / r2;

        /* Only dln_BOp_xx wrt. dr_i is stored here, note that
         * dln_BOp_xx/dr_i = -dln_BOp_xx/dr_j and all others are 0 */
        rvec_Scale( bo_ij->dln_BOp_s, -1.0 * bo_ij->BO_s * Cln_BOp_s, ibond->dvec );
        rvec_Scale( bo_ij->dln_BOp_pi, -1.0 * bo_ij->BO_pi * Cln_BOp_pi, ibond->dvec );
        rvec_Scale( bo_ij->dln_BOp_pi2, -1.0 * bo_ij->BO_pi2 * Cln_BOp_pi2, ibond->dvec );
        rvec_Scale( bo_ji->dln_BOp_s, -1.0, bo_ij->dln_BOp_s );
        rvec_Scale( bo_ji->dln_BOp_pi, -1.0, bo_ij->dln_BOp_pi );
        rvec_Scale( bo_ji->dln_BOp_pi2, -1.0, bo_ij->dln_BOp_pi2 );

        /* Only dBOp wrt. dr_i is stored here, note that
         * dBOp/dr_i = -dBOp/dr_j and all others are 0 */
        rvec_Scale( bo_ij->dBOp, -1.0 * (bo_ij->BO_s * Cln_BOp_s 
                    + bo_ij->BO_pi * Cln_BOp_pi 
                    + bo_ij->BO_pi2 * Cln_BOp_pi2), ibond->dvec );
        rvec_Scale( bo_ji->dBOp, -1.0, bo_ij->dBOp );

        rvec_Add( workspace->dDeltap_self[i], bo_ij->dBOp );
        rvec_Add( workspace->dDeltap_self[j], bo_ji->dBOp );

        bo_ij->BO_s -= bo_cut;
        bo_ij->BO -= bo_cut;
        /* currently total_BOp */
        workspace->total_bond_order[i] += bo_ij->BO;
        bo_ij->Cdbo = 0.0;
        bo_ij->Cdbopi = 0.0;
        bo_ij->Cdbopi2 = 0.0;
        bo_ji->BO_s -= bo_cut;
        bo_ji->BO -= bo_cut;
        workspace->total_bond_order[j] += bo_ji->BO; //currently total_BOp
        bo_ji->Cdbo = 0.0;
        bo_ji->Cdbopi = 0.0;
        bo_ji->Cdbopi2 = 0.0;

        return TRUE;
    }

    return FALSE;
}


/* Compute the bond order term between atoms i and j,
 * and if this term exceeds the cutoff bo_cut, then adds
 * to the bond list according to the following convention:
 *   * if the far neighbor list is stored in half format,
 *      add BOTH atoms to each other's portion of the bond list
 *   * if the far neighbor list is stored in full format,
 *      add atom i to atom j's bonds list ONLY */
int BOp_redundant( storage *workspace, reax_list *bonds, real bo_cut,
         int i, int btop_i, int j, ivec *rel_box, real d, rvec *dvec,
         int far_nbr_list_format, single_body_parameters *sbp_i,
         single_body_parameters *sbp_j, two_body_parameters *twbp )
{
    real r2, C12, C34, C56;
    real Cln_BOp_s, Cln_BOp_pi, Cln_BOp_pi2;
    real BO, BO_s, BO_pi, BO_pi2;
    bond_data *ibond;
    bond_order_data *bo_ij;
    int btop_j;
    bond_data *jbond;
    bond_order_data *bo_ji;

    r2 = SQR(d);

    if ( sbp_i->r_s > 0.0 && sbp_j->r_s > 0.0 )
    {
        C12 = twbp->p_bo1 * pow( d / twbp->r_s, twbp->p_bo2 );
        BO_s = (1.0 + bo_cut) * exp( C12 );
    }
    else
    {
        C12 = 0.0;
        BO_s = 0.0;
    }

    if ( sbp_i->r_pi > 0.0 && sbp_j->r_pi > 0.0 )
    {
        C34 = twbp->p_bo3 * pow( d / twbp->r_p, twbp->p_bo4 );
        BO_pi = exp( C34 );
    }
    else
    {
        C34 = 0.0;
        BO_pi = 0.0;
    }

    if ( sbp_i->r_pi_pi > 0.0 && sbp_j->r_pi_pi > 0.0 )
    {
        C56 = twbp->p_bo5 * pow( d / twbp->r_pp, twbp->p_bo6 );
        BO_pi2 = exp( C56 );
    }
    else
    {
        C56 = 0.0;
        BO_pi2 = 0.0;
    }

    /* Initially BO values are the uncorrected ones, page 1 */
    BO = BO_s + BO_pi + BO_pi2;

    if ( BO >= bo_cut )
    {
        /****** bonds i-j and j-i ******/
        ibond = &bonds->bond_list[btop_i];
        if ( far_nbr_list_format == HALF_LIST )
        {
            btop_j = End_Index( j, bonds );
            jbond = &bonds->bond_list[btop_j];
        }

        ibond->nbr = j;
        ibond->d = d;
        rvec_Copy( ibond->dvec, *dvec );
        ivec_Copy( ibond->rel_box, *rel_box );
        ibond->dbond_index = btop_i;
        if ( far_nbr_list_format == HALF_LIST )
        {
            ibond->sym_index = btop_j;
            jbond->nbr = i;
            jbond->d = d;
            rvec_Scale( jbond->dvec, -1.0, *dvec );
            ivec_Scale( jbond->rel_box, -1.0, *rel_box );
            jbond->dbond_index = btop_i;
            jbond->sym_index = btop_i;

            Set_End_Index( j, btop_j + 1, bonds );
        }
        
        bo_ij = &ibond->bo_data;
        bo_ij->BO = BO;
        bo_ij->BO_s = BO_s;
        bo_ij->BO_pi = BO_pi;
        bo_ij->BO_pi2 = BO_pi2;
        if ( far_nbr_list_format == HALF_LIST )
        {
            bo_ji = &jbond->bo_data;
            bo_ji->BO = BO;
            bo_ji->BO_s = BO_s;
            bo_ji->BO_pi = BO_pi;
            bo_ji->BO_pi2 = BO_pi2;
        }

        /* Bond Order page2-3, derivative of total bond order prime */
        Cln_BOp_s = twbp->p_bo2 * C12 / r2;
        Cln_BOp_pi = twbp->p_bo4 * C34 / r2;
        Cln_BOp_pi2 = twbp->p_bo6 * C56 / r2;

        /* Only dln_BOp_xx wrt. dr_i is stored here, note that
         * dln_BOp_xx/dr_i = -dln_BOp_xx/dr_j and all others are 0 */
        rvec_Scale( bo_ij->dln_BOp_s, -1.0 * bo_ij->BO_s * Cln_BOp_s, ibond->dvec );
        rvec_Scale( bo_ij->dln_BOp_pi, -1.0 * bo_ij->BO_pi * Cln_BOp_pi, ibond->dvec );
        rvec_Scale( bo_ij->dln_BOp_pi2, -1.0 * bo_ij->BO_pi2 * Cln_BOp_pi2, ibond->dvec );
        if ( far_nbr_list_format == HALF_LIST )
        {
            rvec_Scale( bo_ji->dln_BOp_s, -1.0, bo_ij->dln_BOp_s );
            rvec_Scale( bo_ji->dln_BOp_pi, -1.0, bo_ij->dln_BOp_pi );
            rvec_Scale( bo_ji->dln_BOp_pi2, -1.0, bo_ij->dln_BOp_pi2 );
        }

        /* Only dBOp wrt. dr_i is stored here, note that
         * dBOp/dr_i = -dBOp/dr_j and all others are 0 */
        rvec_Scale( bo_ij->dBOp, -1.0 * (bo_ij->BO_s * Cln_BOp_s 
                    + bo_ij->BO_pi * Cln_BOp_pi 
                    + bo_ij->BO_pi2 * Cln_BOp_pi2), ibond->dvec );
        if ( far_nbr_list_format == HALF_LIST )
        {
            rvec_Scale( bo_ji->dBOp, -1.0, bo_ij->dBOp );
        }

        rvec_Add( workspace->dDeltap_self[i], bo_ij->dBOp );
        if ( far_nbr_list_format == HALF_LIST )
        {
            rvec_Add( workspace->dDeltap_self[j], bo_ji->dBOp );
        }

        bo_ij->BO_s -= bo_cut;
        bo_ij->BO -= bo_cut;
        workspace->total_bond_order[i] += bo_ij->BO; //currently total_BOp
        bo_ij->Cdbo = 0.0;
        bo_ij->Cdbopi = 0.0;
        bo_ij->Cdbopi2 = 0.0;
        if ( far_nbr_list_format == HALF_LIST )
        {
            bo_ji->BO_s -= bo_cut;
            bo_ji->BO -= bo_cut;
            workspace->total_bond_order[j] += bo_ji->BO; //currently total_BOp
            bo_ji->Cdbo = 0.0;
            bo_ji->Cdbopi = 0.0;
            bo_ji->Cdbopi2 = 0.0;
        }

        return TRUE;
    }

    return FALSE;
}


/* A very important and crucial assumption here is that each segment
 * belonging to a different atom in nbrhoods->nbr_list is sorted in its own.
 * This can either be done in the general coordinator function or here */
void BO( reax_system *system, control_params *control,
        simulation_data *data, storage *workspace,
        reax_list **lists, output_controls *out_control )
{
    int i, j, pj, type_i, type_j;
    int start_i, end_i;
    int sym_index;
    real val_i, Deltap_i, Deltap_boc_i;
    real val_j, Deltap_j, Deltap_boc_j;
    real f1, f2, f3, f4, f5, f4f5, exp_f4, exp_f5;
    real exp_p1i, exp_p2i, exp_p1j, exp_p2j;
    real temp, u1_ij, u1_ji, Cf1A_ij, Cf1B_ij, Cf1_ij, Cf1_ji;
    real Cf45_ij, Cf45_ji, p_lp1;
    real A0_ij, A1_ij, A2_ij, A2_ji, A3_ij, A3_ji;
    real explp1, p_boc1, p_boc2;
    single_body_parameters *sbp_i, *sbp_j;
    two_body_parameters *twbp;
    bond_order_data *bo_ij, *bo_ji;
    reax_list *bond_list;
#if defined(TEST_FORCES)
    int k, pk, start_j, end_j;
    int top_dbo, top_dDelta;
    dbond_data *pdbo;
    dDelta_data *ptop_dDelta;
    reax_list *dDeltas, *dBOs;
#endif

    p_lp1 = system->reax_param.gp.l[15];
    p_boc1 = system->reax_param.gp.l[0];
    p_boc2 = system->reax_param.gp.l[1];
    bond_list = lists[BONDS];
#if defined(TEST_FORCES)
    top_dbo = 0;
    top_dDelta = 0;
    dDeltas = lists[DDELTAS];
    dBOs = lists[DBOS];
#endif

    /* Calculate Deltaprime, Deltaprime_boc values */
    for ( i = 0; i < system->N; ++i )
    {
        type_i = system->my_atoms[i].type;
        sbp_i = &system->reax_param.sbp[type_i];
        workspace->Deltap[i] = workspace->total_bond_order[i] - sbp_i->valency;
        workspace->Deltap_boc[i] =
            workspace->total_bond_order[i] - sbp_i->valency_val;
        workspace->total_bond_order[i] = 0.0;
    }

    /* Corrected Bond Order calculations */
    for ( i = 0; i < system->N; ++i )
    {
        type_i = system->my_atoms[i].type;
        sbp_i = &system->reax_param.sbp[type_i];
        val_i = sbp_i->valency;
        Deltap_i = workspace->Deltap[i];
        Deltap_boc_i = workspace->Deltap_boc[i];
        start_i = Start_Index( i, bond_list );
        end_i = End_Index( i, bond_list );

        for ( pj = start_i; pj < end_i; ++pj )
        {
            j = bond_list->bond_list[pj].nbr;
            type_j = system->my_atoms[j].type;
            bo_ij = &bond_list->bond_list[pj].bo_data;

            if ( i < j || workspace->bond_mark[j] > 3 )
            {
                twbp = &system->reax_param.tbp[
                    index_tbp(type_i, type_j, system->reax_param.num_atom_types) ];

#if defined(TEST_FORCES)
                Set_Start_Index( pj, top_dbo, dBOs );
#endif

                if ( twbp->ovc < 0.001 && twbp->v13cor < 0.001 )
                {
                    /* There is no correction to bond orders nor to derivatives of
                     * bond order prime! So we leave bond orders unchanged and
                     * set derivative of bond order coefficients s.t.
                     * dBO = dBOp & dBOxx = dBOxxp in Add_dBO_to_Forces */
                    bo_ij->C1dbo = 1.0;
                    bo_ij->C2dbo = 0.0;
                    bo_ij->C3dbo = 0.0;

                    bo_ij->C1dbopi = 1.0;
                    bo_ij->C2dbopi = 0.0;
                    bo_ij->C3dbopi = 0.0;
                    bo_ij->C4dbopi = 0.0;

                    bo_ij->C1dbopi2 = 1.0;
                    bo_ij->C2dbopi2 = 0.0;
                    bo_ij->C3dbopi2 = 0.0;
                    bo_ij->C4dbopi2 = 0.0;

#if defined(TEST_FORCES)
                    pdbo = &dBOs->dbo_list[ top_dbo ];

                    /* compute dBO_ij/dr_i */
                    pdbo->wrt = i;
                    rvec_Copy( pdbo->dBO, bo_ij->dBOp );
                    rvec_Scale( pdbo->dBOpi, bo_ij->BO_pi, bo_ij->dln_BOp_pi );
                    rvec_Scale( pdbo->dBOpi2, bo_ij->BO_pi2, bo_ij->dln_BOp_pi2);

                    /* compute dBO_ij/dr_j */
                    pdbo = &dBOs->dbo_list[ top_dbo + 1 ];
                    pdbo->wrt = j;
                    rvec_Scale( pdbo->dBO, -1.0, bo_ij->dBOp );
                    rvec_Scale( pdbo->dBOpi, -bo_ij->BO_pi, bo_ij->dln_BOp_pi );
                    rvec_Scale(pdbo->dBOpi2, -bo_ij->BO_pi2, bo_ij->dln_BOp_pi2);

                    top_dbo += 2;
#endif
                }
                else
                {
                    val_j = system->reax_param.sbp[type_j].valency;
                    Deltap_j = workspace->Deltap[j];
                    Deltap_boc_j = workspace->Deltap_boc[j];

                    /* on page 1 */
                    if ( twbp->ovc >= 0.001 )
                    {
                        /* Correction for overcoordination */
                        exp_p1i = EXP( -p_boc1 * Deltap_i );
                        exp_p2i = EXP( -p_boc2 * Deltap_i );
                        exp_p1j = EXP( -p_boc1 * Deltap_j );
                        exp_p2j = EXP( -p_boc2 * Deltap_j );

                        f2 = exp_p1i + exp_p1j;
                        f3 = -1.0 / p_boc2 * LOG( 0.5 * ( exp_p2i  + exp_p2j ) );
                        f1 = 0.5 * ( ( val_i + f2 ) / ( val_i + f2 + f3 )
                                + ( val_j + f2 ) / ( val_j + f2 + f3 ) );

                        /* Now come the derivates */
                        /* Bond Order pages 5-7, derivative of f1 */
                        temp = f2 + f3;
                        u1_ij = val_i + temp;
                        u1_ji = val_j + temp;
                        Cf1A_ij = 0.5 * f3 * (1.0 / SQR( u1_ij ) + 1.0 / SQR( u1_ji ));
                        Cf1B_ij = -0.5 * (( u1_ij - f3 ) / SQR( u1_ij )
                                + ( u1_ji - f3 ) / SQR( u1_ji ));

                        //Cf1_ij = -Cf1A_ij * p_boc1 * exp_p1i +
                        //          Cf1B_ij * exp_p2i / ( exp_p2i + exp_p2j );
                        Cf1_ij = 0.50 * ( -p_boc1 * exp_p1i / u1_ij -
                                ((val_i + f2) / SQR(u1_ij)) * ( -p_boc1 * exp_p1i +
                                exp_p2i / ( exp_p2i + exp_p2j ) ) + -p_boc1 * exp_p1i / u1_ji -
                                ((val_j + f2) / SQR(u1_ji)) * ( -p_boc1 * exp_p1i +
                                exp_p2i / ( exp_p2i + exp_p2j ) ));

                        Cf1_ji = -Cf1A_ij * p_boc1 * exp_p1j
                            + Cf1B_ij * exp_p2j / ( exp_p2i + exp_p2j );
                    }
                    else
                    {
                        /* No overcoordination correction! */
                        f1 = 1.0;
                        Cf1_ij = 0.0;
                        Cf1_ji = 0.0;
                    }

                    if ( twbp->v13cor >= 0.001 )
                    {
                        /* Correction for 1-3 bond orders */
                        exp_f4 = EXP( -twbp->p_boc3 * (twbp->p_boc4 * SQR( bo_ij->BO ) - Deltap_boc_i)
                                + twbp->p_boc5 );
                        exp_f5 = EXP( -twbp->p_boc3 * (twbp->p_boc4 * SQR( bo_ij->BO ) - Deltap_boc_j)
                                + twbp->p_boc5 );

                        f4 = 1.0 / (1.0 + exp_f4);
                        f5 = 1.0 / (1.0 + exp_f5);
                        f4f5 = f4 * f5;

                        /* Bond Order pages 8-9, derivative of f4 and f5 */
//                        temp = twbp->p_boc5
//                            - twbp->p_boc3 * twbp->p_boc4 * SQR( bo_ij->BO );
//                        u_ij = temp + twbp->p_boc3 * Deltap_boc_i;
//                        u_ji = temp + twbp->p_boc3 * Deltap_boc_j;
//                        Cf45_ij = Cf45( u_ij, u_ji ) / f4f5;
//                        Cf45_ji = Cf45( u_ji, u_ij ) / f4f5;
                        Cf45_ij = -f4 * exp_f4;
                        Cf45_ji = -f5 * exp_f5;
                    }
                    else
                    {
                        f4 = 1.0;
                        f5 = 1.0;
                        f4f5 = 1.0;
                        Cf45_ij = 0.0;
                        Cf45_ji = 0.0;
                    }

                    /* Bond Order page 10, derivative of total bond order */
                    A0_ij = f1 * f4f5;
                    A1_ij = -2.0 * twbp->p_boc3 * twbp->p_boc4 * bo_ij->BO
                        * (Cf45_ij + Cf45_ji);
                    A2_ij = Cf1_ij / f1 + twbp->p_boc3 * Cf45_ij;
                    A2_ji = Cf1_ji / f1 + twbp->p_boc3 * Cf45_ji;
                    A3_ij = A2_ij + Cf1_ij / f1;
                    A3_ji = A2_ji + Cf1_ji / f1;

                    /* find corrected bond order values and their deriv coefs */
                    bo_ij->BO = bo_ij->BO * A0_ij;
                    bo_ij->BO_pi = bo_ij->BO_pi * A0_ij * f1;
                    bo_ij->BO_pi2 = bo_ij->BO_pi2 * A0_ij * f1;
                    bo_ij->BO_s = bo_ij->BO - ( bo_ij->BO_pi + bo_ij->BO_pi2 );

                    bo_ij->C1dbo = A0_ij + bo_ij->BO * A1_ij;
                    bo_ij->C2dbo = bo_ij->BO * A2_ij;
                    bo_ij->C3dbo = bo_ij->BO * A2_ji;

                    bo_ij->C1dbopi = f1 * f1 * f4 * f5;
                    bo_ij->C2dbopi = bo_ij->BO_pi * A1_ij;
                    bo_ij->C3dbopi = bo_ij->BO_pi * A3_ij;
                    bo_ij->C4dbopi = bo_ij->BO_pi * A3_ji;

                    bo_ij->C1dbopi2 = f1 * f1 * f4 * f5;
                    bo_ij->C2dbopi2 = bo_ij->BO_pi2 * A1_ij;
                    bo_ij->C3dbopi2 = bo_ij->BO_pi2 * A3_ij;
                    bo_ij->C4dbopi2 = bo_ij->BO_pi2 * A3_ji;

#if defined(TEST_FORCES)
                    Calculate_dBO( i, pj, workspace, lists, &top_dbo );
#endif
                }

                /* neglect weak bonds */
                if ( bo_ij->BO < 1.0e-10 )
                {
                    bo_ij->BO = 0.0;
                }
                if ( bo_ij->BO_s < 1.0e-10 )
                {
                    bo_ij->BO_s = 0.0;
                }
                if ( bo_ij->BO_pi < 1.0e-10 )
                {
                    bo_ij->BO_pi = 0.0;
                }
                if ( bo_ij->BO_pi2 < 1.0e-10 )
                {
                    bo_ij->BO_pi2 = 0.0;
                }

                /* now keeps total_BO */
                workspace->total_bond_order[i] += bo_ij->BO;

#if defined(TEST_FORCES)
                Set_End_Index( pj, top_dbo, dBOs );
                Add_dBO( system, lists, i, pj, 1.0, workspace->dDelta );
#endif
            }
            else
            {
                /* We only need to update bond orders from bo_ji
                 * everything else is set in uncorrected_bo calculations */
                sym_index = bond_list->bond_list[pj].sym_index;

                bo_ji = &bond_list->bond_list[ sym_index ].bo_data;
                bo_ij->BO = bo_ji->BO;
                bo_ij->BO_s = bo_ji->BO_s;
                bo_ij->BO_pi = bo_ji->BO_pi;
                bo_ij->BO_pi2 = bo_ji->BO_pi2;

                /* now keeps total_BO */
                workspace->total_bond_order[i] += bo_ij->BO;

#if defined(TEST_FORCES)
                Add_dBO( system, lists, j, sym_index, 1.0, workspace->dDelta );
#endif
            }
        }

#if defined(TEST_FORCES)
        Set_Start_Index( i, top_dDelta, dDeltas );
        ptop_dDelta = &dDeltas->dDelta_list[top_dDelta];

        for ( pj = start_i; pj < end_i; ++pj )
        {
            j = bond_list->bond_list[pj].nbr;

            if ( !rvec_isZero( workspace->dDelta[j] ) )
            {
                ptop_dDelta->wrt = j;
                rvec_Copy( ptop_dDelta->dVal, workspace->dDelta[j] );
                rvec_MakeZero( workspace->dDelta[j] );
                ++top_dDelta;
                ++ptop_dDelta;
            }

            start_j = Start_Index( j, bond_list );
            end_j = End_Index( j, bond_list );
            for ( pk = start_j; pk < end_j; ++pk )
            {
                k = bond_list->bond_list[pk].nbr;
                if ( !rvec_isZero( workspace->dDelta[k] ) )
                {
                    ptop_dDelta->wrt = k;
                    rvec_Copy( ptop_dDelta->dVal, workspace->dDelta[k] );
                    rvec_MakeZero( workspace->dDelta[k] );
                    ++top_dDelta;
                    ++ptop_dDelta;
                }
            }
        }

        Set_End_Index( i, top_dDelta, dDeltas );
#endif
    }

    /* Calculate some helper variables that are  used at many places
     * throughout force calculations */
    for ( i = 0; i < system->N; ++i )
    {
        type_j = system->my_atoms[i].type;
        sbp_j = &system->reax_param.sbp[ type_j ];

        workspace->Delta[i] = workspace->total_bond_order[i] - sbp_j->valency;
        workspace->Delta_e[i] = workspace->total_bond_order[i] - sbp_j->valency_e;
        workspace->Delta_boc[i] = workspace->total_bond_order[i]
            - sbp_j->valency_boc;

        workspace->vlpex[i] = workspace->Delta_e[i]
            - 2.0 * (int)(workspace->Delta_e[i] / 2.0);
        explp1 = EXP(-p_lp1 * SQR(2.0 + workspace->vlpex[i]));
        workspace->nlp[i] = explp1 - (int)(workspace->Delta_e[i] / 2.0);
        workspace->Delta_lp[i] = sbp_j->nlp_opt - workspace->nlp[i];
        workspace->Clp[i] = 2.0 * p_lp1 * explp1 * (2.0 + workspace->vlpex[i]);
        /* Adri uses different dDelta_lp values than the ones in notes... */
        workspace->dDelta_lp[i] = workspace->Clp[i];
//        workspace->dDelta_lp[i] = workspace->Clp[i] + (0.5 - workspace->Clp[i])
//            * ((FABS(workspace->Delta_e[i] / 2.0
//                            - (int)(workspace->Delta_e[i] / 2.0)) < 0.1) ? 1 : 0 );

        if ( sbp_j->mass > 21.0 )
        {
            workspace->nlp_temp[i] = 0.5 * (sbp_j->valency_e - sbp_j->valency);
            workspace->Delta_lp_temp[i] = sbp_j->nlp_opt - workspace->nlp_temp[i];
            workspace->dDelta_lp_temp[i] = 0.0;
        }
        else
        {
            workspace->nlp_temp[i] = workspace->nlp[i];
            workspace->Delta_lp_temp[i] = sbp_j->nlp_opt - workspace->nlp_temp[i];
            workspace->dDelta_lp_temp[i] = workspace->Clp[i];
        }
    }

#if defined(TEST_ENERGIES) || defined(TEST_FORCES)
    Print_Bond_List( system, control, data, lists, out_control);
#endif
}