void ImproperElem::computeForce(BigReal *reduction, BigReal *pressureProfileData) { DebugM(3, "::computeForce() localIndex = " << localIndex[0] << " " << localIndex[1] << " " << localIndex[2] << " " << localIndex[3] << std::endl); // Vector r12, r23, r34; // vector between atoms Vector A,B,C; // cross products BigReal rA, rB, rC; // length of vectors A, B, and C BigReal energy=0; // energy from the angle BigReal phi; // angle between the plans double cos_phi; // cos(phi) double sin_phi; // sin(phi) Vector dcosdA; // Derivative d(cos(phi))/dA Vector dcosdB; // Derivative d(cos(phi))/dB Vector dsindC; // Derivative d(sin(phi))/dC Vector dsindB; // Derivative d(sin(phi))/dB BigReal K,K1; // energy constants BigReal diff; // for periodicity Force f1(0,0,0),f2(0,0,0),f3(0,0,0); // force components //DebugM(3, "::computeForce() -- starting with improper type " << improperType << std::endl); // get the improper information int multiplicity = value->multiplicity; // Calculate the vectors between atoms const Position & pos0 = p[0]->x[localIndex[0]].position; const Position & pos1 = p[1]->x[localIndex[1]].position; const Position & pos2 = p[2]->x[localIndex[2]].position; const Position & pos3 = p[3]->x[localIndex[3]].position; const Lattice & lattice = p[0]->p->lattice; const Vector r12 = lattice.delta(pos0,pos1); const Vector r23 = lattice.delta(pos1,pos2); const Vector r34 = lattice.delta(pos2,pos3); // Calculate the cross products A = cross(r12,r23); B = cross(r23,r34); C = cross(r23,A); // Calculate the distances rA = A.length(); rB = B.length(); rC = C.length(); // Calculate the sin and cos cos_phi = A*B/(rA*rB); sin_phi = C*B/(rC*rB); // Normalize B rB = 1.0/rB; B *= rB; phi= -atan2(sin_phi,cos_phi); if (fabs(sin_phi) > 0.1) { // Normalize A rA = 1.0/rA; A *= rA; dcosdA = rA*(cos_phi*A-B); dcosdB = rB*(cos_phi*B-A); } else { // Normalize C rC = 1.0/rC; C *= rC; dsindC = rC*(sin_phi*C-B); dsindB = rB*(sin_phi*B-C); } // Loop through the multiple parameter sets for this // bond. We will only loop more than once if this // has multiple parameter sets from Charmm22 for (int mult_num=0; mult_num<multiplicity; mult_num++) { /* get angle information */ Real k = value->values[mult_num].k * scale; Real delta = value->values[mult_num].delta; int n = value->values[mult_num].n; // Calculate the energy if (n) { // Periodicity is greater than 0, so use cos form K = k*(1+cos(n*phi - delta)); K1 = -n*k*sin(n*phi - delta); } else { // Periodicity is 0, so just use the harmonic form diff = phi-delta; if (diff < -PI) diff += TWOPI; else if (diff > PI) diff -= TWOPI; K = k*diff*diff; K1 = 2.0*k*diff; } // Add the energy from this improper to the total energy energy += K; // Next, we want to calculate the forces. In order // to do that, we first need to figure out whether the // sin or cos form will be more stable. For this, // just look at the value of phi if (fabs(sin_phi) > 0.1) { // use the sin version to avoid 1/cos terms K1 = K1/sin_phi; f1.x += K1*(r23.y*dcosdA.z - r23.z*dcosdA.y); f1.y += K1*(r23.z*dcosdA.x - r23.x*dcosdA.z); f1.z += K1*(r23.x*dcosdA.y - r23.y*dcosdA.x); f3.x += K1*(r23.z*dcosdB.y - r23.y*dcosdB.z); f3.y += K1*(r23.x*dcosdB.z - r23.z*dcosdB.x); f3.z += K1*(r23.y*dcosdB.x - r23.x*dcosdB.y); f2.x += K1*(r12.z*dcosdA.y - r12.y*dcosdA.z + r34.y*dcosdB.z - r34.z*dcosdB.y); f2.y += K1*(r12.x*dcosdA.z - r12.z*dcosdA.x + r34.z*dcosdB.x - r34.x*dcosdB.z); f2.z += K1*(r12.y*dcosdA.x - r12.x*dcosdA.y + r34.x*dcosdB.y - r34.y*dcosdB.x); } else { // This angle is closer to 0 or 180 than it is to // 90, so use the cos version to avoid 1/sin terms K1 = -K1/cos_phi; f1.x += K1*((r23.y*r23.y + r23.z*r23.z)*dsindC.x - r23.x*r23.y*dsindC.y - r23.x*r23.z*dsindC.z); f1.y += K1*((r23.z*r23.z + r23.x*r23.x)*dsindC.y - r23.y*r23.z*dsindC.z - r23.y*r23.x*dsindC.x); f1.z += K1*((r23.x*r23.x + r23.y*r23.y)*dsindC.z - r23.z*r23.x*dsindC.x - r23.z*r23.y*dsindC.y); f3 += cross(K1,dsindB,r23); f2.x += K1*(-(r23.y*r12.y + r23.z*r12.z)*dsindC.x +(2.0*r23.x*r12.y - r12.x*r23.y)*dsindC.y +(2.0*r23.x*r12.z - r12.x*r23.z)*dsindC.z +dsindB.z*r34.y - dsindB.y*r34.z); f2.y += K1*(-(r23.z*r12.z + r23.x*r12.x)*dsindC.y +(2.0*r23.y*r12.z - r12.y*r23.z)*dsindC.z +(2.0*r23.y*r12.x - r12.y*r23.x)*dsindC.x +dsindB.x*r34.z - dsindB.z*r34.x); f2.z += K1*(-(r23.x*r12.x + r23.y*r12.y)*dsindC.z +(2.0*r23.z*r12.x - r12.z*r23.x)*dsindC.x +(2.0*r23.z*r12.y - r12.z*r23.y)*dsindC.y +dsindB.y*r34.x - dsindB.x*r34.y); } } /* for multiplicity */ /* store the forces */ p[0]->f[localIndex[0]] += f1; p[1]->f[localIndex[1]] += f2 - f1; p[2]->f[localIndex[2]] += f3 - f2; p[3]->f[localIndex[3]] += -f3; DebugM(3, "::computeForce() -- ending with delta energy " << energy << std::endl); reduction[improperEnergyIndex] += energy; reduction[virialIndex_XX] += ( f1.x * r12.x + f2.x * r23.x + f3.x * r34.x ); reduction[virialIndex_XY] += ( f1.x * r12.y + f2.x * r23.y + f3.x * r34.y ); reduction[virialIndex_XZ] += ( f1.x * r12.z + f2.x * r23.z + f3.x * r34.z ); reduction[virialIndex_YX] += ( f1.y * r12.x + f2.y * r23.x + f3.y * r34.x ); reduction[virialIndex_YY] += ( f1.y * r12.y + f2.y * r23.y + f3.y * r34.y ); reduction[virialIndex_YZ] += ( f1.y * r12.z + f2.y * r23.z + f3.y * r34.z ); reduction[virialIndex_ZX] += ( f1.z * r12.x + f2.z * r23.x + f3.z * r34.x ); reduction[virialIndex_ZY] += ( f1.z * r12.y + f2.z * r23.y + f3.z * r34.y ); reduction[virialIndex_ZZ] += ( f1.z * r12.z + f2.z * r23.z + f3.z * r34.z ); if (pressureProfileData) { BigReal z1 = p[0]->x[localIndex[0]].position.z; BigReal z2 = p[1]->x[localIndex[1]].position.z; BigReal z3 = p[2]->x[localIndex[2]].position.z; BigReal z4 = p[3]->x[localIndex[3]].position.z; int n1 = (int)floor((z1-pressureProfileMin)/pressureProfileThickness); int n2 = (int)floor((z2-pressureProfileMin)/pressureProfileThickness); int n3 = (int)floor((z3-pressureProfileMin)/pressureProfileThickness); int n4 = (int)floor((z4-pressureProfileMin)/pressureProfileThickness); pp_clamp(n1, pressureProfileSlabs); pp_clamp(n2, pressureProfileSlabs); pp_clamp(n3, pressureProfileSlabs); pp_clamp(n4, pressureProfileSlabs); int p1 = p[0]->x[localIndex[0]].partition; int p2 = p[1]->x[localIndex[1]].partition; int p3 = p[2]->x[localIndex[2]].partition; int p4 = p[3]->x[localIndex[3]].partition; int pn = pressureProfileAtomTypes; pp_reduction(pressureProfileSlabs, n1, n2, p1, p2, pn, f1.x * r12.x, f1.y * r12.y, f1.z * r12.z, pressureProfileData); pp_reduction(pressureProfileSlabs, n2, n3, p2, p3, pn, f2.x * r23.x, f2.y * r23.y, f2.z * r23.z, pressureProfileData); pp_reduction(pressureProfileSlabs, n3, n4, p3, p4, pn, f3.x * r34.x, f3.y * r34.y, f3.z * r34.z, pressureProfileData); } }
void BondElem::computeForce(BigReal *reduction, BigReal *pressureProfileData) { DebugM(1, "::computeForce() localIndex = " << localIndex[0] << " " << localIndex[1] << std::endl); // skip Lonepair bonds (other k=0. bonds have been filtered out) if (0. == value->k) return; BigReal scal; // force scaling BigReal energy; // energy from the bond //DebugM(3, "::computeForce() -- starting with bond type " << bondType << std::endl); // get the bond information Real k = value->k * scale; Real x0 = value->x0; // compute vectors between atoms and their distances const Lattice & lattice = p[0]->p->lattice; const Vector r12 = lattice.delta(p[0]->x[localIndex[0]].position, p[1]->x[localIndex[1]].position); if (0. == x0) { // for Drude bonds SimParameters *simParams = Node::Object()->simParameters; BigReal drudeBondLen = simParams->drudeBondLen; BigReal drudeBondConst = simParams->drudeBondConst; BigReal r2 = r12.length2(); scal = -2.0*k; // bond interaction for equilibrium length 0 energy = k * r2; if (drudeBondConst > 0 && r2 > drudeBondLen*drudeBondLen) { // add a quartic restraining potential to keep Drude bond short BigReal r = sqrt(r2); BigReal diff = r - drudeBondLen; BigReal diff2 = diff*diff; scal += -4*drudeBondConst * diff2 * diff / r; energy += drudeBondConst * diff2 * diff2; } } else { BigReal r = r12.length(); // Distance between atoms BigReal diff = r - x0; // Compare it to the rest bond // Add the energy from this bond to the total energy energy = k*diff*diff; // Determine the magnitude of the force diff *= -2.0*k; // Scale the force vector accordingly scal = (diff/r); } const Force f12 = scal * r12; // Now add the forces to each force vector p[0]->f[localIndex[0]] += f12; p[1]->f[localIndex[1]] -= f12; DebugM(3, "::computeForce() -- ending with delta energy " << energy << std::endl); reduction[bondEnergyIndex] += energy; reduction[virialIndex_XX] += f12.x * r12.x; reduction[virialIndex_XY] += f12.x * r12.y; reduction[virialIndex_XZ] += f12.x * r12.z; reduction[virialIndex_YX] += f12.y * r12.x; reduction[virialIndex_YY] += f12.y * r12.y; reduction[virialIndex_YZ] += f12.y * r12.z; reduction[virialIndex_ZX] += f12.z * r12.x; reduction[virialIndex_ZY] += f12.z * r12.y; reduction[virialIndex_ZZ] += f12.z * r12.z; if (pressureProfileData) { BigReal z1 = p[0]->x[localIndex[0]].position.z; BigReal z2 = p[1]->x[localIndex[1]].position.z; int n1 = (int)floor((z1-pressureProfileMin)/pressureProfileThickness); int n2 = (int)floor((z2-pressureProfileMin)/pressureProfileThickness); pp_clamp(n1, pressureProfileSlabs); pp_clamp(n2, pressureProfileSlabs); int p1 = p[0]->x[localIndex[0]].partition; int p2 = p[1]->x[localIndex[1]].partition; int pn = pressureProfileAtomTypes; pp_reduction(pressureProfileSlabs, n1, n2, p1, p2, pn, f12.x * r12.x, f12.y * r12.y, f12.z * r12.z, pressureProfileData); } }