As discussed at the end of this bug report, I am trying to extend compute stress/cartesian
to include bond forces.
Initial implementation
Initially, I came up with this code:
--- a/src/EXTRA-COMPUTE/compute_stress_cartesian.cpp
+++ b/src/EXTRA-COMPUTE/compute_stress_cartesian.cpp
@@ -361,6 +362,15 @@ void ComputeStressCartesian::compute_array()
// Check if inside cut-off
if (rsq >= cutsq[itype][jtype]) continue;
pair->single(i, j, itype, jtype, rsq, factor_coul, factor_lj, fpair);
+ // Loop over all bonds, checking if bond is between current pair
+ for (int bondi = 0; bondi < neighbor->nbondlist; bondi++) {
+ int *bond = neighbor->bondlist[bondi];
+ if (bond[0] == i && bond[1] == j) {
+ // Add bond force to pair force
+ force->bond->single(bond[2], rsq, i, j, fpair);
+ break;
+ }
+ }
compute_pressure(fpair, xi1, xi2, delx, dely, delz);
}
}
This works, but unfortunately is excruciatingly slow. (not unexpectedly, obviously, since what I’m doing here is looping over all bonds in the system for each pair of atoms in the neighbor list.
Note that this code requires you to set special_bonds
to > 0, since special neighbors will be excluded from the neighbor lists when set to exactly 0. Hence, instead of special_bonds lj/coul 0.0 1.0 1.0
, for this to work it is required to set special_bonds lj/coul 1.0e-100 1.0 1.0
instead.
Optimization
In order to improve the performance, I tried another approach where instead of looping through all bonds in the system, I specifically loop over the bonds of the current atom:
--- a/src/EXTRA-COMPUTE/compute_stress_cartesian.cpp
+++ b/src/EXTRA-COMPUTE/compute_stress_cartesian.cpp
@@ -361,6 +362,15 @@ void ComputeStressCartesian::compute_array()
// Check if inside cut-off
if (rsq >= cutsq[itype][jtype]) continue;
pair->single(i, j, itype, jtype, rsq, factor_coul, factor_lj, fpair);
+ if (atom->molecular == Atom::MOLECULAR) compute_bond_force(i, j, rsq, fpair);
compute_pressure(fpair, xi1, xi2, delx, dely, delz);
}
}
@@ -409,6 +407,34 @@ void ComputeStressCartesian::compute_array()
}
}
+void ComputeStressCartesian::compute_bond_force(int atom1, int atom2, double rsq, double& f) {
+ // Loop over bonds of atom1
+ for (int i_bond = 0; i_bond < atom->num_bond[atom1]; i_bond++) {
+ // Get local index of bonded atom
+ int bonded_atom = atom->map(atom->bond_atom[atom1][i_bond]);
+ if (atom2 == bonded_atom) {
+ // Add bond force to pair force
+ force->bond->single(atom->bond_type[atom1][i_bond], rsq, atom1, atom2, f);
+ return;
+ }
+ }
This works if newton_bond=false
, since in that case the num_bond
/bond_atom
contains bonds ‘symmetrically’ (for both atoms) (as I found out in a different thread, so I only need to loop over bonds of the ‘left’ atom.
However, for newton_bond=true
, I need to loop over bonds of both the ‘left’ and ‘right’ atom in the pair. This is more complicated since now the right atom can be a ghost atom, in which case num_bond
/bond_atom
does not contain it and I need to manually communicate this information.
As a first try, I came up with this:
--- a/src/EXTRA-COMPUTE/compute_stress_cartesian.cpp
+++ b/src/EXTRA-COMPUTE/compute_stress_cartesian.cpp
@@ -176,6 +176,8 @@ ComputeStressCartesian::ComputeStressCartesian(LAMMPS *lmp, int narg, char **arg
memory->create(tpcyy, nbins1 * nbins2, "tpcyy");
memory->create(tpczz, nbins1 * nbins2, "tpczz");
memory->create(array, size_array_rows, size_array_cols, "stress:cartesian:output");
+
+ comm_forward = atom->bond_per_atom + 1;
}
/* ---------------------------------------------------------------------- */
@@ -252,6 +254,9 @@ void ComputeStressCartesian::compute_array()
// invoke half neighbor list (will copy or build if necessary)
neighbor->build_one(list);
+ // We have to communicate bonds of ghost atoms if newton is on for bonds
+ if (force->newton_bond) comm->forward_comm(this);
+
inum = list->inum;
ilist = list->ilist;
numneigh = list->numneigh;
@@ -361,16 +366,9 @@ void ComputeStressCartesian::compute_array()
// Check if inside cut-off
if (rsq >= cutsq[itype][jtype]) continue;
+
pair->single(i, j, itype, jtype, rsq, factor_coul, factor_lj, fpair);
- // Loop over all bonds, checking if bond is between current pair
- for (int bondi = 0; bondi < neighbor->nbondlist; bondi++) {
- int *bond = neighbor->bondlist[bondi];
- if (bond[0] == i && bond[1] == j) {
- // Add bond force to pair force
- force->bond->single(bond[2], rsq, i, j, fpair);
- break;
- }
- }
+ if (atom->molecular == Atom::MOLECULAR) compute_bond_force(i, j, rsq, fpair);
compute_pressure(fpair, xi1, xi2, delx, dely, delz);
}
}
@@ -409,6 +407,34 @@ void ComputeStressCartesian::compute_array()
}
}
+void ComputeStressCartesian::compute_bond_force(int atom1, int atom2, double rsq, double& f) {
+ // Loop over bonds of atom1
+ for (int i_bond = 0; i_bond < atom->num_bond[atom1]; i_bond++) {
+ // Get local index of bonded atom
+ int bonded_atom = atom->map(atom->bond_atom[atom1][i_bond]);
+ if (atom2 == bonded_atom) {
+ // Add bond force to pair force
+ force->bond->single(atom->bond_type[atom1][i_bond], rsq, atom1, atom2, f);
+ return;
+ }
+ }
+
+ // Only have to loop over bonds of atom2 if newton is on (num_bond and bond_atom only contain 'owned' bonds if
+ // newton is on)
+ if (!force->newton_bond) return;
+
+ for (int i_bond = 0; i_bond < atom->num_bond[atom2]; i_bond++) {
+ // Get local index of bonded atom
+ int bonded_atom = atom->map(atom->bond_atom[atom2][i_bond]);
+ if (atom1 == bonded_atom) {
+ // Add bond force to pair force
+ force->bond->single(atom->bond_type[atom2][i_bond], rsq, atom1, atom2, f);
+ return;
+ }
+ }
+
+}
+
void ComputeStressCartesian::compute_pressure(double fpair, double xi, double yi, double delx,
double dely, double delz)
{
@@ -488,3 +514,48 @@ double ComputeStressCartesian::memory_usage()
{
return (14.0 + dims + 7) * (double) (nbins1 * nbins2) * sizeof(double);
}
+
+int ComputeStressCartesian::pack_forward_comm(int n, int *list, double *buf, int pbc_flag, int *pbc) {
+ // n: number of ghost atoms
+ // list: atom ids
+
+ // Pack num_bond and bond_atom arrays for each ghost atom
+
+ int m = 0;
+
+ // Loop over ghost atoms
+ for (int i = 0; i < n; i++) {
+ int atom1 = list[i];
+ int num_bond = atom->num_bond[atom1];
+ buf[m++] = ubuf(num_bond).d;
+
+ // Loop over bonds for ghost atom
+ for (int j = 0; j < num_bond; j++) {
+ // Get global index of bonded atom
+ buf[m++] = ubuf(atom->bond_atom[atom1][j]).d;
+ }
+ }
+
+ return m;
+}
+
+void ComputeStressCartesian::unpack_forward_comm(int n, int first, double *buf) {
+ int m = 0;
+
+ int last = first + n;
+ for (int i = first; i < last; i++) {
+ int num_bond = (int) ubuf(buf[m++]).i;
+ atom->num_bond[i] = num_bond;
+ for (int j = 0; j < num_bond; i++) {
+ // Get global index of bonded atom from buffer
+ atom->bond_atom[i][j] = (int) ubuf(buf[m++]).i;
+ }
+ }
+}
However, this segfaults in unpack_forward_comm()
, where it is assigning atom->num_bond[i] = num_bond
; I presume this array is only nlocal
large when newton_bond=true
, so the ghost atoms don’t fit.
This is where I’m stuck. I presume I should grow the num_bond
/bond_atom
arrays, but haven’t yet found out how. That’s my question: how should I grow num_bond
/bond_atom
when newton is on, so that I can communicate the bond info of ghost atoms? Or this there a better way to do this?