-
Kurt A. O'Hearn authored
PG-PuReMD: use a warp of threads for apply bond derivatives to forces and simplify these code paths.
Kurt A. O'Hearn authoredPG-PuReMD: use a warp of threads for apply bond derivatives to forces and simplify these code paths.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
forces.c 75.44 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 "forces.h"
#include "allocate.h"
#include "bond_orders.h"
#include "bonds.h"
#include "basic_comm.h"
#include "charges.h"
#include "comm_tools.h"
#include "hydrogen_bonds.h"
#include "io_tools.h"
#include "list.h"
#include "lookup.h"
#include "multi_body.h"
#include "nonbonded.h"
#include "tool_box.h"
#include "torsion_angles.h"
#include "valence_angles.h"
#include "vector.h"
#elif defined(LAMMPS_REAX)
#include "reax_forces.h"
#include "reax_allocate.h"
#include "reax_bond_orders.h"
#include "reax_bonds.h"
#include "reax_basic_comm.h"
#include "reax_charges.h"
#include "reax_comm_tools.h"
#include "reax_hydrogen_bonds.h"
#include "reax_io_tools.h"
#include "reax_list.h"
#include "reax_lookup.h"
#include "reax_multi_body.h"
#include "reax_nonbonded.h"
#include "reax_tool_box.h"
#include "reax_torsion_angles.h"
#include "reax_valence_angles.h"
#include "reax_vector.h"
#endif
#include "index_utils.h"
typedef enum
{
DIAGONAL = 0,
OFF_DIAGONAL = 1,
} MATRIX_ENTRY_POSITION;
/* placeholder for unused interactions in interaction list
* Interaction_Functions, which is initialized in Init_Force_Functions */
void Dummy_Interaction( reax_system *system, control_params *control,
simulation_data *data, storage *workspace, reax_list **lists,
output_controls *out_control )
{
}
void Init_Force_Functions( control_params * const control )
{
control->intr_funcs[0] = &BO;
control->intr_funcs[1] = &Bonds;
control->intr_funcs[2] = &Atom_Energy;
control->intr_funcs[3] = &Valence_Angles;
control->intr_funcs[4] = &Torsion_Angles;
if ( control->hbond_cut > 0.0 )
{
control->intr_funcs[5] = &Hydrogen_Bonds;
}
else
{
control->intr_funcs[5] = &Dummy_Interaction;
}
control->intr_funcs[6] = &Dummy_Interaction;
control->intr_funcs[7] = &Dummy_Interaction;
control->intr_funcs[8] = &Dummy_Interaction;
control->intr_funcs[9] = &Dummy_Interaction;
}
static inline real Init_Charge_Matrix_Entry_Tab( const reax_system * const system,
const control_params * const control, LR_lookup_table * const LR,
int i, int j, real r_ij, MATRIX_ENTRY_POSITION pos )
{
int tmin, tmax;
real val, ret;
LR_lookup_table *t;
ret = 0.0;
switch ( control->charge_method )
{
case QEQ_CM:
//TODO: tabulate other portions of matrices for EE, ACKS2?
case EE_CM:
case ACKS2_CM:
switch ( pos )
{
case OFF_DIAGONAL:
tmin = MIN( system->my_atoms[i].type, system->my_atoms[j].type );
tmax = MAX( system->my_atoms[i].type, system->my_atoms[j].type );
t = &LR[ index_lr( tmin, tmax, system->reax_param.num_atom_types ) ];
val = LR_Lookup_Entry( t, r_ij, LR_CM );
ret = ((i == j) ? 0.5 : 1.0) * val;
break;
case DIAGONAL:
ret = system->reax_param.sbp[system->my_atoms[i].type].eta;
break;
default:
fprintf( stderr, "[ERROR] Invalid matrix position. Terminating...\n" );
exit( INVALID_INPUT );
break;
}
break;
default:
fprintf( stderr, "[ERROR] Invalid charge method. Terminating...\n" );
exit( INVALID_INPUT );
break;
}
return ret;
}
static inline real Init_Charge_Matrix_Entry( const reax_system * const system,
const control_params * const control, const storage * const workspace,
int i, int j, real r_ij, MATRIX_ENTRY_POSITION pos )
{
real Tap, dr3gamij_1, dr3gamij_3, ret;
ret = 0.0;
switch ( control->charge_method )
{
case QEQ_CM:
case EE_CM:
case ACKS2_CM:
switch ( pos )
{
case OFF_DIAGONAL:
Tap = workspace->Tap[7] * r_ij + workspace->Tap[6];
Tap = Tap * r_ij + workspace->Tap[5];
Tap = Tap * r_ij + workspace->Tap[4];
Tap = Tap * r_ij + workspace->Tap[3];
Tap = Tap * r_ij + workspace->Tap[2];
Tap = Tap * r_ij + workspace->Tap[1];
Tap = Tap * r_ij + workspace->Tap[0];
/* shielding */
dr3gamij_1 = r_ij * r_ij * r_ij
+ POW( system->reax_param.tbp[
index_tbp( system->my_atoms[i].type,
system->my_atoms[j].type,
system->reax_param.num_atom_types )
].gamma, -3.0 );
dr3gamij_3 = POW( dr3gamij_1 , 1.0 / 3.0 );
/* i == j: periodic self-interaction term
* i != j: general interaction term */
ret = ((i == j) ? 0.5 : 1.0) * Tap * EV_to_KCALpMOL / dr3gamij_3;
break;
case DIAGONAL:
ret = system->reax_param.sbp[system->my_atoms[i].type].eta;
break;
default:
fprintf( stderr, "[ERROR] Invalid matrix position. Terminating...\n" );
exit( INVALID_INPUT );
break;
}
break;
default:
fprintf( stderr, "[ERROR] Invalid charge method. Terminating...\n" );
exit( INVALID_INPUT );
break;
}
return ret;
}
static void Init_Charge_Matrix_Remaining_Entries( reax_system *system,
control_params *control, reax_list *far_nbr_list,
sparse_matrix * H, //sparse_matrix * H_sp,
int * Htop )//, int * H_sp_top )
{
int i, j, pj, target, val_flag;
real d, xcut, bond_softness, * X_diag;
switch ( control->charge_method )
{
case QEQ_CM:
break;
case EE_CM:
H->start[system->n_cm - 1] = *Htop;
// H_sp->start[system->n_cm - 1] = *H_sp_top;
for ( i = 0; i < system->n_cm - 1; ++i )
{
H->j[*Htop] = i;
H->val[*Htop] = 1.0;
*Htop = *Htop + 1;
// H_sp->j[*H_sp_top] = i;
// H_sp->val[*H_sp_top] = 1.0;
// *H_sp_top = *H_sp_top + 1;
}
H->j[*Htop] = system->n_cm - 1;
H->val[*Htop] = 0.0;
*Htop = *Htop + 1;
// H_sp->j[*H_sp_top] = system->n_cm - 1;
// H_sp->val[*H_sp_top] = 0.0;
// *H_sp_top = *H_sp_top + 1;
break;
case ACKS2_CM:
X_diag = smalloc( sizeof(real) * system->N,
"Init_Charge_Matrix_Remaining_Entries::X_diag" );
for ( i = 0; i < system->N; ++i )
{
X_diag[i] = 0.0;
}
for ( i = 0; i < system->N; ++i )
{
H->start[system->N + i] = *Htop;
// H_sp->start[system->N + i] = *H_sp_top;
/* constraint on ref. value for kinetic energy potential */
H->j[*Htop] = i;
H->val[*Htop] = 1.0;
*Htop = *Htop + 1;
// H_sp->j[*H_sp_top] = i;
// H_sp->val[*H_sp_top] = 1.0;
// *H_sp_top = *H_sp_top + 1;
/* kinetic energy terms */
for ( pj = Start_Index(i, far_nbr_list); pj < End_Index(i, far_nbr_list); ++pj )
{
/* exclude self-periodic images of atoms for
* kinetic energy term because the effective
* potential is the same on an atom and its periodic image */
if ( far_nbr_list->far_nbr_list.d[pj] <= control->nonb_cut )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
xcut = 0.5 * ( system->reax_param.sbp[ system->my_atoms[i].type ].b_s_acks2
+ system->reax_param.sbp[ system->my_atoms[j].type ].b_s_acks2 );
if ( far_nbr_list->far_nbr_list.d[pj] < xcut )
{
d = far_nbr_list->far_nbr_list.d[pj] / xcut;
bond_softness = system->reax_param.gp.l[34] * POW( d, 3.0 )
* POW( 1.0 - d, 6.0 );
if ( bond_softness > 0.0 )
{
val_flag = FALSE;
for ( target = H->start[system->N + i]; target < *Htop; ++target )
{
if ( H->j[target] == system->N + j )
{
H->val[target] += bond_softness;
val_flag = TRUE;
break;
}
}
if ( val_flag == FALSE )
{
H->j[*Htop] = system->N + j;
H->val[*Htop] = bond_softness;
++(*Htop);
}
// val_flag = FALSE;
// for ( target = H_sp->start[system->N + i]; target < *H_sp_top; ++target )
// {
// if ( H_sp->j[target] == system->N + j )
// {
// H_sp->val[target] += bond_softness;
// val_flag = TRUE;
// break;
// }
// }
// if ( val_flag == FALSE )
// {
// H_sp->j[*H_sp_top] = system->N + j;
// H_sp->val[*H_sp_top] = bond_softness;
// ++(*H_sp_top);
// }
X_diag[i] -= bond_softness;
X_diag[j] -= bond_softness;
}
}
}
}
/* placeholders for diagonal entries, to be replaced below */
H->j[*Htop] = system->N + i;
H->val[*Htop] = 0.0;
*Htop = *Htop + 1;
// H_sp->j[*H_sp_top] = system->N + i;
// H_sp->val[*H_sp_top] = 0.0;
// *H_sp_top = *H_sp_top + 1;
}
/* second to last row */
H->start[system->n_cm - 2] = *Htop;
// H_sp->start[system->n_cm - 2] = *H_sp_top;
/* place accumulated diagonal entries (needed second to last row marker above before this code) */
for ( i = system->N; i < 2 * system->N; ++i )
{
for ( pj = H->start[i]; pj < H->start[i + 1]; ++pj )
{
if ( H->j[pj] == i )
{
H->val[pj] = X_diag[i - system->N];
break;
}
}
// for ( pj = H_sp->start[i]; pj < H_sp->start[i + 1]; ++pj )
// {
// if ( H_sp->j[pj] == i )
// {
// H_sp->val[pj] = X_diag[i - system->N];
// break;
// }
// }
}
/* coupling with the kinetic energy potential */
for ( i = 0; i < system->N; ++i )
{
H->j[*Htop] = system->N + i;
H->val[*Htop] = 1.0;
*Htop = *Htop + 1;
// H_sp->j[*H_sp_top] = system->N + i;
// H_sp->val[*H_sp_top] = 1.0;
// *H_sp_top = *H_sp_top + 1;
}
/* explicitly store zero on diagonal */
H->j[*Htop] = system->n_cm - 2;
H->val[*Htop] = 0.0;
*Htop = *Htop + 1;
// H_sp->j[*H_sp_top] = system->n_cm - 2;
// H_sp->val[*H_sp_top] = 0.0;
// *H_sp_top = *H_sp_top + 1;
/* last row */
H->start[system->n_cm - 1] = *Htop;
// H_sp->start[system->n_cm - 1] = *H_sp_top;
for ( i = 0; i < system->N; ++i )
{
H->j[*Htop] = i;
H->val[*Htop] = 1.0;
*Htop = *Htop + 1;
// H_sp->j[*H_sp_top] = i;
// H_sp->val[*H_sp_top] = 1.0;
// *H_sp_top = *H_sp_top + 1;
}
/* explicitly store zero on diagonal */
H->j[*Htop] = system->n_cm - 1;
H->val[*Htop] = 0.0;
*Htop = *Htop + 1;
// H_sp->j[*H_sp_top] = system->n_cm - 1;
// H_sp->val[*H_sp_top] = 0.0;
// *H_sp_top = *H_sp_top + 1;
sfree( X_diag, "Init_Charge_Matrix_Remaining_Entries::X_diag" );
break;
default:
break;
}
}
/* Compute the distances and displacement vectors for entries
* in the far neighbors list if it's a NOT re-neighboring step */
static void Init_Distance( reax_system *system, control_params *control,
simulation_data *data, storage *workspace, reax_list **lists,
output_controls *out_control )
{
int i, j, pj;
int start_i, end_i;
reax_list *far_nbr_list;
reax_atom *atom_i, *atom_j;
far_nbr_list = lists[FAR_NBRS];
for ( i = 0; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
/* update distance and displacement vector between atoms i and j (i-j) */
for ( pj = start_i; pj < end_i; ++pj )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
atom_j = &system->my_atoms[j];
far_nbr_list->far_nbr_list.dvec[pj][0] = atom_j->x[0] - atom_i->x[0];
far_nbr_list->far_nbr_list.dvec[pj][1] = atom_j->x[1] - atom_i->x[1];
far_nbr_list->far_nbr_list.dvec[pj][2] = atom_j->x[2] - atom_i->x[2];
far_nbr_list->far_nbr_list.d[pj] = rvec_Norm( far_nbr_list->far_nbr_list.dvec[pj] );
}
}
}
#if defined(NEUTRAL_TERRITORY)
/* Compute the charge matrix entries and store the matrix in half format
* using the far neighbors list (stored in full format) and according to
* the neutral territory communication method */
static void Init_CM_Half_NT( reax_system *system, control_params *control,
simulation_data *data, storage *workspace, reax_list **lists,
output_controls *out_control )
{
int i, j, pj;
int start_i, end_i;
int type_i, type_j;
int cm_top;
int local, renbr;
real r_ij;
sparse_matrix *H;
reax_list *far_nbr_list;
two_body_parameters *twbp;
reax_atom *atom_i, *atom_j;
int mark[6];
int total_cnt[6];
int bin[6];
int total_sum[6];
int nt_flag;
far_nbr_list = lists[FAR_NBRS];
H = workspace->H;
H->n = system->n;
cm_top = 0;
renbr = is_refactoring_step( control, data );
nt_flag = TRUE;
if ( renbr == TRUE )
{
for ( i = 0; i < 6; ++i )
{
total_cnt[i] = 0;
bin[i] = 0;
total_sum[i] = 0;
}
for ( i = system->n; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
if ( atom_i->nt_dir != -1 )
{
total_cnt[ atom_i->nt_dir ]++;
}
}
total_sum[0] = system->n;
for ( i = 1; i < 6; ++i )
{
total_sum[i] = total_sum[i - 1] + total_cnt[i - 1];
}
for ( i = system->n; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
if ( atom_i->nt_dir != -1 )
{
atom_i->pos = total_sum[ atom_i->nt_dir ] + bin[ atom_i->nt_dir ];
bin[ atom_i->nt_dir ]++;
}
}
H->NT = total_sum[5] + total_cnt[5];
}
mark[0] = 1;
mark[1] = 1;
mark[2] = 2;
mark[3] = 2;
mark[4] = 2;
mark[5] = 2;
for ( i = 0; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
type_i = atom_i->type;
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
if ( i < system->n )
{
local = 1;
}
else if ( atom_i->nt_dir != -1 )
{
local = 2;
nt_flag = FALSE;
}
else
{
continue;
}
if ( local == 1 )
{
H->start[i] = cm_top;
H->j[cm_top] = i;
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, i, 0.0, DIAGONAL );
++cm_top;
}
for ( pj = start_i; pj < end_i; ++pj )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
atom_j = &system->my_atoms[j];
if ( far_nbr_list->far_nbr_list.d[pj] <= control->nonb_cut )
{
type_j = atom_j->type;
r_ij = far_nbr_list->far_nbr_list.d[pj];
twbp = &system->reax_param.tbp[
index_tbp(type_i, type_j, system->reax_param.num_atom_types) ];
if ( local == 1 )
{
/* H matrix entry */
if ( atom_j->nt_dir > 0 || (j < system->n && i < j) )
{
if ( j < system->n )
{
H->j[cm_top] = j;
}
else
{
H->j[cm_top] = atom_j->pos;
}
if ( control->tabulate == 0 )
{
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, j, r_ij, OFF_DIAGONAL );
}
else
{
H->val[cm_top] = Init_Charge_Matrix_Entry_Tab( system, control,
workspace->LR, i, j, r_ij, OFF_DIAGONAL );
}
++cm_top;
}
}
else if ( local == 2 )
{
/* H matrix entry */
if ( atom_j->nt_dir != -1
&& mark[atom_i->nt_dir] != mark[atom_j->nt_dir]
&& atom_i->pos < atom_j->pos )
{
if ( nt_flag == FALSE )
{
nt_flag = TRUE;
H->start[atom_i->pos] = cm_top;
}
//TODO: necessary?
if ( j < system->n )
{
H->j[cm_top] = j;
}
else
{
H->j[cm_top] = atom_j->pos;
}
if ( control->tabulate == 0 )
{
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, j, r_ij, OFF_DIAGONAL );
}
else
{
H->val[cm_top] = Init_Charge_Matrix_Entry_Tab( system, control,
workspace->LR, i, j, r_ij, OFF_DIAGONAL );
}
++cm_top;
}
}
}
}
if ( local == 1 )
{
H->end[i] = cm_top;
}
else if ( local == 2 )
{
if ( nt_flag == TRUE )
{
H->end[atom_i->pos] = cm_top;
}
else
{
H->start[atom_i->pos] = 0;
H->end[atom_i->pos] = 0;
}
}
}
/* reallocation check */
for ( i = 0; i < system->N; ++i )
{
if ( i < system->n && H->end[i] - H->start[i] > system->max_cm_entries[i] )
{
workspace->realloc.cm = TRUE;
break;
}
}
}
/* Compute the charge matrix entries and store the matrix in full format
* using the far neighbors list (stored in full format) and according to
* the neutral territory communication method */
static void Init_CM_Full_NT( reax_system *system, control_params *control,
simulation_data *data, storage *workspace, reax_list **lists,
output_controls *out_control )
{
int i, j, pj;
int start_i, end_i;
int type_i, type_j;
int cm_top;
int local, renbr;
real r_ij;
sparse_matrix *H;
reax_list *far_nbr_list;
two_body_parameters *twbp;
reax_atom *atom_i, *atom_j;
int mark[6];
int total_cnt[6];
int bin[6];
int total_sum[6];
int nt_flag;
far_nbr_list = lists[FAR_NBRS];
H = workspace->H;
H->n = system->n;
cm_top = 0;
renbr = is_refactoring_step( control, data );
nt_flag = TRUE;
if ( renbr == TRUE )
{
for ( i = 0; i < 6; ++i )
{
total_cnt[i] = 0;
bin[i] = 0;
total_sum[i] = 0;
}
for ( i = system->n; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
if ( atom_i->nt_dir != -1 )
{
total_cnt[ atom_i->nt_dir ]++;
}
}
total_sum[0] = system->n;
for ( i = 1; i < 6; ++i )
{
total_sum[i] = total_sum[i - 1] + total_cnt[i - 1];
}
for ( i = system->n; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
if ( atom_i->nt_dir != -1 )
{
atom_i->pos = total_sum[ atom_i->nt_dir ] + bin[ atom_i->nt_dir ];
bin[ atom_i->nt_dir ]++;
}
}
H->NT = total_sum[5] + total_cnt[5];
}
mark[0] = 1;
mark[1] = 1;
mark[2] = 2;
mark[3] = 2;
mark[4] = 2;
mark[5] = 2;
for ( i = 0; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
type_i = atom_i->type;
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
if ( i < system->n )
{
local = 1;
}
else if ( atom_i->nt_dir != -1 )
{
local = 2;
nt_flag = FALSE;
}
else
{
continue;
}
if ( local == 1 )
{
H->start[i] = cm_top;
H->j[cm_top] = i;
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, i, 0.0, DIAGONAL );
++cm_top;
}
for ( pj = start_i; pj < end_i; ++pj )
{
if ( far_nbr_list->far_nbr_list.d[pj] <= control->nonb_cut )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
atom_j = &system->my_atoms[j];
type_j = atom_j->type;
r_ij = far_nbr_list->far_nbr_list.d[pj];
twbp = &system->reax_param.tbp[
index_tbp(type_i, type_j, system->reax_param.num_atom_types) ];
if ( local == 1 )
{
/* H matrix entry */
if ( atom_j->nt_dir > 0 || (j < system->n) )
{
if ( j < system->n )
{
H->j[cm_top] = j;
}
else
{
H->j[cm_top] = atom_j->pos;
}
if ( control->tabulate == 0 )
{
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, j, r_ij, OFF_DIAGONAL );
}
else
{
H->val[cm_top] = Init_Charge_Matrix_Entry_Tab( system, control,
workspace->LR, i, j, r_ij, OFF_DIAGONAL );
}
++cm_top;
}
}
else if ( local == 2 )
{
/* H matrix entry */
if ( ( atom_j->nt_dir != -1
&& mark[atom_i->nt_dir] != mark[atom_j->nt_dir] )
|| ( j < system->n && atom_i->nt_dir != 0 ) )
{
if ( nt_flag == FALSE )
{
nt_flag = TRUE;
H->start[atom_i->pos] = cm_top;
}
if ( j < system->n )
{
H->j[cm_top] = j;
}
else
{
H->j[cm_top] = atom_j->pos;
}
if ( control->tabulate == 0 )
{
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, j, r_ij, OFF_DIAGONAL );
}
else
{
H->val[cm_top] = Init_Charge_Matrix_Entry_Tab( system, control,
workspace->LR, i, j, r_ij, OFF_DIAGONAL );
}
++cm_top;
}
}
}
}
if ( local == 1 )
{
H->end[i] = cm_top;
}
else if ( local == 2 )
{
if ( nt_flag == TRUE )
{
H->end[atom_i->pos] = cm_top;
}
else
{
H->start[atom_i->pos] = 0;
H->end[atom_i->pos] = 0;
}
}
}
/* reallocation check */
for ( i = 0; i < system->N; ++i )
{
if ( i < system->n && H->end[i] - H->start[i] > system->max_cm_entries[i] )
{
workspace->realloc.cm = TRUE;
break;
}
}
}
#else
/* Compute the charge matrix entries and store the matrix in half format
* using the far neighbors list (stored in half format) and according to
* the full shell communication method */
static void Init_CM_Half_FS( reax_system *system, control_params *control,
simulation_data *data, storage *workspace, reax_list **lists,
output_controls *out_control )
{
int i, j, pj;
int start_i, end_i;
int cm_top;
real r_ij;
sparse_matrix *H;
reax_list *far_nbr_list;
reax_atom *atom_i, *atom_j;
far_nbr_list = lists[FAR_NBRS];
H = &workspace->H;
H->n = system->n;
cm_top = 0;
for ( i = 0; i < system->n; ++i )
{
atom_i = &system->my_atoms[i];
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
/* diagonal entry in the matrix */
H->start[i] = cm_top;
H->j[cm_top] = i;
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, i, r_ij, DIAGONAL );
++cm_top;
for ( pj = start_i; pj < end_i; ++pj )
{
if ( far_nbr_list->far_nbr_list.d[pj] <= control->nonb_cut )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
atom_j = &system->my_atoms[j];
/* if j is a local atom OR
* if j is a ghost atom in the upper triangular region of the matrix */
if ( j < system->n || atom_i->orig_id < atom_j->orig_id )
{
r_ij = far_nbr_list->far_nbr_list.d[pj];
H->j[cm_top] = j;
if ( control->tabulate == 0 )
{
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, j, r_ij, OFF_DIAGONAL );
}
else
{
H->val[cm_top] = Init_Charge_Matrix_Entry_Tab( system, control,
workspace->LR, i, j, r_ij, OFF_DIAGONAL );
}
++cm_top;
}
}
}
H->end[i] = cm_top;
}
/* reallocation check */
for ( i = 0; i < system->n; ++i )
{
if ( H->end[i] - H->start[i] > system->max_cm_entries[i] )
{
workspace->realloc.cm = TRUE;
break;
}
}
}
/* Compute the charge matrix entries and store the matrix in full format
* using the far neighbors list (stored in full format) and according to
* the full shell communication method */
static void Init_CM_Full_FS( reax_system *system, control_params *control,
simulation_data *data, storage *workspace, reax_list **lists,
output_controls *out_control )
{
int i, j, pj;
int start_i, end_i;
int cm_top;
real r_ij;
sparse_matrix *H;
reax_list *far_nbr_list;
far_nbr_list = lists[FAR_NBRS];
H = &workspace->H;
H->n = system->n;
cm_top = 0;
for ( i = 0; i < system->n; ++i )
{
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
/* diagonal entry in the matrix */
H->start[i] = cm_top;
H->j[cm_top] = i;
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, i, r_ij, DIAGONAL );
++cm_top;
/* off-diagonal entries in the matrix */
for ( pj = start_i; pj < end_i; ++pj )
{
if ( far_nbr_list->far_nbr_list.d[pj] <= control->nonb_cut )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
r_ij = far_nbr_list->far_nbr_list.d[pj];
H->j[cm_top] = j;
if ( control->tabulate == 0 )
{
H->val[cm_top] = Init_Charge_Matrix_Entry( system, control,
workspace, i, j, r_ij, OFF_DIAGONAL );
}
else
{
H->val[cm_top] = Init_Charge_Matrix_Entry_Tab( system, control,
workspace->LR, i, j, r_ij, OFF_DIAGONAL );
}
++cm_top;
}
}
H->end[i] = cm_top;
}
/* reallocation check */
for ( i = 0; i < system->n; ++i )
{
if ( H->end[i] - H->start[i] > system->max_cm_entries[i] )
{
workspace->realloc.cm = TRUE;
break;
}
}
}
#endif
/* Compute entries of the bonds/hbonds lists and store the lists in full format
* using the far neighbors list (stored in half format)
*
* Note: this version does NOT contain an optimization to restrict the bond_mark
* array to at most the 3-hop neighborhood */
static void Init_Bond_Half( reax_system *system, control_params *control,
simulation_data *data, storage *workspace, reax_list **lists,
output_controls *out_control )
{
int i, j, pj;
int start_i, end_i;
int type_i, type_j;
int btop_i;
int ihb, jhb, ihb_top;
int local;
real cutoff;
reax_list *far_nbr_list, *bond_list, *hbond_list;
single_body_parameters *sbp_i, *sbp_j;
two_body_parameters *twbp;
reax_atom *atom_i, *atom_j;
int jhb_top;
far_nbr_list = lists[FAR_NBRS];
bond_list = lists[BONDS];
hbond_list = lists[HBONDS];
for ( i = 0; i < system->n; ++i )
{
workspace->bond_mark[i] = 0;
}
for ( i = system->n; i < system->N; ++i )
{
/* put ghost atoms to an infinite distance (i.e., 1000) */
workspace->bond_mark[i] = 1000;
}
btop_i = 0;
for ( i = 0; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
type_i = atom_i->type;
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
/* start at end because other atoms
* can add to this atom's list (half-list) */
btop_i = End_Index( i, bond_list );
sbp_i = &system->reax_param.sbp[type_i];
ihb = NON_H_BONDING_ATOM;
ihb_top = -1;
if ( i < system->n )
{
local = TRUE;
cutoff = control->nonb_cut;
}
else
{
local = FALSE;
cutoff = control->bond_cut;
}
if ( local == TRUE )
{
if ( control->hbond_cut > 0.0 )
{
ihb = sbp_i->p_hbond;
if ( ihb == H_ATOM )
{
/* start at end because other atoms
* can add to this atom's list (half-list) */
ihb_top = End_Index( atom_i->Hindex, hbond_list );
}
else
{
ihb_top = -1;
}
}
}
/* update i-j distance - check if j is within cutoff */
for ( pj = start_i; pj < end_i; ++pj )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
atom_j = &system->my_atoms[j];
if ( far_nbr_list->far_nbr_list.d[pj] <= cutoff )
{
type_j = atom_j->type;
sbp_j = &system->reax_param.sbp[type_j];
twbp = &system->reax_param.tbp[
index_tbp(type_i, type_j, system->reax_param.num_atom_types) ];
if ( local == TRUE )
{
/* hydrogen bond lists */
if ( control->hbond_cut > 0.0
&& (ihb == H_ATOM || ihb == H_BONDING_ATOM)
&& far_nbr_list->far_nbr_list.d[pj] <= control->hbond_cut )
{
jhb = sbp_j->p_hbond;
if ( ihb == H_ATOM && jhb == H_BONDING_ATOM )
{
hbond_list->hbond_list[ihb_top].nbr = j;
hbond_list->hbond_list[ihb_top].scl = 1;
hbond_list->hbond_list[ihb_top].ptr = pj;
++ihb_top;
}
/* only add to list for local j (far nbrs is half-list) */
else if ( j < system->n && ihb == H_BONDING_ATOM && jhb == H_ATOM )
{
jhb_top = End_Index( atom_j->Hindex, hbond_list );
hbond_list->hbond_list[jhb_top].nbr = i;
hbond_list->hbond_list[jhb_top].scl = -1;
hbond_list->hbond_list[jhb_top].ptr = pj;
Set_End_Index( atom_j->Hindex, jhb_top + 1, hbond_list );
}
}
}
/* uncorrected bond orders */
if ( far_nbr_list->far_nbr_list.d[pj] <= control->bond_cut
&& BOp( workspace, bond_list, control->bo_cut,
i, btop_i, far_nbr_list->far_nbr_list.nbr[pj],
&far_nbr_list->far_nbr_list.rel_box[pj], far_nbr_list->far_nbr_list.d[pj],
&far_nbr_list->far_nbr_list.dvec[pj], far_nbr_list->format,
sbp_i, sbp_j, twbp ) == TRUE )
{
++btop_i;
if ( workspace->bond_mark[j] > workspace->bond_mark[i] + 1 )
{
workspace->bond_mark[j] = workspace->bond_mark[i] + 1;
}
else if ( workspace->bond_mark[i] > workspace->bond_mark[j] + 1 )
{
workspace->bond_mark[i] = workspace->bond_mark[j] + 1;
}
}
}
}
Set_End_Index( i, btop_i, bond_list );
if ( local == TRUE && ihb == H_ATOM )
{
Set_End_Index( atom_i->Hindex, ihb_top, hbond_list );
}
}
/* reallocation checks */
for ( i = 0; i < system->N; ++i )
{
if ( Num_Entries( i, bond_list ) > system->max_bonds[i] )
{
workspace->realloc.bonds = TRUE;
break;
}
}
for ( i = 0; i < system->n; ++i )
{
if ( system->reax_param.sbp[ system->my_atoms[i].type ].p_hbond == H_ATOM
&& Num_Entries( system->my_atoms[i].Hindex, hbond_list )
> system->max_hbonds[system->my_atoms[i].Hindex] )
{
workspace->realloc.hbonds = TRUE;
break;
}
}
}
/* Compute entries of the bonds/hbonds lists and store the lists in full format
* using the far neighbors list (stored in full format) */
static void Init_Bond_Full( reax_system *system, control_params *control,
simulation_data *data, storage *workspace, reax_list **lists,
output_controls *out_control )
{
int i, j, pj;
int start_i, end_i;
int type_i, type_j;
int ihb, ihb_top;
reax_list *far_nbr_list, *bond_list, *hbond_list;
single_body_parameters *sbp_i, *sbp_j;
two_body_parameters *twbp;
reax_atom *atom_i, *atom_j;
int btop_i;
int k, push;
int *q;
far_nbr_list = lists[FAR_NBRS];
bond_list = lists[BONDS];
hbond_list = lists[HBONDS];
push = 0;
btop_i = 0;
bond_list = lists[BONDS];
q = smalloc( sizeof(int) * (system->N - system->n),
"Init_Bond_Full::q" );
for ( i = 0; i < system->n; ++i )
{
workspace->bond_mark[i] = 0;
}
for ( i = system->n; i < system->N; ++i )
{
/* put ghost atoms to an infinite distance (i.e., 1000) */
workspace->bond_mark[i] = 1000;
}
/* bonds that are directly connected to local atoms */
for ( i = 0; i < system->n; ++i )
{
atom_i = &system->my_atoms[i];
type_i = atom_i->type;
btop_i = End_Index( i, bond_list );
sbp_i = &system->reax_param.sbp[type_i];
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
ihb = sbp_i->p_hbond;
ihb_top = Start_Index( atom_i->Hindex, hbond_list );
for ( pj = start_i; pj < end_i; ++pj )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
atom_j = &system->my_atoms[j];
if ( control->hbond_cut > 0.0 && ihb == H_ATOM )
{
/* check if j is within cutoff */
if ( far_nbr_list->far_nbr_list.d[pj] <= control->hbond_cut
&& system->reax_param.sbp[atom_j->type].p_hbond == H_BONDING_ATOM )
{
hbond_list->hbond_list[ihb_top].nbr = j;
hbond_list->hbond_list[ihb_top].scl = 1;
hbond_list->hbond_list[ihb_top].ptr = pj;
++ihb_top;
}
}
if ( i <= j && far_nbr_list->far_nbr_list.d[pj] <= control->bond_cut )
{
type_j = atom_j->type;
sbp_j = &system->reax_param.sbp[type_j];
twbp = &system->reax_param.tbp[
index_tbp(type_i, type_j, system->reax_param.num_atom_types) ];
if ( BOp( workspace, bond_list, control->bo_cut,
i, btop_i, far_nbr_list->far_nbr_list.nbr[pj],
&far_nbr_list->far_nbr_list.rel_box[pj], far_nbr_list->far_nbr_list.d[pj],
&far_nbr_list->far_nbr_list.dvec[pj], far_nbr_list->format,
sbp_i, sbp_j, twbp ) == TRUE )
{
++btop_i;
/* if j is a non-local atom, push it on the queue
* to search for it's bonded neighbors later */
if ( workspace->bond_mark[j] == 1000 )
{
workspace->bond_mark[j] = 101;
q[ push++ ] = j;
}
}
}
}
if ( control->hbond_cut > 0.0 && ihb == H_ATOM )
{
Set_End_Index( atom_i->Hindex, ihb_top, hbond_list );
}
Set_End_Index( i, btop_i, bond_list );
}
/* bonds that are indirectly connected to local atoms */
for ( k = 0; k < push; ++k )
{
i = q[k];
workspace->bond_mark[i] -= 100;
atom_i = &system->my_atoms[i];
type_i = atom_i->type;
btop_i = End_Index( i, bond_list );
sbp_i = &system->reax_param.sbp[type_i];
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
for ( pj = start_i; pj < end_i; ++pj )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
if ( workspace->bond_mark[i] == 3
&& workspace->bond_mark[j] == 1000 )
{
continue;
}
atom_j = &system->my_atoms[j];
if ( workspace->bond_mark[j] > 100
&& far_nbr_list->far_nbr_list.d[pj] <= control->bond_cut )
{
type_j = atom_j->type;
sbp_j = &system->reax_param.sbp[type_j];
twbp = &system->reax_param.tbp[
index_tbp(type_i, type_j, system->reax_param.num_atom_types) ];
if ( BOp( workspace, bond_list, control->bo_cut,
i, btop_i, far_nbr_list->far_nbr_list.nbr[pj],
&far_nbr_list->far_nbr_list.rel_box[pj], far_nbr_list->far_nbr_list.d[pj],
&far_nbr_list->far_nbr_list.dvec[pj], far_nbr_list->format,
sbp_i, sbp_j, twbp ) == TRUE )
{
++btop_i;
if ( workspace->bond_mark[j] == 1000 )
{
workspace->bond_mark[j] = workspace->bond_mark[i] + 100;
if ( workspace->bond_mark[i] < 3 )
{
q[ push++ ] = j;
}
}
}
}
}
Set_End_Index( i, btop_i, bond_list );
}
/* reallocation checks */
for ( i = 0; i < system->N; ++i )
{
if ( Num_Entries( i, bond_list ) > system->max_bonds[i] )
{
workspace->realloc.bonds = TRUE;
break;
}
}
for ( i = 0; i < system->n; ++i )
{
if ( system->reax_param.sbp[ system->my_atoms[i].type ].p_hbond == H_ATOM
&& Num_Entries( system->my_atoms[i].Hindex, hbond_list )
> system->max_hbonds[system->my_atoms[i].Hindex] )
{
workspace->realloc.hbonds = TRUE;
break;
}
}
sfree( q, "Init_Bond_Full::q" );
}
static void Compute_Bonded_Forces( reax_system * const system,
control_params * const control,
simulation_data * const data, storage * const workspace,
reax_list ** const lists, output_controls * const out_control )
{
int i;
#if defined(TEST_ENERGY)
/* Mark beginning of a new timestep in bonded energy files */
Debug_Marker_Bonded( out_control, data->step );
#endif
/* Implement all force calls as function pointers */
for ( i = 0; i < NUM_INTRS; i++ )
{
if ( control->intr_funcs[i] != NULL )
{
(control->intr_funcs[i])( system, control, data, workspace,
lists, out_control );
}
}
}
static void Compute_NonBonded_Forces( reax_system * const system,
control_params * const control,
simulation_data * const data, storage * const workspace,
reax_list ** const lists, output_controls * const out_control,
mpi_datatypes * const mpi_data )
{
#if defined(TEST_ENERGY)
/* Mark beginning of a new timestep in nonbonded energy files */
Debug_Marker_Nonbonded( out_control, data->step );
#endif
/* van der Waals and Coulomb interactions */
if ( control->tabulate == 0 )
{
vdW_Coulomb_Energy( system, control, data, workspace, lists, out_control );
}
else
{
Tabulated_vdW_Coulomb_Energy( system, control, data, workspace, lists, out_control );
}
}
static void Estimate_Storages_CM( reax_system * const system, control_params * const control,
reax_list ** const lists, int * const matrix_dim, int cm_format )
{
int i, j, pj;
int start_i, end_i;
int local;
real cutoff;
reax_list *far_nbr_list;
reax_atom *atom_i, *atom_j;
#if defined(NEUTRAL_TERRITORY)
int mark[6] = {1, 1, 2, 2, 2, 2};
#endif
far_nbr_list = lists[FAR_NBRS];
*matrix_dim = 0;
for ( i = 0; i < system->total_cap; ++i )
{
if ( i < system->local_cap )
{
system->cm_entries[i] = 0;
}
}
for ( i = 0; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
if ( i < system->n )
{
local = 1;
cutoff = control->nonb_cut;
++system->cm_entries[i];
++(*matrix_dim);
}
#if defined(NEUTRAL_TERRITORY)
else if ( atom_i->nt_dir != -1 )
{
local = 2;
cutoff = control->nonb_cut;
++(*matrix_dim);
}
#endif
else
{
local = 0;
cutoff = control->bond_cut;
}
for ( pj = start_i; pj < end_i; ++pj )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
#if !defined(NEUTRAL_TERRITORY)
if ( far_nbr_list->format == HALF_LIST )
#endif
{
atom_j = &system->my_atoms[j];
}
if ( far_nbr_list->far_nbr_list.d[pj] <= cutoff )
{
if ( local == 1 )
{
#if defined(NEUTRAL_TERRITORY)
if( atom_j->nt_dir > 0 || j < system->n )
{
++system->cm_entries[i];
}
#else
if ( (far_nbr_list->format == HALF_LIST
&& (j < system->n || atom_i->orig_id < atom_j->orig_id))
|| far_nbr_list->format == FULL_LIST )
{
++system->cm_entries[i];
}
#endif
}
#if defined(NEUTRAL_TERRITORY)
else if ( local == 2 )
{
if( ( atom_j->nt_dir != -1 && mark[atom_i->nt_dir] != mark[atom_j->nt_dir] )
|| ( j < system->n && atom_i->nt_dir != 0 ))
{
++system->cm_entries[i];
}
}
#endif
}
}
}
#if defined(NEUTRAL_TERRITORY)
/* Since we don't know the NT atoms' position yet, Htop cannot be calculated accurately.
* Therefore, we assume it is full and divide 2 if necessary. */
if ( cm_format == SYM_HALF_MATRIX )
{
for ( i = 0; i < system->local_cap; ++i )
{
system->cm_entries[i] = (system->cm_entries[i] + system->n + 1) / 2;
}
}
#endif
#if defined(NEUTRAL_TERRITORY)
*matrix_dim = (int) MAX( *matrix_dim * SAFE_ZONE_NT, MIN_CAP );
#else
*matrix_dim = (int) MAX( *matrix_dim * SAFE_ZONE, MIN_CAP );
#endif
for ( i = 0; i < system->N; ++i )
{
if ( i < system->local_cap )
{
#if defined(NEUTRAL_TERRITORY)
system->max_cm_entries[i] = MAX( (int)(system->cm_entries[i] * SAFE_ZONE_NT), MIN_CM_ENTRIES );
#else
system->max_cm_entries[i] = MAX( (int)(system->cm_entries[i] * SAFE_ZONE), MIN_CM_ENTRIES );
#endif
}
}
/* set currently unused space to min. capacity */
for ( i = system->N; i < system->local_cap; ++i )
{
system->max_cm_entries[i] = MIN_CM_ENTRIES;
}
/* reductions to get totals */
system->total_cm_entries = 0;
for ( i = 0; i < system->local_cap; ++i )
{
system->total_cm_entries += system->max_cm_entries[i];
}
system->total_cm_entries = MAX( system->total_cm_entries, MIN_CAP * MIN_CM_ENTRIES );
}
static void Estimate_Storages_Bonds( reax_system * const system,
control_params * const control, reax_list ** const lists )
{
int i, j, pj;
int start_i, end_i;
int type_i, type_j;
int ihb, jhb;
int local;
real cutoff;
real r_ij;
real C12, C34, C56;
real BO, BO_s, BO_pi, BO_pi2;
reax_list *far_nbr_list;
single_body_parameters *sbp_i, *sbp_j;
two_body_parameters *twbp;
reax_atom *atom_i, *atom_j;
#if defined(NEUTRAL_TERRITORY)
int mark[6] = {1, 1, 2, 2, 2, 2};
#endif
far_nbr_list = lists[FAR_NBRS];
for ( i = 0; i < system->total_cap; ++i )
{
system->bonds[i] = 0;
}
for ( i = 0; i < system->total_cap; ++i )
{
system->hbonds[i] = 0;
}
for ( i = 0; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
type_i = atom_i->type;
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
sbp_i = &system->reax_param.sbp[type_i];
if ( i < system->n )
{
local = 1;
cutoff = control->nonb_cut;
ihb = sbp_i->p_hbond;
}
#if defined(NEUTRAL_TERRITORY)
else if ( atom_i->nt_dir != -1 )
{
local = 2;
cutoff = control->nonb_cut;
ihb = -1;
}
#endif
else
{
local = 0;
cutoff = control->bond_cut;
ihb = NON_H_BONDING_ATOM;
}
for ( pj = start_i; pj < end_i; ++pj )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
#if !defined(NEUTRAL_TERRITORY)
if ( far_nbr_list->format == HALF_LIST )
#endif
{
atom_j = &system->my_atoms[j];
}
if ( far_nbr_list->far_nbr_list.d[pj] <= cutoff )
{
type_j = system->my_atoms[j].type;
r_ij = far_nbr_list->far_nbr_list.d[pj];
sbp_j = &system->reax_param.sbp[type_j];
twbp = &system->reax_param.tbp[
index_tbp(type_i, type_j, system->reax_param.num_atom_types) ];
if ( local == 1 )
{
/* hydrogen bond lists */
if ( control->hbond_cut > 0.1
&& (ihb == H_ATOM || ihb == H_BONDING_ATOM)
&& far_nbr_list->far_nbr_list.d[pj] <= control->hbond_cut )
{
jhb = sbp_j->p_hbond;
if ( ihb == H_ATOM && jhb == H_BONDING_ATOM )
{
++system->hbonds[atom_i->Hindex];
}
/* only add to list for local j (far nbrs is half-list) */
else if ( far_nbr_list->format == HALF_LIST
&& (j < system->n && ihb == H_BONDING_ATOM && jhb == H_ATOM) )
{
++system->hbonds[atom_j->Hindex];
}
}
}
/* uncorrected bond orders */
if ( far_nbr_list->far_nbr_list.d[pj] <= control->bond_cut )
{
if ( sbp_i->r_s > 0.0 && sbp_j->r_s > 0.0 )
{
C12 = twbp->p_bo1 * POW( r_ij / twbp->r_s, twbp->p_bo2 );
BO_s = (1.0 + control->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( r_ij / 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( r_ij / 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 >= control->bo_cut )
{
++system->bonds[i];
if ( far_nbr_list->format == HALF_LIST )
{
++system->bonds[j];
}
}
}
}
}
}
for ( i = 0; i < system->total_cap; ++i )
{
system->max_hbonds[i] = 0;
}
for ( i = 0; i < system->N; ++i )
{
if ( far_nbr_list->format == HALF_LIST )
{
system->max_bonds[i] = MAX( (int)(2.0 * system->bonds[i] * SAFE_ZONE), MIN_BONDS );
}
else
{
system->max_bonds[i] = MAX( (int)(system->bonds[i] * SAFE_ZONE), MIN_BONDS );
}
if ( system->reax_param.sbp[ system->my_atoms[i].type ].p_hbond == H_ATOM )
{
system->max_hbonds[ system->my_atoms[i].Hindex ] = MAX(
(int)(system->hbonds[ system->my_atoms[i].Hindex ] * SAFE_ZONE), MIN_HBONDS );
}
}
/* set currently unused space to min. capacity */
for ( i = system->N; i < system->total_cap; ++i )
{
system->max_bonds[i] = MIN_BONDS;
}
for ( i = system->N; i < system->total_cap; ++i )
{
system->max_hbonds[i] = MIN_HBONDS;
}
/* reductions to get totals */
system->total_bonds = 0;
system->total_hbonds = 0;
system->total_thbodies = 0;
for ( i = 0; i < system->total_cap; ++i )
{
system->total_bonds += system->max_bonds[i];
}
for ( i = 0; i < system->total_cap; ++i )
{
system->total_hbonds += system->max_hbonds[i];
}
if ( far_nbr_list->format == HALF_LIST )
{
for ( i = 0; i < system->total_cap; ++i )
{
system->total_thbodies += SQR( system->max_bonds[i] / 2.0 );
}
}
else
{
for ( i = 0; i < system->total_cap; ++i )
{
system->total_thbodies += SQR( system->max_bonds[i] );
}
}
system->total_bonds = MAX( system->total_bonds, MIN_CAP * MIN_BONDS );
system->total_hbonds = MAX( system->total_hbonds, MIN_CAP * MIN_HBONDS );
system->total_thbodies = MAX( system->total_thbodies * SAFE_ZONE, MIN_3BODIES );
/* duplicate info in atom structs in case of
* ownership transfer across processor boundaries */
for ( i = 0; i < system->n; ++i )
{
system->my_atoms[i].num_bonds = system->bonds[i];
}
for ( i = 0; i < system->N; ++i )
{
system->my_atoms[i].num_hbonds = system->hbonds[i];
}
}
/* this version of Compute_Total_Force computes forces from
* coefficients accumulated by all interaction functions.
* Saves enormous time & space! */
static void Compute_Total_Force( reax_system * const system,
control_params * const control,
simulation_data * const data, storage * const workspace, reax_list ** const lists,
mpi_datatypes * const mpi_data )
{
int i, pj;
reax_list * const bond_list = lists[BONDS];
if ( control->virial == 0 )
{
for ( i = 0; i < system->N; ++i )
{
for ( pj = Start_Index(i, bond_list); pj < End_Index(i, bond_list); ++pj )
{
if ( i < bond_list->bond_list[pj].nbr )
{
Add_dBond_to_Forces( i, pj, system, data, workspace, lists );
}
}
}
}
else
{
for ( i = 0; i < system->N; ++i )
{
for ( pj = Start_Index(i, bond_list); pj < End_Index(i, bond_list); ++pj )
{
if ( i < bond_list->bond_list[pj].nbr )
{
Add_dBond_to_Forces_NPT( i, pj, system, data, workspace, lists );
}
}
}
}
#if defined(PURE_REAX)
/* now all forces are computed to their partially-final values
* based on the neighbors information each processor has had.
* final values of force on each atom needs to be computed by adding up
* all partially-final pieces */
Coll_FS( system, mpi_data, workspace->f, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
for ( i = 0; i < system->n; ++i )
{
rvec_Copy( system->my_atoms[i].f, workspace->f[i] );
}
#if defined(TEST_FORCES)
Coll_FS( system, mpi_data, workspace->f_ele, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_vdw, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_be, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_lp, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_ov, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_un, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_ang, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_coa, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_pen, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_hb, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_tor, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
Coll_FS( system, mpi_data, workspace->f_con, RVEC_PTR_TYPE, mpi_data->mpi_rvec );
#endif
#endif
}
static int Init_Forces( reax_system *system, control_params *control,
simulation_data *data, storage *workspace, reax_list **lists,
output_controls *out_control, mpi_datatypes *mpi_data )
{
int i, renbr, ret;
static int dist_done = FALSE, cm_done = FALSE, bonds_done = FALSE;
#if defined(LOG_PERFORMANCE)
double time;
time = Get_Time( );
#endif
renbr = (data->step - data->prev_steps) % control->reneighbor == 0 ? TRUE : FALSE;
if ( renbr == FALSE && dist_done == FALSE )
{
Init_Distance( system, control, data, workspace, lists, out_control );
dist_done = TRUE;
}
#if defined(LOG_PERFORMANCE)
Update_Timing_Info( &time, &data->timing.init_dist );
#endif
if ( cm_done == FALSE )
{
Init_Matrix_Row_Indices( &workspace->H, system->max_cm_entries );
#if defined(NEUTRAL_TERRITORY)
if ( workspace->H.format == SYM_HALF_MATRIX )
{
Init_CM_Half_NT( system, control, data, workspace, lists, out_control );
}
else
{
Init_CM_Full_NT( system, control, data, workspace, lists, out_control );
}
#else
if ( workspace->H.format == SYM_HALF_MATRIX )
{
Init_CM_Half_FS( system, control, data, workspace, lists, out_control );
}
else
{
Init_CM_Full_FS( system, control, data, workspace, lists, out_control );
}
#endif
}
#if defined(LOG_PERFORMANCE)
Update_Timing_Info( &time, &data->timing.init_cm );
#endif
if ( bonds_done == FALSE )
{
for ( i = 0; i < system->total_cap; ++i )
{
workspace->total_bond_order[i] = 0.0;
}
for ( i = 0; i < system->total_cap; ++i )
{
rvec_MakeZero( workspace->dDeltap_self[i] );
}
Init_List_Indices( lists[BONDS], system->max_bonds );
Init_List_Indices( lists[HBONDS], system->max_hbonds );
if ( lists[FAR_NBRS]->format == HALF_LIST )
{
Init_Bond_Half( system, control, data, workspace, lists, out_control );
}
else
{
Init_Bond_Full( system, control, data, workspace, lists, out_control );
}
}
#if defined(LOG_PERFORMANCE)
Update_Timing_Info( &time, &data->timing.init_bond );
#endif
ret = (workspace->realloc.cm == FALSE
&& workspace->realloc.bonds == FALSE
&& workspace->realloc.hbonds == FALSE
? SUCCESS : FAILURE);
if ( workspace->realloc.cm == FALSE )
{
cm_done = TRUE;
}
if ( workspace->realloc.bonds == FALSE && workspace->realloc.hbonds == FALSE )
{
bonds_done = TRUE;
}
if ( ret == SUCCESS )
{
dist_done = FALSE;
cm_done = FALSE;
bonds_done = FALSE;
}
return ret;
}
static int Init_Forces_No_Charges( reax_system * const system, control_params * const control,
simulation_data * const data, storage * const workspace, reax_list ** const lists,
output_controls * const out_control )
{
int i, j, pj;
int start_i, end_i;
int type_i, type_j;
int btop_i;
int ihb, jhb, ihb_top;
int local, flag, renbr;
real cutoff;
reax_list *far_nbr_list, *bond_list, *hbond_list;
single_body_parameters *sbp_i, *sbp_j;
two_body_parameters *twbp;
reax_atom *atom_i, *atom_j;
int jhb_top;
int start_j, end_j;
int btop_j;
far_nbr_list = lists[FAR_NBRS];
bond_list = lists[BONDS];
hbond_list = lists[HBONDS];
for ( i = 0; i < system->n; ++i )
{
workspace->bond_mark[i] = 0;
}
for ( i = system->n; i < system->N; ++i )
{
/* put ghost atoms to an infinite distance */
workspace->bond_mark[i] = 1000;
}
renbr = (data->step - data->prev_steps) % control->reneighbor == 0 ? TRUE : FALSE;
for ( i = 0; i < system->N; ++i )
{
atom_i = &system->my_atoms[i];
type_i = atom_i->type;
start_i = Start_Index( i, far_nbr_list );
end_i = End_Index( i, far_nbr_list );
if ( far_nbr_list->format == HALF_LIST )
{
/* start at end because other atoms
* can add to this atom's list (half-list) */
btop_i = End_Index( i, bond_list );
}
else if ( far_nbr_list->format == FULL_LIST )
{
btop_i = Start_Index( i, bond_list );
}
else
{
btop_i = 0;
}
sbp_i = &system->reax_param.sbp[type_i];
ihb = NON_H_BONDING_ATOM;
ihb_top = -1;
if ( i < system->n )
{
local = TRUE;
cutoff = MAX( control->hbond_cut, control->bond_cut );
}
else
{
local = FALSE;
cutoff = control->bond_cut;
}
if ( local == TRUE && control->hbond_cut > 0.0 )
{
ihb = sbp_i->p_hbond;
if ( ihb == H_ATOM )
{
if ( far_nbr_list->format == HALF_LIST )
{
/* start at end because other atoms
* can add to this atom's list (half-list) */
ihb_top = End_Index( atom_i->Hindex, hbond_list );
}
else if ( far_nbr_list->format == FULL_LIST )
{
ihb_top = Start_Index( atom_i->Hindex, hbond_list );
}
}
else
{
ihb_top = -1;
}
}
/* update i-j distance - check if j is within cutoff */
for ( pj = start_i; pj < end_i; ++pj )
{
j = far_nbr_list->far_nbr_list.nbr[pj];
atom_j = &system->my_atoms[j];
if ( renbr == TRUE )
{
if ( far_nbr_list->far_nbr_list.d[pj] <= cutoff )
{
flag = TRUE;
}
else
{
flag = FALSE;
}
}
else
{
far_nbr_list->far_nbr_list.dvec[pj][0] = atom_j->x[0] - atom_i->x[0];
far_nbr_list->far_nbr_list.dvec[pj][1] = atom_j->x[1] - atom_i->x[1];
far_nbr_list->far_nbr_list.dvec[pj][2] = atom_j->x[2] - atom_i->x[2];
far_nbr_list->far_nbr_list.d[pj] = rvec_Norm_Sqr( far_nbr_list->far_nbr_list.dvec[pj] );
if ( far_nbr_list->far_nbr_list.d[pj] <= SQR(cutoff) )
{
far_nbr_list->far_nbr_list.d[pj] = SQRT( far_nbr_list->far_nbr_list.d[pj] );
flag = TRUE;
}
else
{
flag = FALSE;
}
}
if ( flag == TRUE )
{
type_j = atom_j->type;
sbp_j = &system->reax_param.sbp[type_j];
twbp = &system->reax_param.tbp[
index_tbp(type_i, type_j, system->reax_param.num_atom_types) ];
if ( local == TRUE )
{
/* hydrogen bond lists */
if ( control->hbond_cut > 0.0
&& (ihb == H_ATOM || ihb == H_BONDING_ATOM)
&& far_nbr_list->far_nbr_list.d[pj] <= control->hbond_cut )
{
jhb = sbp_j->p_hbond;
if ( ihb == H_ATOM && jhb == H_BONDING_ATOM )
{
hbond_list->hbond_list[ihb_top].nbr = j;
hbond_list->hbond_list[ihb_top].scl = 1;
hbond_list->hbond_list[ihb_top].ptr = pj;
++ihb_top;
}
/* only add to list for local j (far nbrs is half-list) */
else if ( far_nbr_list->format == HALF_LIST
&& ihb == H_BONDING_ATOM && jhb == H_ATOM )
{
jhb_top = End_Index( atom_j->Hindex, hbond_list );
hbond_list->hbond_list[jhb_top].nbr = i;
hbond_list->hbond_list[jhb_top].scl = -1;
hbond_list->hbond_list[jhb_top].ptr = pj;
Set_End_Index( atom_j->Hindex, jhb_top + 1, hbond_list );
}
}
}
/* uncorrected bond orders */
if ( //(workspace->bond_mark[i] < 3 || workspace->bond_mark[j] < 3) &&
far_nbr_list->far_nbr_list.d[pj] <= control->bond_cut
&& BOp( workspace, bond_list, control->bo_cut,
i, btop_i, far_nbr_list->far_nbr_list.nbr[pj],
&far_nbr_list->far_nbr_list.rel_box[pj], far_nbr_list->far_nbr_list.d[pj],
&far_nbr_list->far_nbr_list.dvec[pj], far_nbr_list->format,
sbp_i, sbp_j, twbp ) == TRUE )
{
++btop_i;
if ( workspace->bond_mark[j] > workspace->bond_mark[i] + 1 )
{
workspace->bond_mark[j] = workspace->bond_mark[i] + 1;
}
else if ( workspace->bond_mark[i] > workspace->bond_mark[j] + 1 )
{
workspace->bond_mark[i] = workspace->bond_mark[j] + 1;
}
}
}
}
Set_End_Index( i, btop_i, bond_list );
if ( local == TRUE && ihb == H_ATOM )
{
Set_End_Index( atom_i->Hindex, ihb_top, hbond_list );
}
}
if ( far_nbr_list->format == FULL_LIST )
{
/* set sym_index for bonds list (far_nbrs full list) */
for ( i = 0; i < system->N; ++i )
{
start_i = Start_Index( i, bond_list );
end_i = End_Index( i, bond_list );
for ( btop_i = start_i; btop_i < end_i; ++btop_i )
{
j = bond_list->bond_list[btop_i].nbr;
start_j = Start_Index( j, bond_list );
end_j = End_Index( j, bond_list );
for ( btop_j = start_j; btop_j < end_j; ++btop_j )
{
if ( bond_list->bond_list[btop_j].nbr == i )
{
bond_list->bond_list[btop_i].sym_index = btop_j;
break;
}
}
}
}
}
/* reallocation checks */
for ( i = 0; i < system->N; ++i )
{
if ( Num_Entries( i, bond_list ) > system->max_bonds[i] )
{
workspace->realloc.bonds = TRUE;
}
if ( i < system->n
&& system->reax_param.sbp[ system->my_atoms[i].type ].p_hbond == H_ATOM
&& Num_Entries( system->my_atoms[i].Hindex, hbond_list )
> system->max_hbonds[system->my_atoms[i].Hindex] )
{
workspace->realloc.hbonds = TRUE;
}
}
return (workspace->realloc.bonds == TRUE
|| workspace->realloc.hbonds == TRUE) ? FAILURE : SUCCESS;
}
void Estimate_Storages( reax_system * const system, control_params * const control,
reax_list ** const lists, storage *workspace, int realloc_cm,
int realloc_bonds, int * const matrix_dim, int cm_format )
{
if ( realloc_cm == TRUE )
{
Estimate_Storages_CM( system, control, lists, matrix_dim, cm_format );
}
if ( realloc_bonds == TRUE )
{
Estimate_Storages_Bonds( system, control, lists );
}
}
int Compute_Forces( reax_system * const system, control_params * const control,
simulation_data * const data, storage * const workspace,
reax_list ** const lists, output_controls * const out_control,
mpi_datatypes * const mpi_data )
{
int charge_flag, matrix_dim, ret, ret_mpi;
#if defined(LOG_PERFORMANCE)
real time;
time = Get_Time( );
#endif
/********* init forces ************/
if ( control->charge_freq
&& (data->step - data->prev_steps) % control->charge_freq == 0 )
{
charge_flag = TRUE;
}
else
{
charge_flag = FALSE;
}
if ( charge_flag == TRUE )
{
ret = Init_Forces( system, control, data, workspace, lists, out_control, mpi_data );
}
else
{
ret = Init_Forces_No_Charges( system, control, data, workspace, lists, out_control );
}
if ( ret != SUCCESS )
{
Estimate_Storages( system, control, lists, workspace,
workspace->realloc.cm,
(workspace->realloc.bonds == TRUE
|| workspace->realloc.hbonds == TRUE ? TRUE : FALSE),
&matrix_dim, workspace->H.format );
}
#if defined(LOG_PERFORMANCE)
Update_Timing_Info( &time, &data->timing.init_forces );
#endif
if ( ret == SUCCESS )
{
Compute_Bonded_Forces( system, control, data, workspace,
lists, out_control );
#if defined(LOG_PERFORMANCE)
Update_Timing_Info( &time, &data->timing.bonded );
#endif
/**************** charges ************************/
#if defined(PURE_REAX)
if ( charge_flag == TRUE )
{
Compute_Charges( system, control, data, workspace, out_control, mpi_data );
}
#if defined(LOG_PERFORMANCE)
Update_Timing_Info( &time, &data->timing.cm );
#endif
/* dynamically determine preconditioner recomputation rate */
if ( control->cm_solver_pre_comp_refactor == -1 )
{
/* root MPI process determines recomputation
* and broadcasts to other processes */
if ( system->my_rank == MASTER_NODE )
{
/* preconditioner just recomputed, record timings */
if ( data->refactor == TRUE )
{
data->refactor = FALSE;
data->timing.cm_last_pre_comp = data->timing.cm_solver_pre_comp;
data->timing.last_nbrs = data->timing.nbrs;
data->last_pc_step = data->step;
data->timing.cm_optimum = data->timing.cm_solver_pre_app
+ data->timing.cm_solver_spmv
+ data->timing.cm_solver_vector_ops
+ data->timing.cm_solver_orthog
+ data->timing.cm_solver_tri_solve;
data->timing.cm_total_loss = ZERO;
}
/* not enough initial guesses for spline extrapolation of initial guesses,
* so do not record metrics */
else if ( data->step <= 4 )
{
data->timing.cm_optimum = data->timing.cm_solver_pre_app
+ data->timing.cm_solver_spmv
+ data->timing.cm_solver_vector_ops
+ data->timing.cm_solver_orthog
+ data->timing.cm_solver_tri_solve;
}
else
{
/* record time lost from preconditioner degradation */
data->timing.cm_total_loss += data->timing.cm_solver_pre_app
+ data->timing.cm_solver_spmv
+ data->timing.cm_solver_vector_ops
+ data->timing.cm_solver_orthog
+ data->timing.cm_solver_tri_solve
- data->timing.cm_optimum;
/* cases:
* - check if cumulative loss exceeds recomputation time,
* and if so, schedule recomputation
* - since preconditioner recomputation is coupled with
* neighbor list regeneration, schedule precomputation
* recomputation if neighbor lists are recomputed */
if ( data->timing.cm_total_loss > data->timing.cm_last_pre_comp + data->timing.last_nbrs
|| data->step - data->last_pc_step + 1 >= control->reneighbor )
{
data->refactor = TRUE;
}
}
}
ret_mpi = MPI_Bcast( &data->refactor, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD );
Check_MPI_Error( ret_mpi, __FILE__, __LINE__ );
}
#endif //PURE_REAX
Compute_NonBonded_Forces( system, control, data, workspace,
lists, out_control, mpi_data );
#if defined(LOG_PERFORMANCE)
Update_Timing_Info( &time, &data->timing.nonb );
#endif
Compute_Total_Force( system, control, data, workspace, lists, mpi_data );
#if defined(LOG_PERFORMANCE)
Update_Timing_Info( &time, &data->timing.bonded );
#endif
#if defined(TEST_FORCES)
Print_Force_Files( system, control, data, workspace, lists, out_control, mpi_data );
#endif
}
return ret;
}