From 916ed7176072427013204b25ee0ccefc2353cf15 Mon Sep 17 00:00:00 2001 From: jrgissing <jrgiss05@gmail.com> Date: Sun, 6 May 2018 00:34:03 -0600 Subject: [PATCH] fix bond/react final touches --- doc/src/fix_bond_react.txt | 75 ++-- .../nylon,6-6_melt/in.large_nylon_melt | 4 +- .../misc/bond_react/tiny_nylon/in.tiny_nylon | 4 +- src/USER-MISC/fix_bond_react.cpp | 423 +++++++++++------- src/USER-MISC/fix_bond_react.h | 9 +- 5 files changed, 306 insertions(+), 209 deletions(-) diff --git a/doc/src/fix_bond_react.txt b/doc/src/fix_bond_react.txt index b5f9a73084..13c4f49b46 100644 --- a/doc/src/fix_bond_react.txt +++ b/doc/src/fix_bond_react.txt @@ -11,9 +11,9 @@ fix bond/react command :h3 [Syntax:] fix ID group-ID bond/react common_keyword values ... - react react-ID react-group-ID Nevery Rmin template-ID(pre-reacted) template-ID(post-reacted) map_file individual_keyword values ... - react react-ID react-group-ID Nevery Rmin template-ID(pre-reacted) template-ID(post-reacted) map_file individual_keyword values ... - react react-ID react-group-ID Nevery Rmin template-ID(pre-reacted) template-ID(post-reacted) map_file individual_keyword values ... + react react-ID react-group-ID Nevery Rmin Rmax template-ID(pre-reacted) template-ID(post-reacted) map_file individual_keyword values ... + react react-ID react-group-ID Nevery Rmin Rmax template-ID(pre-reacted) template-ID(post-reacted) map_file individual_keyword values ... + react react-ID react-group-ID Nevery Rmin Rmax template-ID(pre-reacted) template-ID(post-reacted) map_file individual_keyword values ... ... :pre ID, group-ID are documented in "fix"_fix.html command. Group-ID is ignored. :ulb,l @@ -29,7 +29,8 @@ react = mandatory argument indicating new reaction specification react-ID = user-assigned name for the reaction react-group-ID = only atoms in this group are available for the reaction Nevery = attempt reaction every this many steps :l - Rmin = bonding pair atoms separated by less than Rmin can initiate reaction (distance units) :l + Rmin = bonding pair atoms must be separated by more than Rmin to initiate reaction (distance units) :l + Rmax = bonding pair atoms must be separated by less than Rmax to initiate reaction (distance units) :l template-ID(pre-reacted) = ID of a molecule template containing pre-reaction topology :l template-ID(post-reacted) = ID of a molecule template containing post-reaction topology :l map_file = name of file specifying corresponding atomIDs in the pre- and post-reacted templates :l @@ -46,15 +47,15 @@ react = mandatory argument indicating new reaction specification molecule mol1 pre_reacted_topology.txt molecule mol2 post_reacted_topology.txt -fix 5 all bond/react stabilization no react myrxn1 all 1 3.25 mol1 mol2 map_file.txt +fix 5 all bond/react stabilization no react myrxn1 all 1 0 3.25 mol1 mol2 map_file.txt molecule mol1 pre_reacted_rxn1.txt molecule mol2 post_reacted_rxn1.txt molecule mol3 pre_reacted_rxn2.txt molecule mol4 post_reacted_rxn2.txt fix 5 all bond/react stabilization yes nvt_grp .03 & - react myrxn1 all 1 3.25 mol1 mol2 map_file_rxn1.txt prob 0.50 12345 & - react myrxn2 all 1 2.75 mol3 mol4 map_file_rxn2.txt prob 0.25 12345 + react myrxn1 all 1 0 3.25 mol1 mol2 map_file_rxn1.txt prob 0.50 12345 & + react myrxn2 all 1 0 2.75 mol3 mol4 map_file_rxn2.txt prob 0.25 12345 fix 6 nvt_grp nvt temp 300 300 100 # system-wide thermostat must be defined after bond/react :pre [Description:] @@ -101,9 +102,14 @@ The group-ID set using the {stabilization} keyword should be a previously unused group-ID. The fix bond/react command creates a "dynamic group"_group.html of this name that excludes reacting atoms. This dynamic group-ID should then be used by a subsequent system-wide -time integrator, as shown in the second example above. It is necessary -to place the time integration command after the fix bond/react command -due to the internal dynamic grouping performed by fix bond/react. +time integrator, as shown in the second example above. It is currently +necessary to place the time integration command after the fix +bond/react command due to the internal dynamic grouping performed by +fix bond/react. + +NOTE: The internally created group currently applies to all atoms in +the system, i.e. you should generally not have a separate thermostat +which acts on the 'all' group. The following comments pertain to each 'react' argument: @@ -118,21 +124,20 @@ modified to match the post-reaction template. A bonding atom pair will be identified if several conditions are met. First, a pair of atoms within the specified react-group-ID of type -typei and typej must be within a distance Rmin of each other. The atom -types typei and typej are specified in the pre- and post-reaction -templates. The distance calculation uses the pair neighbor list, -therefore bonded neighbor exclusions may prevent a reaction between -1st, 2nd or 3rd bonded neighbor atoms. If multiple bonding atom pairs -are identified for an atom, the closest bonding atom partner is set as -its "nearest" bonding partner. Then, if both an atomi and atomj have -each other as their nearest bonding partners, these two atoms are -identified as the bonding atom pair of the reaction site. Once this -unique bonding atom pair is identified for each reaction, there could -two or more reactions that involve a given atom on the same timestep. -If this is the case, only one such reaction is permitted to occur. -This reaction is chosen randomly from all potential reactions. This -capability allows e.g. for different reaction pathways to proceed from -identical reaction sites with user-specified probabilities. +typei and typej must separated by a distance between Rmin and Rmax. It +is possible that multiple bonding atom pairs are identified: if the +bonding atoms in the pre-reacted template are not 1-2, 1-3, or 1-4 +neighbors, the closest bonding atom partner is set as its bonding +partner; otherwise, the farthest potential partner is chosen. Then, if +both an atomi and atomj have each other as their nearest bonding +partners, these two atoms are identified as the bonding atom pair of +the reaction site. Once this unique bonding atom pair is identified +for each reaction, there could two or more reactions that involve a +given atom on the same timestep. If this is the case, only one such +reaction is permitted to occur. This reaction is chosen randomly from +all potential reactions. This capability allows e.g. for different +reaction pathways to proceed from identical reaction sites with +user-specified probabilities. The pre-reacted molecule template is specified by a molecule command. This molecule template file contains a sample reaction site and its @@ -262,9 +267,11 @@ angles, dihedrals or impropers are supported. A few capabilities to note: 1) You may specify as many 'react' arguments as desired. For example, you could break down a complicated reaction mechanism into several reaction steps, each defined by its -own 'react' argument. 2) While typically a bond is formed between the -bonding atom pairs specified in the pre-reacted molecule template, -this is not required. +own 'react' argument. 2) While typically a bond is formed or removed +between the bonding atom pairs specified in the pre-reacted molecule +template, this is not required. 3) By reversing the order of the pre- +and post- reacted molecule templates in another 'react' argument, you +can allow for the possibility of one or more reverse reactions. The optional keywords deal with the probability of a given reaction occurring as well as the stable equilibration of each reaction site as @@ -300,14 +307,14 @@ reaction: fix 1 bond_react_MASTER_group temp/rescale 1 300 300 10 1 NOTE: This command must be added after the fix bond/react command, and -will apply to all reaction steps. +will apply to all reactions. Computationally, each timestep this fix operates, it loops over -neighbor lists and computes distances between pairs of atoms in the -list. It also communicates between neighboring processors to -coordinate which bonds are created. All of these operations increase -the cost of a timestep. Thus you should be cautious about invoking -this fix too frequently. +neighbor lists (for bond-forming reactions) and computes distances +between pairs of atoms in the list. It also communicates between +neighboring processors to coordinate which bonds are created and/or +removed. All of these operations increase the cost of a timestep. Thus +you should be cautious about invoking this fix too frequently. You can dump out snapshots of the current bond topology via the dump local command. diff --git a/examples/USER/misc/bond_react/nylon,6-6_melt/in.large_nylon_melt b/examples/USER/misc/bond_react/nylon,6-6_melt/in.large_nylon_melt index 072a8e3c45..f2dc506dde 100644 --- a/examples/USER/misc/bond_react/nylon,6-6_melt/in.large_nylon_melt +++ b/examples/USER/misc/bond_react/nylon,6-6_melt/in.large_nylon_melt @@ -32,8 +32,8 @@ thermo 50 # dump 1 all xyz 100 test_vis.xyz fix myrxns all bond/react stabilization yes statted_grp .03 & - react rxn1 all 1 2.9 mol1 mol2 rxn1_stp1_map & - react rxn2 all 1 5 mol3 mol4 rxn1_stp2_map + react rxn1 all 1 0.0 2.9 mol1 mol2 rxn1_stp1_map & + react rxn2 all 1 0.0 5.0 mol3 mol4 rxn1_stp2_map # stable at 800K fix 1 statted_grp nvt temp 800 800 100 diff --git a/examples/USER/misc/bond_react/tiny_nylon/in.tiny_nylon b/examples/USER/misc/bond_react/tiny_nylon/in.tiny_nylon index 3e21f69331..1f7e9c42b7 100644 --- a/examples/USER/misc/bond_react/tiny_nylon/in.tiny_nylon +++ b/examples/USER/misc/bond_react/tiny_nylon/in.tiny_nylon @@ -33,8 +33,8 @@ thermo 50 # dump 1 all xyz 1 test_vis.xyz fix myrxns all bond/react stabilization yes statted_grp .03 & - react rxn1 all 1 2.9 mol1 mol2 rxn1_stp1_map & - react rxn2 all 1 5 mol3 mol4 rxn1_stp2_map + react rxn1 all 1 0.0 2.9 mol1 mol2 rxn1_stp1_map & + react rxn2 all 1 0.0 5.0 mol3 mol4 rxn1_stp2_map fix 1 statted_grp nvt temp 300 300 100 diff --git a/src/USER-MISC/fix_bond_react.cpp b/src/USER-MISC/fix_bond_react.cpp index b3c92eed67..c3d7866334 100644 --- a/src/USER-MISC/fix_bond_react.cpp +++ b/src/USER-MISC/fix_bond_react.cpp @@ -43,6 +43,17 @@ Contributing Author: Jacob Gissinger (jacob.gissinger@colorado.edu) using namespace LAMMPS_NS; using namespace FixConst; +static const char cite_fix_bond_react[] = + "fix bond/react:\n\n" + "@Article{Gissinger17,\n" + " author = {J. R. Gissinger, B. D. Jensen, K. E. Wise},\n" + " title = {Modeling chemical reactions in classical molecular dynamics simulations},\n" + " journal = {Polymer},\n" + " year = 2017,\n" + " volume = 128,\n" + " pages = {211--217}\n" + "}\n\n"; + #define BIG 1.0e20 #define DELTA 16 #define MAXLINE 256 @@ -62,6 +73,8 @@ enum{ACCEPT,REJECT,PROCEED,CONTINUE,GUESSFAIL,RESTORE}; FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg) { + if (lmp->citeme) lmp->citeme->add(cite_fix_bond_react); + fix1 = NULL; fix2 = NULL; @@ -71,7 +84,6 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : MPI_Comm_size(world,&nprocs); attempted_rxn = 0; - ghostcheck_flag = 0; force_reneighbor = 1; next_reneighbor = -1; vector_flag = 1; @@ -127,7 +139,7 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : // this looks excessive // the price of vectorization (all reactions in one command)? memory->create(nevery,nreacts,"bond/react:nevery"); - memory->create(cutsq,nreacts,"bond/react:cutsq"); + memory->create(cutsq,nreacts,2,"bond/react:cutsq"); memory->create(unreacted_mol,nreacts,"bond/react:unreacted_mol"); memory->create(reacted_mol,nreacts,"bond/react:reacted_mol"); memory->create(fraction,nreacts,"bond/react:fraction"); @@ -138,6 +150,7 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : memory->create(jatomtype,nreacts,"bond/react:jatomtype"); memory->create(ibonding,nreacts,"bond/react:ibonding"); memory->create(jbonding,nreacts,"bond/react:jbonding"); + memory->create(closeneigh,nreacts,"bond/react:closeneigh"); memory->create(groupbits,nreacts,"bond/react:groupbits"); memory->create(reaction_count,nreacts,"bond/react:reaction_count"); memory->create(local_rxn_count,nreacts,"bond/react:local_rxn_count"); @@ -176,7 +189,11 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : double cutoff = force->numeric(FLERR,arg[iarg++]); if (cutoff < 0.0) error->all(FLERR,"Illegal fix bond/react command 0.5"); - cutsq[rxn] = cutoff*cutoff; + cutsq[rxn][0] = cutoff*cutoff; + + cutoff = force->numeric(FLERR,arg[iarg++]); + if (cutoff < 0.0) error->all(FLERR,"Illegal fix bond/react command 0.55"); + cutsq[rxn][1] = cutoff*cutoff; unreacted_mol[rxn] = atom->find_molecule(arg[iarg++]); if (unreacted_mol[rxn] == -1) error->all(FLERR,"Unreacted molecule template ID for " @@ -241,6 +258,23 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : if (atom->molecular != 1) error->all(FLERR,"Cannot use fix bond/react with non-molecular systems"); + // check if bonding atoms are 1-2, 1-3, or 1-4 bonded neighbors + // if so, we don't need non-bonded neighbor list + for (int myrxn = 0; myrxn < nreacts; myrxn++) { + closeneigh[myrxn] = -1; // indicates will search non-bonded neighbors + onemol = atom->molecules[unreacted_mol[myrxn]]; + for (int k = 0; k < onemol->nspecial[ibonding[myrxn]-1][2]; k++) { + if (onemol->special[ibonding[myrxn]-1][k] == jbonding[myrxn]) { + closeneigh[myrxn] = 2; // index for 1-4 neighbor + if (k < onemol->nspecial[ibonding[myrxn]-1][1]) + closeneigh[myrxn] = 1; // index for 1-3 neighbor + if (k < onemol->nspecial[ibonding[myrxn]-1][0]) + closeneigh[myrxn] = 0; // index for 1-2 neighbor + break; + } + } + } + // initialize Marsaglia RNG with processor-unique seed random = new class RanMars*[nreacts]; @@ -259,6 +293,7 @@ FixBondReact::FixBondReact(LAMMPS *lmp, int narg, char **arg) : nmax = 0; partner = finalpartner = NULL; distsq = NULL; + probability = NULL; maxcreate = 0; created = NULL; local_ncreate = NULL; @@ -302,6 +337,7 @@ FixBondReact::~FixBondReact() memory->destroy(local_ncreate); memory->destroy(ncreate); memory->destroy(distsq); + memory->destroy(probability); memory->destroy(created); memory->destroy(edge); memory->destroy(equivalences); @@ -320,6 +356,7 @@ FixBondReact::~FixBondReact() memory->destroy(jatomtype); memory->destroy(ibonding); memory->destroy(jbonding); + memory->destroy(closeneigh); memory->destroy(groupbits); memory->destroy(reaction_count); memory->destroy(local_rxn_count); @@ -372,7 +409,7 @@ it will have the name 'i_limit_tags' and will be intitialized to 0 (not in group void FixBondReact::post_constructor() { - //let's add the limit_tags per-atom property fix + // let's add the limit_tags per-atom property fix int len = strlen("per_atom_props") + 1; id_fix2 = new char[len]; strcpy(id_fix2,"per_atom_props"); @@ -521,7 +558,7 @@ void FixBondReact::init() // check cutoff for iatomtype,jatomtype for (int i = 0; i < nreacts; i++) { - if (force->pair == NULL || cutsq[i] > force->pair->cutsq[iatomtype[i]][jatomtype[i]]) + if (force->pair == NULL || cutsq[i][1] > force->pair->cutsq[iatomtype[i]][jatomtype[i]]) error->all(FLERR,"Fix bond/react cutoff is longer than pairwise cutoff"); } @@ -548,10 +585,6 @@ void FixBondReact::init_list(int id, NeighList *ptr) void FixBondReact::post_integrate() { - int inum,jnum,itype,jtype,possible; - double xtmp,ytmp,ztmp,delx,dely,delz,rsq; - int *ilist,*jlist,*numneigh,**firstneigh; - // check if any reactions could occur on this timestep int nevery_check = 1; for (int i = 0; i < nreacts; i++) { @@ -578,7 +611,6 @@ void FixBondReact::post_integrate() comm->forward_comm(); // resize bond partner list and initialize it - // probability array overlays distsq array // needs to be atom->nmax in length if (atom->nmax > nmax) { @@ -587,13 +619,14 @@ void FixBondReact::post_integrate() memory->destroy(distsq); memory->destroy(local_ncreate); memory->destroy(ncreate); + memory->destroy(probability); nmax = atom->nmax; memory->create(partner,nmax,"bond/react:partner"); memory->create(finalpartner,nmax,"bond/react:finalpartner"); - memory->create(distsq,nmax,"bond/react:distsq"); + memory->create(distsq,nmax,2,"bond/react:distsq"); memory->create(local_ncreate,nreacts,"bond/react:local_ncreate"); memory->create(ncreate,nreacts,"bond/react:ncreate"); - probability = distsq; + memory->create(probability,nmax,"bond/react:probability"); } // reset create counts @@ -605,129 +638,28 @@ void FixBondReact::post_integrate() int nlocal = atom->nlocal; int nall = atom->nlocal + atom->nghost; - // NOTE: this might be faster if we remembered neighbor distances from - // previous timestep and used those --JG - // loop over neighbors of my atoms // each atom sets one closest eligible partner atom ID to bond with - double **x = atom->x; tagint *tag = atom->tag; - int **nspecial = atom->nspecial; - tagint **special = atom->special; - int *mask = atom->mask; int *type = atom->type; neighbor->build_one(list,1); - inum = list->inum; - ilist = list->ilist; - numneigh = list->numneigh; - firstneigh = list->firstneigh; - - // per-atom property indicating if in bond/react master group - int flag; - int index1 = atom->find_custom("limit_tags",flag); - int *i_limit_tags = atom->ivector[index1]; int i,j; - for (int myrxn = 0; myrxn < nreacts; myrxn++) { + for (rxnID = 0; rxnID < nreacts; rxnID++) { for (int ii = 0; ii < nall; ii++) { partner[ii] = 0; finalpartner[ii] = 0; - distsq[ii] = BIG; + distsq[ii][0] = 0.0; + distsq[ii][1] = BIG; } - for (int ii = 0; ii < inum; ii++) { // inum vs nlocal - i = ilist[ii]; - if (!(mask[i] & groupbits[myrxn])) continue; - if (i_limit_tags[i] != 0) continue; - itype = type[i]; - xtmp = x[i][0]; - ytmp = x[i][1]; - ztmp = x[i][2]; - jlist = firstneigh[i]; - jnum = numneigh[i]; - - for (int jj = 0; jj < jnum; jj++) { - j = jlist[jj]; - j &= NEIGHMASK; - - if (!(mask[j] & groupbits[myrxn])) { - continue; - } - - if (i_limit_tags[j] != 0) { - continue; - } - - jtype = type[j]; - possible = 0; - - if (itype == iatomtype[myrxn] && jtype == jatomtype[myrxn]) { - possible = 1; - } else if (itype == jatomtype[myrxn] && jtype == iatomtype[myrxn]) { - possible = 1; - } - - if (possible == 0) continue; - - for (int k = 0; k < nspecial[i][0]; k++) - if (special[i][k] == tag[j]) possible = 0; - if (!possible) continue; - - // NOTE(for below): certain neighbor list settings prevent 3-cycles anyway! - // e.g., in my examples, must use kspace command to include 1-3 neighbors for consideration here - - // do not allow a three-membered ring to be created (by the new bond) - // check 1-3 neighbors of atom I - for (int k = nspecial[i][0]; k < nspecial[i][1]; k++) - if (special[i][k] == tag[j]) possible = 0; - if (possible == 0) { - continue; - } - - // do not allow a four-membered ring to be created (by the new bond) - // check 1-4 neighbors of atom I -> probably make an option - - /* - for (k = nspecial[i][1]; k < nspecial[i][2]; k++) - if (special[i][k] == tag[j]) { - possible = 0; - } - if (possible == 0) continue; - */ - - // do not allow 5 membered rings -> probably make this an option - - /* - for (k = nspecial[i][1]; k < nspecial[i][2]; k++) { - for (m = nspecial[atom->map(special[i][k])][1]; m < nspecial[atom->map(special[i][k])][2]; m++) { - if (special[atom->map(special[i][k])][m] == tag[j]) possible = 0; - / } - } - if (possible == 0) continue; - */ - - delx = xtmp - x[j][0]; - dely = ytmp - x[j][1]; - delz = ztmp - x[j][2]; - rsq = delx*delx + dely*dely + delz*delz; - - if (rsq >= cutsq[myrxn]) { - continue; - } - if (rsq < distsq[i]) { - partner[i] = tag[j]; - distsq[i] = rsq; - } - if (rsq < distsq[j]) { - partner[j] = tag[i]; - distsq[j] = rsq; - } - } - } + // fork between far and close_partner here + if (closeneigh[rxnID] < 0) far_partner(); + else close_partner(); // reverse comm of distsq and partner // not needed if newton_pair off since I,J pair was seen by both procs @@ -739,9 +671,9 @@ void FixBondReact::post_integrate() // for prob check, generate random value for each atom with a bond partner // forward comm of partner and random value, so ghosts have it - if (fraction[myrxn] < 1.0) { + if (fraction[rxnID] < 1.0) { for (int i = 0; i < nlocal; i++) - if (partner[i]) probability[i] = random[myrxn]->uniform(); + if (partner[i]) probability[i] = random[rxnID]->uniform(); } commflag = 2; @@ -764,11 +696,11 @@ void FixBondReact::post_integrate() // apply probability constraint using RN for atom with smallest ID - if (fraction[myrxn] < 1.0) { + if (fraction[rxnID] < 1.0) { if (tag[i] < tag[j]) { - if (probability[i] >= fraction[myrxn]) continue; + if (probability[i] >= fraction[rxnID]) continue; } else { - if (probability[j] >= fraction[myrxn]) continue; + if (probability[j] >= fraction[rxnID]) continue; } } @@ -780,31 +712,17 @@ void FixBondReact::post_integrate() if (tag[i] < tag[j]) temp_ncreate++; } - local_ncreate[myrxn] = temp_ncreate; + local_ncreate[rxnID] = temp_ncreate; // break loop if no even eligible bonding atoms were found (on any proc) int some_chance; MPI_Allreduce(&temp_ncreate,&some_chance,1,MPI_INT,MPI_SUM,world); - if (!some_chance) { - continue; - } + if (!some_chance) continue; // communicate final partner commflag = 3; comm->forward_comm_fix(this); -//obsolete comment block -/* - // I think this also simplifies for bond/react. - // but currently unsure how to adapt it - JG - // create list of created bonds that influence my owned atoms - // even if between owned-ghost or ghost-ghost atoms - // finalpartner is now set for owned and ghost atoms so loop over nall - // OK if duplicates in created list due to ghosts duplicating owned atoms - // check J < 0 to insure a created bond to unknown atom is included - // i.e. a bond partner outside of skin length -*/ - // add instance to 'created' only if this processor // owns the atoms with smaller global ID // NOTE: we no longer care about ghost-ghost instances as bond/create did @@ -816,21 +734,21 @@ void FixBondReact::post_integrate() j = atom->map(finalpartner[i]); // if (j < 0 || tag[i] < tag[j]) { if (tag[i] < tag[j]) { //atom->map(std::min(tag[i],tag[j])) <= nlocal && - if (ncreate[myrxn] == maxcreate) { + if (ncreate[rxnID] == maxcreate) { maxcreate += DELTA; // third column of 'created': bond/react integer ID memory->grow(created,maxcreate,2,nreacts,"bond/react:created"); } // to ensure types remain in same order // unnecessary now taken from reaction map file - if (iatomtype[myrxn] == type[i]) { - created[ncreate[myrxn]][0][myrxn] = tag[i]; - created[ncreate[myrxn]][1][myrxn] = finalpartner[i]; + if (iatomtype[rxnID] == type[i]) { + created[ncreate[rxnID]][0][rxnID] = tag[i]; + created[ncreate[rxnID]][1][rxnID] = finalpartner[i]; } else { - created[ncreate[myrxn]][0][myrxn] = finalpartner[i]; - created[ncreate[myrxn]][1][myrxn] = tag[i]; + created[ncreate[rxnID]][0][rxnID] = finalpartner[i]; + created[ncreate[rxnID]][1][rxnID] = tag[i]; } - ncreate[myrxn]++; + ncreate[rxnID]++; } } unlimit_bond(); //free atoms that have been relaxed @@ -854,7 +772,152 @@ void FixBondReact::post_integrate() superimpose_algorithm(); // free atoms that have been limited after reacting unlimit_bond(); +} + +/* ---------------------------------------------------------------------- + Search non-bonded neighbor lists if bonding atoms are not in special list +------------------------------------------------------------------------- */ + +void FixBondReact::far_partner() +{ + int inum,jnum,itype,jtype,possible; + double xtmp,ytmp,ztmp,delx,dely,delz,rsq; + int *ilist,*jlist,*numneigh,**firstneigh; + + // loop over neighbors of my atoms + // each atom sets one closest eligible partner atom ID to bond with + + double **x = atom->x; + tagint *tag = atom->tag; + int **nspecial = atom->nspecial; + tagint **special = atom->special; + int *mask = atom->mask; + int *type = atom->type; + + inum = list->inum; + ilist = list->ilist; + numneigh = list->numneigh; + firstneigh = list->firstneigh; + + // per-atom property indicating if in bond/react master group + int flag; + int index1 = atom->find_custom("limit_tags",flag); + int *i_limit_tags = atom->ivector[index1]; + + int i,j; + + for (int ii = 0; ii < inum; ii++) { + i = ilist[ii]; + if (!(mask[i] & groupbits[rxnID])) continue; + if (i_limit_tags[i] != 0) continue; + itype = type[i]; + xtmp = x[i][0]; + ytmp = x[i][1]; + ztmp = x[i][2]; + jlist = firstneigh[i]; + jnum = numneigh[i]; + + for (int jj = 0; jj < jnum; jj++) { + j = jlist[jj]; + j &= NEIGHMASK; + + if (!(mask[j] & groupbits[rxnID])) { + continue; +} + + if (i_limit_tags[j] != 0) { + continue; + } + + jtype = type[j]; + possible = 0; + + if (itype == iatomtype[rxnID] && jtype == jatomtype[rxnID]) { + possible = 1; + } else if (itype == jatomtype[rxnID] && jtype == iatomtype[rxnID]) { + possible = 1; + } + + if (possible == 0) continue; + + // do not allow bonding atoms within special list + for (int k = 0; k < nspecial[i][2]; k++) + if (special[i][k] == tag[j]) possible = 0; + if (!possible) continue; + + delx = xtmp - x[j][0]; + dely = ytmp - x[j][1]; + delz = ztmp - x[j][2]; + rsq = delx*delx + dely*dely + delz*delz; + + if (rsq >= cutsq[rxnID][1] || rsq <= cutsq[rxnID][0]) { + continue; + } + if (rsq < distsq[i][1]) { + partner[i] = tag[j]; + distsq[i][1] = rsq; + } + if (rsq < distsq[j][1]) { + partner[j] = tag[i]; + distsq[j][1] = rsq; + } + } + } +} + +/* ---------------------------------------------------------------------- + Slightly simpler to find bonding partner when a close neighbor +------------------------------------------------------------------------- */ + +void FixBondReact::close_partner() +{ + int n,i1,i2,itype,jtype; + double delx,dely,delz,rsq; + + double **x = atom->x; + tagint *tag = atom->tag; + tagint *type = atom->type; + int *mask = atom->mask; + int **nspecial = atom->nspecial; + int **special = atom->special; + + // per-atom property indicating if in bond/react master group + int flag; + int index1 = atom->find_custom("limit_tags",flag); + int *i_limit_tags = atom->ivector[index1]; + // loop over special list + for (int ii = 0; ii < atom->nlocal; ii++) { + itype = type[ii]; + n = 0; + if (closeneigh[rxnID] != 0) + n = nspecial[ii][closeneigh[rxnID]-1]; + for (n; n < nspecial[ii][closeneigh[rxnID]]; n++) { + i1 = ii; + i2 = atom->map(special[ii][n]); + jtype = type[i2]; + if (!(mask[i1] & groupbits[rxnID])) continue; + if (!(mask[i2] & groupbits[rxnID])) continue; + if (i_limit_tags[i1] != 0) continue; + if (i_limit_tags[i2] != 0) continue; + if (itype != iatomtype[rxnID] || jtype != jatomtype[rxnID]) continue; + + delx = x[i1][0] - x[i2][0]; + dely = x[i1][1] - x[i2][1]; + delz = x[i1][2] - x[i2][2]; + rsq = delx*delx + dely*dely + delz*delz; + if (rsq >= cutsq[rxnID][1] || rsq <= cutsq[rxnID][0]) continue; + + if (rsq > distsq[i1][0]) { + partner[i1] = tag[i2]; + distsq[i1][0] = rsq; + } + if (rsq > distsq[i2][0]) { + partner[i2] = tag[i1]; + distsq[i2][0] = rsq; + } + } + } } /* ---------------------------------------------------------------------- @@ -1076,8 +1139,8 @@ void FixBondReact::make_a_guess() if (assigned_count == nfirst_neighs) status = GUESSFAIL; // check if all neigh atom types are the same between simulation and unreacted mol - int mol_ntypes[atom->ntypes]; - int lcl_ntypes[atom->ntypes]; + int *mol_ntypes = new int[atom->ntypes]; + int *lcl_ntypes = new int[atom->ntypes]; for (int i = 0; i < atom->ntypes; i++) { mol_ntypes[i] = 0; @@ -1096,6 +1159,9 @@ void FixBondReact::make_a_guess() } } + delete [] mol_ntypes; + delete [] lcl_ntypes; + // okay everything seems to be in order. let's assign some ID pairs!!! neighbor_loop(); } @@ -1384,7 +1450,7 @@ void FixBondReact::find_landlocked_atoms(int myrxn) // if no edge atoms (small reacting molecule), all atoms are landlocked // we can delete all current topology of landlocked atoms and replace - // alwasy remove edge atoms from landlocked list + // always remove edge atoms from landlocked list for (int i = 0; i < twomol->natoms; i++) { if (edge[equivalences[i][1][myrxn]-1][myrxn] == 1) landlocked_atoms[i][myrxn] = 0; else landlocked_atoms[i][myrxn] = 1; @@ -1395,7 +1461,6 @@ void FixBondReact::find_landlocked_atoms(int myrxn) if ((force->dihedral && twomol->dihedralflag) || (force->improper && twomol->improperflag)) nspecial_limit = 1; - if (nspecial_limit != -1) { for (int i = 0; i < twomol->natoms; i++) { for (int j = 0; j < twomol->nspecial[i][nspecial_limit]; j++) { @@ -1410,11 +1475,21 @@ void FixBondReact::find_landlocked_atoms(int myrxn) // bad molecule templates check // if atoms change types, but aren't landlocked, that's bad - for (int i = 0; i < twomol->natoms; i++) { if (twomol->type[i] != onemol->type[equivalences[i][1][myrxn]-1] && landlocked_atoms[i][myrxn] == 0) error->one(FLERR,"Atom affected by reaction too close to template edge"); } + + // also, if atoms change number of bonds, but aren't landlocked, that could be bad + if (me == 0) + for (int i = 0; i < twomol->natoms; i++) { + if (twomol->nspecial[i][0] != onemol->nspecial[equivalences[i][1][myrxn]-1][0] && landlocked_atoms[i][myrxn] == 0) { + char str[128]; + sprintf(str,"An atom in 'react #%d' changes bond connectivity but not atom type",myrxn+1); + error->warning(FLERR,str); + break; + } + } } /* ---------------------------------------------------------------------- @@ -1438,7 +1513,8 @@ void FixBondReact::dedup_mega_gloves(int dedup_mode) dedup_size = global_megasize; } - tagint dedup_glove[max_natoms+1][dedup_size]; + tagint **dedup_glove; + memory->create(dedup_glove,max_natoms+1,dedup_size,"bond/react:dedup_glove"); if (dedup_mode == 0) { for (int i = 0; i < dedup_size; i++) { @@ -1456,8 +1532,8 @@ void FixBondReact::dedup_mega_gloves(int dedup_mode) // dedup_mask is size dedup_size and filters reactions that have been deleted // a value of 1 means this reaction instance has been deleted - int dedup_mask[dedup_size]; - int dup_list[dedup_size]; + int *dedup_mask = new int[dedup_size]; + int *dup_list = new int[dedup_size]; for (int i = 0; i < dedup_size; i++) { dedup_mask[i] = 0; @@ -1467,14 +1543,12 @@ void FixBondReact::dedup_mega_gloves(int dedup_mode) // let's randomly mix up our reaction instances first // then we can feel okay about ignoring ones we've already deleted (or accepted) // based off std::shuffle - // will produce same 'random' sequences on different processors. - // not really an issue, as reaction lists are independent + int *temp_rxn = new int[max_natoms+1]; for (int i = dedup_size-1; i > 0; --i) { //dedup_size - //choose random entry to swap current one with - int k = rand() % (i+1); + // choose random entry to swap current one with + int k = floor(random[0]->uniform()*(i+1)); // swap entries - int temp_rxn[max_natoms+1]; for (int j = 0; j < max_natoms+1; j++) temp_rxn[j] = dedup_glove[j][i]; @@ -1483,6 +1557,7 @@ void FixBondReact::dedup_mega_gloves(int dedup_mode) dedup_glove[j][k] = temp_rxn[j]; } } + delete [] temp_rxn; for (int i = 0; i < dedup_size; i++) { if (dedup_mask[i] == 0) { @@ -1557,6 +1632,10 @@ void FixBondReact::dedup_mega_gloves(int dedup_mode) } global_megasize = new_global_megasize; } + + memory->destroy(dedup_glove); + delete [] dedup_mask; + delete [] dup_list; } /* ---------------------------------------------------------------------- @@ -1743,9 +1822,6 @@ void FixBondReact::ghost_glovecast() for (int j = 0; j < global_megasize; j++) global_mega_glove[i][j] = 0; - - ghostcheck_flag = 1; - if (ghostly_num_mega > 0) { for (int i = 0; i < max_natoms+1; i++) { for (int j = 0; j < ghostly_num_mega; j++) { @@ -1806,7 +1882,8 @@ void FixBondReact::update_everything() // add check for local atoms as well int update_num_mega; - tagint update_mega_glove[max_natoms+1][MAX(local_num_mega,global_megasize)]; + tagint **update_mega_glove; + memory->create(update_mega_glove,max_natoms+1,MAX(local_num_mega,global_megasize),"bond/react:update_mega_glove"); for (int pass = 0; pass < 2; pass++) { @@ -2217,6 +2294,9 @@ void FixBondReact::update_everything() } } + + memory->destroy(update_mega_glove); + // something to think about: this could done much more concisely if // all atom-level info (bond,angles, etc...) were kinda inherited from a common data struct --JG @@ -2522,7 +2602,10 @@ int FixBondReact::pack_reverse_comm(int n, int first, double *buf) for (i = first; i < last; i++) { buf[m++] = ubuf(partner[i]).d; - buf[m++] = distsq[i]; + if (closeneigh[rxnID] < 0) + buf[m++] = distsq[i][1]; + else + buf[m++] = distsq[i][0]; } return m; } @@ -2538,10 +2621,16 @@ void FixBondReact::unpack_reverse_comm(int n, int *list, double *buf) if (commflag != 1) { for (i = 0; i < n; i++) { j = list[i]; - if (buf[m+1] < distsq[j]) { + if (closeneigh[rxnID] < 0) + if (buf[m+1] < distsq[j][1]) { partner[j] = (tagint) ubuf(buf[m++]).i; - distsq[j] = buf[m++]; + distsq[j][1] = buf[m++]; } else m += 2; + else + if (buf[m+1] > distsq[j][0]) { + partner[j] = (tagint) ubuf(buf[m++]).i; + distsq[j][0] = buf[m++]; + } else m += 2; } } } diff --git a/src/USER-MISC/fix_bond_react.h b/src/USER-MISC/fix_bond_react.h index a0f52c0288..e60fff37fb 100644 --- a/src/USER-MISC/fix_bond_react.h +++ b/src/USER-MISC/fix_bond_react.h @@ -53,7 +53,7 @@ class FixBondReact : public Fix { FILE *fp; int *iatomtype,*jatomtype; int *seed; - double *cutsq,*fraction; + double **cutsq,*fraction; tagint lastcheck; int stabilization_flag; int *stabilize_steps_flag; @@ -66,7 +66,7 @@ class FixBondReact : public Fix { int nmax; // max num local atoms int max_natoms; // max natoms in a molecule template tagint *partner,*finalpartner; - double *distsq,*probability; + double **distsq,*probability; int *ncreate; int maxcreate; int allncreate; @@ -96,10 +96,9 @@ class FixBondReact : public Fix { void superimpose_algorithm(); // main function of the superimpose algorithm int *ibonding,*jbonding; + int *closeneigh; // indicates if bonding atoms of a rxn are 1-2, 1-3, or 1-4 neighbors int nedge,nequivalent; // number of edge, equivalent atoms in mapping file int attempted_rxn; // there was an attempt! - int ghostcheck_flag; // idicates whether a reaction instances contains a nonlocal atom - int this_rxn_count; // num of local reaction occurrences int *local_rxn_count; int *ghostly_rxn_count; int avail_guesses; // num of restore points available @@ -143,6 +142,8 @@ class FixBondReact : public Fix { void skip_lines(int, char *); int parse(char *, char **, int); + void far_partner(); + void close_partner(); void find_landlocked_atoms(int); void glove_ghostcheck(); void ghost_glovecast(); -- GitLab