/*---------------------------------------------------------------------- SerialReax - Reax Force Field Simulator 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/>. ----------------------------------------------------------------------*/ #include "forces.h" #include "box.h" #include "bond_orders.h" #include "bonds.h" #include "charges.h" #include "hydrogen_bonds.h" #if defined(TEST_FORCES) #include "io_tools.h" #endif #include "list.h" #include "multi_body.h" #include "nonbonded.h" #include "system_props.h" #include "torsion_angles.h" #include "tool_box.h" #include "valence_angles.h" #include "vector.h" typedef enum { DIAGONAL = 0, OFF_DIAGONAL = 1, } MATRIX_ENTRY_POSITION; #if defined(TEST_FORCES) static int compare_bonds( const void *p1, const void *p2 ) { return ((bond_data *)p1)->nbr - ((bond_data *)p2)->nbr; } #endif void Init_Bonded_Force_Functions( control_params *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] = NULL; } control->intr_funcs[6] = NULL; control->intr_funcs[7] = NULL; control->intr_funcs[8] = NULL; control->intr_funcs[9] = NULL; } static void Compute_Bonded_Forces( reax_system *system, control_params *control, simulation_data *data, static_storage *workspace, reax_list **lists, output_controls *out_control ) { int i; #if defined(TEST_ENERGY) /* Mark beginning of a new timestep in each energy file */ fprintf( out_control->ebond, "step: %d\n%6s%6s%12s%12s%12s\n", data->step, "atom1", "atom2", "bo", "ebond", "total" ); fprintf( out_control->elp, "step: %d\n%6s%12s%12s%12s\n", data->step, "atom", "nlp", "elp", "total" ); fprintf( out_control->eov, "step: %d\n%6s%12s%12s\n", data->step, "atom", "eov", "total" ); fprintf( out_control->eun, "step: %d\n%6s%12s%12s\n", data->step, "atom", "eun", "total" ); fprintf( out_control->eval, "step: %d\n%6s%6s%6s%12s%12s%12s%12s%12s%12s\n", data->step, "atom1", "atom2", "atom3", "angle", "bo(12)", "bo(23)", "eval", "epen", "total" ); fprintf( out_control->epen, "step: %d\n%6s%6s%6s%12s%12s%12s%12s%12s\n", data->step, "atom1", "atom2", "atom3", "angle", "bo(12)", "bo(23)", "epen", "total" ); fprintf( out_control->ecoa, "step: %d\n%6s%6s%6s%12s%12s%12s%12s%12s\n", data->step, "atom1", "atom2", "atom3", "angle", "bo(12)", "bo(23)", "ecoa", "total" ); fprintf( out_control->ehb, "step: %d\n%6s%6s%6s%12s%12s%12s%12s%12s\n", data->step, "atom1", "atom2", "atom3", "r(23)", "angle", "bo(12)", "ehb", "total" ); fprintf( out_control->etor, "step: %d\n%6s%6s%6s%6s%12s%12s%12s%12s\n", data->step, "atom1", "atom2", "atom3", "atom4", "phi", "bo(23)", "etor", "total" ); fprintf( out_control->econ, "step:%d\n%6s%6s%6s%6s%12s%12s%12s%12s%12s%12s\n", data->step, "atom1", "atom2", "atom3", "atom4", "phi", "bo(12)", "bo(23)", "bo(34)", "econ", "total" ); #endif /* function calls for bonded interactions */ for ( i = 0; i < NUM_INTRS; i++ ) { if ( control->intr_funcs[i] != NULL ) { (control->intr_funcs[i])( system, control, data, workspace, lists, out_control ); } } #if defined(TEST_FORCES) /* function calls for printing bonded interactions */ for ( i = 0; i < NUM_INTRS; i++ ) { if ( control->print_intr_funcs[i] != NULL ) { (control->print_intr_funcs[i])( system, control, data, workspace, lists, out_control ); } } #endif } static void Compute_NonBonded_Forces( reax_system *system, control_params *control, simulation_data *data, static_storage *workspace, reax_list** lists, output_controls *out_control, int realloc ) { real t_start, t_elapsed; #if defined(TEST_ENERGY) fprintf( out_control->evdw, "step: %d\n%6s%6s%12s%12s%12s\n", data->step, "atom1", "atom2", "r12", "evdw", "total" ); fprintf( out_control->ecou, "step: %d\n%6s%6s%12s%12s%12s%12s%12s\n", data->step, "atom1", "atom2", "r12", "q1", "q2", "ecou", "total" ); #endif t_start = Get_Time( ); Compute_Charges( system, control, data, workspace, out_control, realloc ); t_elapsed = Get_Timing_Info( t_start ); data->timing.cm += t_elapsed; if ( control->cm_solver_pre_comp_refactor == -1 ) { if ( data->step <= 4 || is_refactoring_step( control, data ) ) { if ( is_refactoring_step( control, data ) ) { data->timing.cm_last_pre_comp = data->timing.cm_solver_pre_comp; } 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 = 0.0; } else { 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; } } 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 ); } #if defined(TEST_FORCES) Print_vdW_Coulomb_Forces( system, control, data, workspace, lists, out_control ); #endif } /* 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 *system, control_params *control, simulation_data *data, static_storage *workspace, reax_list **lists ) { int i; reax_list *bonds; bonds = lists[BONDS]; #if defined(_OPENMP) #pragma omp parallel default(shared) #endif { int pj; #if defined(_OPENMP) int j; #endif if ( control->compute_pressure == FALSE && (control->ensemble == NVE || control->ensemble == nhNVT || control->ensemble == bNVT) ) { #if defined(_OPENMP) #pragma omp for schedule(static) #endif for ( i = 0; i < system->N; ++i ) { for ( pj = Start_Index(i, bonds); pj < End_Index(i, bonds); ++pj ) { if ( i <= bonds->bond_list[pj].nbr ) { Add_dBond_to_Forces( i, pj, system, data, workspace, lists ); } } } } else if ( control->ensemble == sNPT || control->ensemble == iNPT || control->ensemble == aNPT || control->compute_pressure == TRUE ) { #if defined(_OPENMP) #pragma omp for schedule(static) #endif for ( i = 0; i < system->N; ++i ) { for ( pj = Start_Index(i, bonds); pj < End_Index(i, bonds); ++pj ) { if ( i <= bonds->bond_list[pj].nbr ) { Add_dBond_to_Forces_NPT( i, pj, system, data, workspace, lists ); } } } } #if defined(_OPENMP) /* reduction (sum) on thread-local force vectors */ #pragma omp for schedule(static) for ( i = 0; i < system->N; ++i ) { for ( j = 0; j < control->num_threads; ++j ) { rvec_Add( system->atoms[i].f, workspace->f_local[j * system->N + i] ); } } #endif } } static void Validate_Lists( static_storage *workspace, reax_list **lists, int step, int n, int Hmax, int Htop, int num_bonds, int num_hbonds ) { int i, flag; reax_list *bonds, *hbonds; bonds = lists[BONDS]; hbonds = lists[HBONDS]; /* far neighbors */ if ( Htop > Hmax * DANGER_ZONE ) { workspace->realloc.Htop = Htop; if ( Htop > Hmax ) { fprintf( stderr, "[ERROR] step%d - ran out of space on H matrix: Htop=%d, max = %d", step, Htop, Hmax ); exit( INSUFFICIENT_MEMORY ); } } /* bond list */ flag = -1; workspace->realloc.num_bonds = num_bonds; for ( i = 0; i < n - 1; ++i ) { if ( End_Index(i, bonds) >= Start_Index(i + 1, bonds) - 2 ) { workspace->realloc.bonds = 1; if ( End_Index(i, bonds) > Start_Index(i + 1, bonds) ) { flag = i; } } } if ( flag > -1 ) { fprintf( stderr, "[ERROR] step%d-bondchk failed: i=%d end(i)=%d str(i+1)=%d\n", step, flag, End_Index(flag, bonds), Start_Index(flag + 1, bonds) ); exit( INSUFFICIENT_MEMORY ); } if ( End_Index(i, bonds) >= bonds->total_intrs - 2 ) { workspace->realloc.bonds = 1; if ( End_Index(i, bonds) > bonds->total_intrs ) { fprintf( stderr, "[ERROR] step%d-bondchk failed: i=%d end(i)=%d bond_end=%d\n", step, flag, End_Index(i, bonds), bonds->total_intrs ); exit( INSUFFICIENT_MEMORY ); } } /* hbonds list */ if ( workspace->num_H > 0 ) { flag = -1; workspace->realloc.num_hbonds = num_hbonds; for ( i = 0; i < workspace->num_H - 1; ++i ) { if ( Num_Entries(i, hbonds) >= (Start_Index(i + 1, hbonds) - Start_Index(i, hbonds)) * DANGER_ZONE ) { workspace->realloc.hbonds = 1; if ( End_Index(i, hbonds) > Start_Index(i + 1, hbonds) ) { flag = i; } } } if ( flag > -1 ) { fprintf( stderr, "[ERROR] step%d-hbondchk failed: i=%d end(i)=%d str(i+1)=%d\n", step, flag, End_Index(flag, hbonds), Start_Index(flag + 1, hbonds) ); exit( INSUFFICIENT_MEMORY ); } if ( Num_Entries(i, hbonds) >= (hbonds->total_intrs - Start_Index(i, hbonds)) * DANGER_ZONE ) { workspace->realloc.hbonds = 1; if ( End_Index(i, hbonds) > hbonds->total_intrs ) { fprintf( stderr, "[ERROR] step%d-hbondchk failed: i=%d end(i)=%d hbondend=%d\n", step, flag, End_Index(i, hbonds), hbonds->total_intrs ); exit( INSUFFICIENT_MEMORY ); } } } } static inline real Init_Charge_Matrix_Entry_Tab( reax_system *system, control_params *control, LR_lookup_table **LR, int i, int j, real r_ij, MATRIX_ENTRY_POSITION pos ) { int r; real base, dif, 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: t = &LR[MIN( system->atoms[i].type, system->atoms[j].type )] [MAX( system->atoms[i].type, system->atoms[j].type )]; /* cubic spline interpolation */ r = (int)(r_ij * t->inv_dx); if ( r == 0 ) { ++r; } base = (real)(r + 1) * t->dx; dif = r_ij - base; val = ((t->ele[r].d * dif + t->ele[r].c) * dif + t->ele[r].b) * dif + t->ele[r].a; val *= EV_to_KCALpMOL / C_ELE; ret = ((i == j) ? 0.5 : 1.0) * val; break; case DIAGONAL: ret = system->reax_param.sbp[system->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( reax_system *system, control_params *control, static_storage *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[system->atoms[i].type][system->atoms[j].type].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->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: if ( system->num_molec_charge_constraints == 0 ) { 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 ) { /* total charge constraint on QM atoms */ #if defined(QMMM) if ( system->atoms[i].qmmm_mask == TRUE ) { #endif H->j[*Htop] = i; H->val[*Htop] = 1.0; H_sp->j[*H_sp_top] = i; H_sp->val[*H_sp_top] = 1.0; #if defined(QMMM) } #endif *Htop = *Htop + 1; *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; } else { for ( i = 0; i < system->num_molec_charge_constraints; ++i ) { H->start[system->N + i] = *Htop; H_sp->start[system->N + i] = *H_sp_top; for ( j = system->molec_charge_constraint_ranges[2 * i]; j <= system->molec_charge_constraint_ranges[2 * i + 1]; ++j ) { /* molecule charge constraint on QM atoms */ #if defined(QMMM) if ( system->atoms[j - 1].qmmm_mask == TRUE ) { #endif H->j[*Htop] = j - 1; H->val[*Htop] = 1.0; H_sp->j[*H_sp_top] = j - 1; H_sp->val[*H_sp_top] = 1.0; *Htop = *Htop + 1; *H_sp_top = *H_sp_top + 1; #if defined(QMMM) } #endif } /* explicit zeros on diagonals */ 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; } } break; case ACKS2_CM: X_diag = smalloc( sizeof(real) * system->N, __FILE__, __LINE__ ); 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[pj].d <= control->nonb_cut ) { j = far_nbr_list->far_nbr_list[pj].nbr; xcut = 0.5 * ( system->reax_param.sbp[ system->atoms[i].type ].b_s_acks2 + system->reax_param.sbp[ system->atoms[j].type ].b_s_acks2 ); if ( far_nbr_list->far_nbr_list[pj].d < xcut ) { d = far_nbr_list->far_nbr_list[pj].d / 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, __FILE__, __LINE__ ); break; default: break; } } /* Generate bond list (full format), hydrogen bond list (full format), * and charge matrix (half symmetric format) * from the far neighbors list (with distance updates, if necessary) */ static void Init_Forces( reax_system *system, control_params *control, simulation_data *data, static_storage *workspace, reax_list **lists, output_controls *out_control ) { int i, j, pj, target; int start_i, end_i; int type_i, type_j; int Htop, H_sp_top, btop_i, btop_j, num_bonds, num_hbonds; int ihb, jhb, ihb_top, jhb_top; int flag, flag_sp, val_flag, renbr; real r_ij, r2, val; real C12, C34, C56; real Cln_BOp_s, Cln_BOp_pi, Cln_BOp_pi2; real BO, BO_s, BO_pi, BO_pi2; sparse_matrix *H, *H_sp; reax_list *far_nbrs, *bonds, *hbonds; single_body_parameters *sbp_i, *sbp_j; two_body_parameters *twbp; far_neighbor_data *nbr_pj; reax_atom *atom_i, *atom_j; bond_data *ibond, *jbond; bond_order_data *bo_ij, *bo_ji; far_nbrs = lists[FAR_NBRS]; bonds = lists[BONDS]; hbonds = lists[HBONDS]; H = &workspace->H; H_sp = &workspace->H_sp; Htop = 0; H_sp_top = 0; num_bonds = 0; num_hbonds = 0; btop_i = 0; btop_j = 0; renbr = ((data->step - data->prev_steps) % control->reneighbor) == 0 ? TRUE : FALSE; for ( i = 0; i < system->N; ++i ) { atom_i = &system->atoms[i]; type_i = atom_i->type; start_i = Start_Index( i, far_nbrs ); end_i = End_Index( i, far_nbrs ); H->start[i] = Htop; H_sp->start[i] = H_sp_top; btop_i = End_Index( i, bonds ); sbp_i = &system->reax_param.sbp[type_i]; if ( control->hbond_cut > 0.0 ) { ihb = sbp_i->p_hbond; if ( ihb == H_ATOM ) { ihb_top = End_Index( workspace->hbond_index[i], hbonds ); } else { ihb_top = -1; } } else { ihb = NON_H_BONDING_ATOM; ihb_top = -1; } for ( pj = start_i; pj < end_i; ++pj ) { nbr_pj = &far_nbrs->far_nbr_list[pj]; j = nbr_pj->nbr; flag = FALSE; flag_sp = FALSE; #if defined(QMMM) if ( system->atoms[i].qmmm_mask == TRUE || system->atoms[j].qmmm_mask == TRUE ) { #endif /* check if reneighboring step -- * atomic distances just computed via * Verlet list, so use current distances */ if ( renbr == TRUE ) { if ( nbr_pj->d <= control->nonb_cut ) { flag = TRUE; if ( nbr_pj->d <= control->nonb_sp_cut ) { flag_sp = TRUE; } } } /* update atomic distances */ else { atom_j = &system->atoms[j]; nbr_pj->d = control->compute_atom_distance( &system->box, atom_i->x, atom_j->x, atom_i->rel_map, atom_j->rel_map, nbr_pj->rel_box, nbr_pj->dvec ); if ( nbr_pj->d <= control->nonb_cut ) { flag = TRUE; if ( nbr_pj->d <= control->nonb_sp_cut ) { flag_sp = TRUE; } } } if ( flag == TRUE ) { type_j = system->atoms[j].type; sbp_j = &system->reax_param.sbp[type_j]; twbp = &system->reax_param.tbp[type_i][type_j]; r_ij = nbr_pj->d; val = Init_Charge_Matrix_Entry( system, control, workspace, i, j, r_ij, OFF_DIAGONAL ); val_flag = FALSE; for ( target = H->start[i]; target < Htop; ++target ) { if ( H->j[target] == j ) { H->val[target] += val; val_flag = TRUE; break; } } if ( val_flag == FALSE ) { H->j[Htop] = j; H->val[Htop] = val; ++Htop; } /* H_sp matrix entry */ if ( flag_sp == TRUE ) { val_flag = FALSE; for ( target = H_sp->start[i]; target < H_sp_top; ++target ) { if ( H_sp->j[target] == j ) { H_sp->val[target] += val; val_flag = TRUE; break; } } if ( val_flag == FALSE ) { H_sp->j[H_sp_top] = j; H_sp->val[H_sp_top] = val; ++H_sp_top; } } #if defined(QMMM) if ( system->atoms[i].qmmm_mask == TRUE && system->atoms[j].qmmm_mask == TRUE ) { #endif /* Only non-dummy atoms can form bonds */ if ( system->atoms[i].is_dummy == FALSE && system->atoms[j].is_dummy == FALSE ) { /* hydrogen bond lists */ if ( control->hbond_cut > 0.0 && (ihb == H_ATOM || ihb == H_BONDING_ATOM) && nbr_pj->d <= control->hbond_cut ) { jhb = sbp_j->p_hbond; if ( ihb == H_ATOM && jhb == H_BONDING_ATOM ) { hbonds->hbond_list[ihb_top].nbr = j; hbonds->hbond_list[ihb_top].scl = 1; hbonds->hbond_list[ihb_top].ptr = nbr_pj; ++ihb_top; ++num_hbonds; } else if ( ihb == H_BONDING_ATOM && jhb == H_ATOM ) { jhb_top = End_Index( workspace->hbond_index[j], hbonds ); hbonds->hbond_list[jhb_top].nbr = i; hbonds->hbond_list[jhb_top].scl = -1; hbonds->hbond_list[jhb_top].ptr = nbr_pj; Set_End_Index( workspace->hbond_index[j], jhb_top + 1, hbonds ); ++num_hbonds; } } /* uncorrected bond orders */ if ( nbr_pj->d <= control->bond_cut ) { r2 = SQR( r_ij ); 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 ) { num_bonds += 2; /****** 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; jbond->nbr = i; ibond->d = r_ij; jbond->d = r_ij; rvec_Copy( ibond->dvec, nbr_pj->dvec ); rvec_Scale( jbond->dvec, -1, nbr_pj->dvec ); ivec_Copy( ibond->rel_box, nbr_pj->rel_box ); ivec_Scale( jbond->rel_box, -1, nbr_pj->rel_box ); ibond->dbond_index = btop_i; jbond->dbond_index = btop_i; ibond->sym_index = btop_j; jbond->sym_index = btop_i; ++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, -bo_ij->BO_s * Cln_BOp_s, ibond->dvec ); rvec_Scale( bo_ij->dln_BOp_pi, -bo_ij->BO_pi * Cln_BOp_pi, ibond->dvec ); rvec_Scale( bo_ij->dln_BOp_pi2, -bo_ij->BO_pi2 * Cln_BOp_pi2, ibond->dvec ); rvec_Scale( bo_ji->dln_BOp_s, -1., bo_ij->dln_BOp_s ); rvec_Scale( bo_ji->dln_BOp_pi, -1., bo_ij->dln_BOp_pi ); rvec_Scale( bo_ji->dln_BOp_pi2, -1., 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, -(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., 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 -= control->bo_cut; bo_ij->BO -= control->bo_cut; bo_ji->BO_s -= control->bo_cut; bo_ji->BO -= control->bo_cut; workspace->total_bond_order[i] += bo_ij->BO; workspace->total_bond_order[j] += bo_ji->BO; bo_ij->Cdbo = 0.0; bo_ij->Cdbopi = 0.0; bo_ij->Cdbopi2 = 0.0; bo_ji->Cdbo = 0.0; bo_ji->Cdbopi = 0.0; bo_ji->Cdbopi2 = 0.0; Set_End_Index( j, btop_j + 1, bonds ); } } } #if defined(QMMM) } #endif } #if defined(QMMM) } #endif } /* diagonal entry */ H->j[Htop] = i; H->val[Htop] = Init_Charge_Matrix_Entry( system, control, workspace, i, i, r_ij, DIAGONAL ); ++Htop; H_sp->j[H_sp_top] = i; H_sp->val[H_sp_top] = H->val[Htop - 1]; ++H_sp_top; Set_End_Index( i, btop_i, bonds ); if ( ihb == H_ATOM ) { Set_End_Index( workspace->hbond_index[i], ihb_top, hbonds ); } } Init_Charge_Matrix_Remaining_Entries( system, control, far_nbrs, H, H_sp, &Htop, &H_sp_top ); H->start[system->N_cm] = Htop; H_sp->start[system->N_cm] = H_sp_top; /* validate lists - decide if reallocation is required! */ Validate_Lists( workspace, lists, data->step, system->N, H->m, Htop, num_bonds, num_hbonds ); #if defined(TEST_FORCES) /* Calculate_dBO requires a sorted bonds list */ // for ( i = 0; i < bonds->n; ++i ) // { // if ( Num_Entries(i, bonds) > 0 ) // { // qsort( &bonds->bond_list[Start_Index(i, bonds)], // Num_Entries(i, bonds), sizeof(bond_data), compare_bonds ); // } // } #endif #if defined(DEBUG_FOCUS) fprintf( stderr, "step%d: Htop = %d, num_bonds = %d, num_hbonds = %d\n", data->step, Htop, num_bonds, num_hbonds ); #endif } static void Init_Forces_Tab( reax_system *system, control_params *control, simulation_data *data, static_storage *workspace, reax_list **lists, output_controls *out_control ) { int i, j, pj, target; int start_i, end_i; int type_i, type_j; int Htop, H_sp_top, btop_i, btop_j, num_bonds, num_hbonds; int ihb, jhb, ihb_top, jhb_top; int flag, flag_sp, val_flag, renbr; real r_ij, r2, val; real C12, C34, C56; real Cln_BOp_s, Cln_BOp_pi, Cln_BOp_pi2; real BO, BO_s, BO_pi, BO_pi2; sparse_matrix *H, *H_sp; reax_list *far_nbrs, *bonds, *hbonds; single_body_parameters *sbp_i, *sbp_j; two_body_parameters *twbp; far_neighbor_data *nbr_pj; reax_atom *atom_i, *atom_j; bond_data *ibond, *jbond; bond_order_data *bo_ij, *bo_ji; far_nbrs = lists[FAR_NBRS]; bonds = lists[BONDS]; hbonds = lists[HBONDS]; H = &workspace->H; H_sp = &workspace->H_sp; Htop = 0; H_sp_top = 0; num_bonds = 0; num_hbonds = 0; btop_i = 0; btop_j = 0; renbr = ((data->step - data->prev_steps) % control->reneighbor) == 0 ? TRUE : FALSE; for ( i = 0; i < system->N; ++i ) { atom_i = &system->atoms[i]; type_i = atom_i->type; start_i = Start_Index( i, far_nbrs ); end_i = End_Index( i, far_nbrs ); H->start[i] = Htop; H_sp->start[i] = H_sp_top; btop_i = End_Index( i, bonds ); sbp_i = &system->reax_param.sbp[type_i]; if ( control->hbond_cut > 0.0 ) { ihb = sbp_i->p_hbond; if ( ihb == H_ATOM ) { ihb_top = End_Index( workspace->hbond_index[i], hbonds ); } else { ihb_top = -1; } } else { ihb = NON_H_BONDING_ATOM; ihb_top = -1; } /* update i-j distance - check if j is within cutoff */ for ( pj = start_i; pj < end_i; ++pj ) { nbr_pj = &far_nbrs->far_nbr_list[pj]; j = nbr_pj->nbr; flag = FALSE; flag_sp = FALSE; #if defined(QMMM) if ( system->atoms[i].qmmm_mask == TRUE || system->atoms[j].qmmm_mask == TRUE ) { #endif /* check if reneighboring step -- * atomic distances just computed via * Verlet list, so use current distances */ if ( renbr == TRUE ) { if ( nbr_pj->d <= control->nonb_cut ) { flag = TRUE; if ( nbr_pj->d <= control->nonb_sp_cut ) { flag_sp = TRUE; } } } /* update atomic distances */ else { atom_j = &system->atoms[j]; nbr_pj->d = control->compute_atom_distance( &system->box, atom_i->x, atom_j->x, atom_i->rel_map, atom_j->rel_map, nbr_pj->rel_box, nbr_pj->dvec ); if ( nbr_pj->d <= control->nonb_cut ) { flag = TRUE; if ( nbr_pj->d <= control->nonb_sp_cut ) { flag_sp = TRUE; } } } if ( flag == TRUE ) { type_j = system->atoms[j].type; sbp_j = &system->reax_param.sbp[type_j]; twbp = &system->reax_param.tbp[type_i][type_j]; r_ij = nbr_pj->d; val = Init_Charge_Matrix_Entry( system, control, workspace, i, j, r_ij, OFF_DIAGONAL ); val_flag = FALSE; for ( target = H->start[i]; target < Htop; ++target ) { if ( H->j[target] == j ) { H->val[target] += val; val_flag = TRUE; break; } } if ( val_flag == FALSE ) { H->j[Htop] = j; H->val[Htop] = val; ++Htop; } /* H_sp matrix entry */ if ( flag_sp == TRUE ) { val_flag = FALSE; for ( target = H_sp->start[i]; target < H_sp_top; ++target ) { if ( H_sp->j[target] == j ) { H_sp->val[target] += val; val_flag = TRUE; break; } } if ( val_flag == FALSE ) { H_sp->j[H_sp_top] = j; H_sp->val[H_sp_top] = val; ++H_sp_top; } } #if defined(QMMM) if ( system->atoms[i].qmmm_mask == TRUE && system->atoms[j].qmmm_mask == TRUE ) { #endif /* Only non-dummy atoms can form bonds */ if ( system->atoms[i].is_dummy == FALSE && system->atoms[j].is_dummy == FALSE ) { /* hydrogen bond lists */ if ( control->hbond_cut > 0.0 && (ihb == H_ATOM || ihb == H_BONDING_ATOM) && nbr_pj->d <= control->hbond_cut ) { jhb = sbp_j->p_hbond; if ( ihb == H_ATOM && jhb == H_BONDING_ATOM ) { hbonds->hbond_list[ihb_top].nbr = j; hbonds->hbond_list[ihb_top].scl = 1; hbonds->hbond_list[ihb_top].ptr = nbr_pj; ++ihb_top; ++num_hbonds; } else if ( ihb == H_BONDING_ATOM && jhb == H_ATOM ) { jhb_top = End_Index( workspace->hbond_index[j], hbonds ); hbonds->hbond_list[jhb_top].nbr = i; hbonds->hbond_list[jhb_top].scl = -1; hbonds->hbond_list[jhb_top].ptr = nbr_pj; Set_End_Index( workspace->hbond_index[j], jhb_top + 1, hbonds ); ++num_hbonds; } } /* uncorrected bond orders */ if ( nbr_pj->d <= control->bond_cut ) { r2 = SQR( r_ij ); 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 ) { num_bonds += 2; /****** 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; jbond->nbr = i; ibond->d = r_ij; jbond->d = r_ij; rvec_Copy( ibond->dvec, nbr_pj->dvec ); rvec_Scale( jbond->dvec, -1, nbr_pj->dvec ); ivec_Copy( ibond->rel_box, nbr_pj->rel_box ); ivec_Scale( jbond->rel_box, -1, nbr_pj->rel_box ); ibond->dbond_index = btop_i; jbond->dbond_index = btop_i; ibond->sym_index = btop_j; jbond->sym_index = btop_i; ++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, -bo_ij->BO_s * Cln_BOp_s, ibond->dvec ); rvec_Scale( bo_ij->dln_BOp_pi, -bo_ij->BO_pi * Cln_BOp_pi, ibond->dvec ); rvec_Scale( bo_ij->dln_BOp_pi2, -bo_ij->BO_pi2 * Cln_BOp_pi2, ibond->dvec ); rvec_Scale( bo_ji->dln_BOp_s, -1., bo_ij->dln_BOp_s ); rvec_Scale( bo_ji->dln_BOp_pi, -1., bo_ij->dln_BOp_pi ); rvec_Scale( bo_ji->dln_BOp_pi2, -1., 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, -(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., 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 -= control->bo_cut; bo_ij->BO -= control->bo_cut; bo_ji->BO_s -= control->bo_cut; bo_ji->BO -= control->bo_cut; workspace->total_bond_order[i] += bo_ij->BO; workspace->total_bond_order[j] += bo_ji->BO; bo_ij->Cdbo = 0.0; bo_ij->Cdbopi = 0.0; bo_ij->Cdbopi2 = 0.0; bo_ji->Cdbo = 0.0; bo_ji->Cdbopi = 0.0; bo_ji->Cdbopi2 = 0.0; Set_End_Index( j, btop_j + 1, bonds ); } } } #if defined(QMMM) } #endif } #if defined(QMMM) } #endif } /* diagonal entry */ H->j[Htop] = i; H->val[Htop] = Init_Charge_Matrix_Entry( system, control, workspace, i, i, r_ij, DIAGONAL ); ++Htop; H_sp->j[H_sp_top] = i; H_sp->val[H_sp_top] = H->val[Htop - 1]; ++H_sp_top; Set_End_Index( i, btop_i, bonds ); if ( ihb == H_ATOM ) { Set_End_Index( workspace->hbond_index[i], ihb_top, hbonds ); } } Init_Charge_Matrix_Remaining_Entries( system, control, far_nbrs, H, H_sp, &Htop, &H_sp_top ); H->start[system->N_cm] = Htop; H_sp->start[system->N_cm] = H_sp_top; /* validate lists - decide if reallocation is required! */ Validate_Lists( workspace, lists, data->step, system->N, H->m, Htop, num_bonds, num_hbonds ); #if defined(DEBUG_FOCUS) fprintf( stderr, "step%d: Htop = %d, num_bonds = %d, num_hbonds = %d\n", data->step, Htop, num_bonds, num_hbonds ); //Print_Bonds( system, bonds, "sbonds.out" ); //Print_Bond_List2( system, bonds, "sbonds.out" ); //Print_Sparse_Matrix2( H, "H.out", NULL ); #endif } void Estimate_Storage_Sizes( reax_system *system, control_params *control, reax_list **lists, int *Htop, int *hb_top, int *bond_top, int *num_3body ) { int i, j, pj; int start_i, end_i; int type_i, type_j; int ihb, jhb; real r_ij; real C12, C34, C56; real BO, BO_s, BO_pi, BO_pi2; reax_list *far_nbrs; single_body_parameters *sbp_i, *sbp_j; two_body_parameters *twbp; far_neighbor_data *nbr_pj; reax_atom *atom_i, *atom_j; far_nbrs = lists[FAR_NBRS]; for ( i = 0; i < far_nbrs->n; ++i ) { atom_i = &system->atoms[i]; type_i = atom_i->type; start_i = Start_Index( i, far_nbrs ); end_i = End_Index( i, far_nbrs ); sbp_i = &system->reax_param.sbp[type_i]; ihb = sbp_i->p_hbond; /* update i-j distance - check if j is within cutoff */ for ( pj = start_i; pj < end_i; ++pj ) { nbr_pj = &far_nbrs->far_nbr_list[pj]; if ( nbr_pj->d <= control->nonb_cut ) { j = nbr_pj->nbr; atom_j = &system->atoms[j]; type_j = atom_j->type; sbp_j = &system->reax_param.sbp[type_j]; twbp = &system->reax_param.tbp[type_i][type_j]; ++(*Htop); /* hydrogen bond lists */ if ( control->hbond_cut > 0.0 && (ihb == H_ATOM || ihb == H_BONDING_ATOM) && nbr_pj->d <= control->hbond_cut ) { jhb = sbp_j->p_hbond; if ( ihb == H_ATOM && jhb == H_BONDING_ATOM ) { ++hb_top[i]; } else if ( ihb == H_BONDING_ATOM && jhb == H_ATOM ) { ++hb_top[j]; } } /* uncorrected bond orders */ if ( nbr_pj->d <= control->bond_cut ) { r_ij = nbr_pj->d; 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 ) { ++bond_top[i]; ++bond_top[j]; } } } } } *Htop += system->N; *Htop *= SAFE_ZONE; for ( i = 0; i < system->N; ++i ) { hb_top[i] = MAX( hb_top[i] * SAFE_HBONDS, MIN_HBONDS ); *num_3body += SQR( bond_top[i] ); bond_top[i] = MAX( bond_top[i] * 2, MIN_BONDS ); } *num_3body *= SAFE_ZONE; } void Compute_Forces( reax_system *system, control_params *control, simulation_data *data, static_storage *workspace, reax_list** lists, output_controls *out_control, int realloc ) { real t_start, t_elapsed; t_start = Get_Time( ); if ( control->tabulate <= 0 ) { Init_Forces( system, control, data, workspace, lists, out_control ); } else { Init_Forces_Tab( system, control, data, workspace, lists, out_control ); } t_elapsed = Get_Timing_Info( t_start ); data->timing.init_forces += t_elapsed; t_start = Get_Time( ); Compute_Bonded_Forces( system, control, data, workspace, lists, out_control ); t_elapsed = Get_Timing_Info( t_start ); data->timing.bonded += t_elapsed; t_start = Get_Time( ); Compute_NonBonded_Forces( system, control, data, workspace, lists, out_control, realloc ); t_elapsed = Get_Timing_Info( t_start ); data->timing.nonb += t_elapsed; Compute_Total_Force( system, control, data, workspace, lists ); #if defined(DEBUG_FOCUS) //Print_Total_Force( system, control, data, workspace, lists, out_control ); #endif #if defined(TEST_FORCES) Print_Total_Force( system, control, data, workspace, lists, out_control ); Compare_Total_Forces( system, control, data, workspace, lists, out_control ); #endif }