// calculates and adds its forces to the current forces of the force field void MMFF94OutOfPlaneBend::updateForces() { const double FC = K0 * 2. * RADIAN_TO_DEGREE * RADIAN_TO_DEGREE; bool us = getForceField()->getUseSelection(); ////////////////////////////////////////////////////////////////// // ids of the non-central atoms for the three runs per out of plane bend: vector<vector<Position> > atom_ids; // Atom i: vector<Position> temp; temp.push_back(0); temp.push_back(0); temp.push_back(1); atom_ids.push_back(temp); // Atom k: temp.clear(); temp.push_back(1); temp.push_back(2); temp.push_back(2); atom_ids.push_back(temp); // Atom l: temp.clear(); temp.push_back(2); temp.push_back(1); temp.push_back(0); atom_ids.push_back(temp); //////////////////////////////////////////////////////////////////////// // all calculations below have to be performed at double precision // otherwise the results are far off, especially for small wilson angles // // temp variables: double length; TVector3<double> delta; // the three atoms bound to the central atom (for the actual plane bend) vector<Atom*> partners(3); // lenght of the vectors from the central atom to outer atoms: vector<double> lengths(3); // normalized bond vectors from central atom to outer atoms: vector<TVector3<double> > nbv(3); // index of the individual atoms in partners and nbv: Position pi, pk, pl; // normal vectors of the three planes: TVector3<double> an, bn, cn; for (Position t = 0; t < bends_.size(); t++) { // the current bend const OutOfPlaneBend& bend = bends_[t]; Atom& ta1 = *bend.i->ptr; Atom& ta2 = *bend.j->ptr; Atom& ta3 = *bend.k->ptr; Atom& ta4 = *bend.l->ptr; // if using selection and no atom is selected: ignore this bend: if (us && !ta1.isSelected() && !ta2.isSelected() && !ta3.isSelected() && !ta4.isSelected()) { continue; } // non central atoms for this bend: partners[0] = &ta1; partners[1] = &ta3; partners[2] = &ta4; Atom& center_atom = ta2; // abort for this bend if two atoms have the same position: bool error = false; // calculate normalized bond vectors from central atom to outer atoms: for (Position p = 0; p < 3; p++) { // transformation from single to double precision: delta.x = partners[p]->getPosition().x - center_atom.getPosition().x; delta.y = partners[p]->getPosition().y - center_atom.getPosition().y; delta.z = partners[p]->getPosition().z - center_atom.getPosition().z; length = delta.getLength(); if (Maths::isZero(length)) { error = true; break; } // normalize the bond vector: delta /= length; // store the normalized bond vector: nbv[p] = delta; // store length of this bond: lengths[p] = length; } // abort if any bond lenght equals zero if (error) continue; // three runs per OOP: for (Position run = 0; run < 3; run++) { // position of the individual atoms in partners[] and nbv[] pi = atom_ids[0][run]; pk = atom_ids[1][run]; pl = atom_ids[2][run]; Atom& i = *partners[pi]; Atom& k = *partners[pk]; Atom& l = *partners[pl]; // normalized vectors from central atom to outer atoms: const TVector3<double>& ji = nbv[pi]; const TVector3<double>& jk = nbv[pk]; const TVector3<double>& jl = nbv[pl]; const double& length_ji = lengths[pi]; const double& length_jk = lengths[pk]; const double& length_jl = lengths[pl]; // the normal vectors of the three planes: an = ji % jk; bn = jk % jl; cn = jl % ji; // Bond angle ji to jk const double cos_theta = ji * jk; const double theta = acos(cos_theta); // If theta equals 180 degree or 0 degree if (Maths::isZero(theta) || Maths::isZero(fabs(theta - Constants::PI))) { continue; } const double sin_theta = sin(theta); const double sin_dl = an * jl / sin_theta; // the wilson angle: const double dl = asin(sin_dl); // In case: wilson angle equals 0 or 180 degree: do nothing if (Maths::isZero(dl) || Maths::isZero(fabs(dl - Constants::PI))) { continue; } const double cos_dl = cos(dl); // if wilson angle equal 90 degree: abort if (cos_dl < 0.0001) { continue; } // scaling factor for all forces: // wilson K0 * this_bend_constant * wilson_angle * DEGREE_TO_RADIAN * DEGREE_TO_RADIAN double c1 = -dl * FC * bend.k_oop * FORCES_FACTOR * Constants::JOULE_PER_CAL; double tmp = cos_dl / c1; /* Log.precision(30); Log.error() << "bond " << theta << std::endl; Log.error() << "wilson " << dl << std::endl; Log.error() << "tan_dl " << tan_dl << std::endl; Log.error() << "cdst " << cdst << std::endl; Log.error() << "tdst " << tdst << std::endl; Log.error() << "c1 " << c1 << std::endl; Log.error() << "abc " << an << bn << cn << std::endl << std::endl << std::endl << std::endl; */ const TVector3<double> d_l = ((an / sin_theta - jl * sin_dl) / length_jl) / tmp; const TVector3<double> d_i = (((bn + (((-ji + jk * cos_theta) * sin_dl) / sin_theta)) / length_ji) / tmp) / sin_theta; const TVector3<double> d_k = (((cn + (((-jk + ji * cos_theta) * sin_dl) / sin_theta)) / length_jk) / tmp) / sin_theta; if (!us || i.isSelected()) AddDV3_(i.getForce(), d_i); if (!us || k.isSelected()) AddDV3_(k.getForce(), d_k); if (!us || l.isSelected()) AddDV3_(l.getForce(), d_l); if (!us || center_atom.isSelected()) AddDV3_(center_atom.getForce(), -(d_i + d_k + d_l)); #ifdef BALL_MMFF94_TEST getForceField()->error() << std::endl << i.getName() << " " << d_i << std::endl << center_atom.getName() << " " << -(d_i +d_k + d_l) << std::endl << k.getName() << " " << d_k << std::endl << l.getName() << " " << d_l << std::endl; #endif } } }