//////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2008 The Regents of the University of California // // This file is part of Qbox // // Qbox is distributed 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. // See the file COPYING in the root directory of this distribution // or . // //////////////////////////////////////////////////////////////////////////////// // // TorsionConstraint.C // //////////////////////////////////////////////////////////////////////////////// // $Id: TorsionConstraint.C,v 1.6 2008-09-08 15:56:19 fgygi Exp $ #include "TorsionConstraint.h" #include "AtomSet.h" #include "Atom.h" #include "Species.h" #include #include #include #include using namespace std; //////////////////////////////////////////////////////////////////////////////// void TorsionConstraint::setup(const AtomSet& atoms) { // find position in tau array corresponding to atom name1 is1_ = atoms.is(name1_); ia1_ = atoms.ia(name1_); assert(is1_>=0); assert(ia1_>=0); m1_ = atoms.species_list[is1_]->mass() * 1822.89; assert(m1_>0.0); m1_inv_ = 1.0 / m1_; is2_ = atoms.is(name2_); ia2_ = atoms.ia(name2_); assert(is2_>=0); assert(ia2_>=0); m2_ = atoms.species_list[is2_]->mass() * 1822.89; assert(m2_>0.0); m2_inv_ = 1.0 / m2_; is3_ = atoms.is(name3_); ia3_ = atoms.ia(name3_); assert(is3_>=0); assert(ia3_>=0); m3_ = atoms.species_list[is3_]->mass() * 1822.89; assert(m3_>0.0); m3_inv_ = 1.0 / m3_; is4_ = atoms.is(name4_); ia4_ = atoms.ia(name4_); assert(is4_>=0); assert(ia4_>=0); m4_ = atoms.species_list[is4_]->mass() * 1822.89; assert(m4_>0.0); m4_inv_ = 1.0 / m4_; } //////////////////////////////////////////////////////////////////////////////// void TorsionConstraint::update(double dt) { angle_ += velocity_ * dt; if ( angle_ > 180.0 ) angle_ -= 360.0; if ( angle_ < -180.0 ) angle_ += 360.0; sin_angle_ = sin((M_PI/180.0)*angle_); cos_angle_ = cos((M_PI/180.0)*angle_); } //////////////////////////////////////////////////////////////////////////////// bool TorsionConstraint::enforce_r(const vector > &r0, vector > &rp) const { const double* pr1 = &r0[is1_][3*ia1_]; const double* pr2 = &r0[is2_][3*ia2_]; const double* pr3 = &r0[is3_][3*ia3_]; const double* pr4 = &r0[is4_][3*ia4_]; double* pr1p = &rp[is1_][3*ia1_]; double* pr2p = &rp[is2_][3*ia2_]; double* pr3p = &rp[is3_][3*ia3_]; double* pr4p = &rp[is4_][3*ia4_]; D3vector r1(pr1); D3vector r2(pr2); D3vector r3(pr3); D3vector r4(pr4); D3vector r1p(pr1p); D3vector r2p(pr2p); D3vector r3p(pr3p); D3vector r4p(pr4p); const double h = 0.001; const double fac = 0.5 / h; D3vector dx(h,0,0), dy(0,h,0), dz(0,0,h); // compute gradient at r D3vector g1,g2,g3,g4; grad_sigma(r1,r2,r3,r4,g1,g2,g3,g4); const double a = torsion_angle(r1,r2,r3,r4); double ng = g1*g1 + g2*g2 + g3*g3 + g4*g4; assert(ng>=0.0); // compute gradient at rp D3vector g1p,g2p,g3p,g4p; grad_sigma(r1p,r2p,r3p,r4p,g1p,g2p,g3p,g4p); const double ap = torsion_angle(r1p,r2p,r3p,r4p); const double ngp = g1p*g1p + g2p*g2p + g3p*g3p + g4p*g4p; assert(ngp>=0.0); const double err = fabs(ap-angle_); if ( ng == 0.0 ) { // gradient is ill defined at r and is likely to change // rapidly between r and rp, invalidating the Taylor expansion // use the gradient at rp for the correction if ( ngp == 0.0 ) { // gradient is undefined at r and rp // Insufficient information to correct rp return err < tol_; } else { // gradient is defined at rp but not at r // use gradient at rp only g1 = g1p; g2 = g2p; g3 = g3p; g4 = g4p; ng = ngp; } } else { // gradient is defined at r if ( ngp == 0.0 ) { // gradient is defined at r but not at rp return err < tol_; } else { // gradient is defined at r and rp // no action necessary } } // test alignment of the gradient at r and at rp // compute scalar product of normalized gradients const double sp = ( g1*g1p + g2*g2p + g3*g3p + g4*g4p ) / sqrt( ng * ngp ); if ( fabs(sp) < 0.5*sqrt(3.0) ) { g1 = g1p; g2 = g2p; g3 = g3p; g4 = g4p; } #if DEBUG_CONSTRAINTS cout << " TorsionConstraint::enforce_r: " << name1_ << " " << name2_ << " " << name3_ << " " << name4_ << endl; cout << " TorsionConstraint::enforce_r: tol = " << tol_ << endl; cout << " TorsionConstraint::enforce_r: ap = " << ap << endl; cout << " TorsionConstraint::enforce_r: err = " << err << endl; #endif if ( err < tol_ ) return true; // make one shake iteration const double den = m1_inv_ * g1 * g1p + m2_inv_ * g2 * g2p + m3_inv_ * g3 * g3p + m4_inv_ * g4 * g4p; assert(fabs(den)>0.0); const double lambda = - sigma(r1p,r2p,r3p,r4p) / den; pr1p[0] += m1_inv_ * lambda * g1.x; pr1p[1] += m1_inv_ * lambda * g1.y; pr1p[2] += m1_inv_ * lambda * g1.z; pr2p[0] += m2_inv_ * lambda * g2.x; pr2p[1] += m2_inv_ * lambda * g2.y; pr2p[2] += m2_inv_ * lambda * g2.z; pr3p[0] += m3_inv_ * lambda * g3.x; pr3p[1] += m3_inv_ * lambda * g3.y; pr3p[2] += m3_inv_ * lambda * g3.z; pr4p[0] += m4_inv_ * lambda * g4.x; pr4p[1] += m4_inv_ * lambda * g4.y; pr4p[2] += m4_inv_ * lambda * g4.z; return false; } //////////////////////////////////////////////////////////////////////////////// bool TorsionConstraint::enforce_v(const vector > &r0, vector > &v0) const { const double* pr1 = &r0[is1_][3*ia1_]; const double* pr2 = &r0[is2_][3*ia2_]; const double* pr3 = &r0[is3_][3*ia3_]; const double* pr4 = &r0[is4_][3*ia4_]; double* pv1 = &v0[is1_][3*ia1_]; double* pv2 = &v0[is2_][3*ia2_]; double* pv3 = &v0[is3_][3*ia3_]; double* pv4 = &v0[is4_][3*ia4_]; D3vector r1(pr1); D3vector r2(pr2); D3vector r3(pr3); D3vector r4(pr4); D3vector v1(pv1); D3vector v2(pv2); D3vector v3(pv3); D3vector v4(pv4); D3vector g1,g2,g3,g4; grad_sigma(r1,r2,r3,r4,g1,g2,g3,g4); const double norm2 = g1*g1 + g2*g2 + g3*g3 +g4*g4; // if the gradient is too small, do not attempt correction if ( norm2 < 1.e-6 ) return true; const double proj = v1*g1 + v2*g2 + v3*g3 + v4*g4; const double err = fabs(proj)/sqrt(norm2); #if DEBUG_CONSTRAINTS cout << " TorsionConstraint::enforce_v: " << name1_ << " " << name2_ << " " << name3_ << " " << name4_<< endl; cout << " TorsionConstraint::enforce_v: tol = " << tol_ << endl; cout << " TorsionConstraint::enforce_v: err = " << err cout << " TorsionConstraint::enforce_v: g1 = " << g1 << endl; cout << " TorsionConstraint::enforce_v: g2 = " << g2 << endl; cout << " TorsionConstraint::enforce_v: g3 = " << g3 << endl; cout << " TorsionConstraint::enforce_v: g4 = " << g4 << endl; #endif if ( err < tol_ ) return true; // make one shake iteration const double den = m1_inv_ * g1 * g1 + m2_inv_ * g2 * g2 + m3_inv_ * g3 * g3 + m4_inv_ * g4 * g4; assert(den>0.0); const double eta = -proj / den; pv1[0] += m1_inv_ * eta * g1.x; pv1[1] += m1_inv_ * eta * g1.y; pv1[2] += m1_inv_ * eta * g1.z; pv2[0] += m2_inv_ * eta * g2.x; pv2[1] += m2_inv_ * eta * g2.y; pv2[2] += m2_inv_ * eta * g2.z; pv3[0] += m3_inv_ * eta * g3.x; pv3[1] += m3_inv_ * eta * g3.y; pv3[2] += m3_inv_ * eta * g3.z; pv4[0] += m4_inv_ * eta * g4.x; pv4[1] += m4_inv_ * eta * g4.y; pv4[2] += m4_inv_ * eta * g4.z; return false; } //////////////////////////////////////////////////////////////////////////////// void TorsionConstraint::compute_force(const vector > &r0, const vector > &f) { const double* pr1 = &r0[is1_][3*ia1_]; const double* pr2 = &r0[is2_][3*ia2_]; const double* pr3 = &r0[is3_][3*ia3_]; const double* pr4 = &r0[is4_][3*ia4_]; const double* pf1 = &f[is1_][3*ia1_]; const double* pf2 = &f[is2_][3*ia2_]; const double* pf3 = &f[is3_][3*ia3_]; const double* pf4 = &f[is4_][3*ia4_]; D3vector r1(pr1); D3vector r2(pr2); D3vector r3(pr3); D3vector r4(pr4); D3vector f1(pf1); D3vector f2(pf2); D3vector f3(pf3); D3vector f4(pf4); const double h = 0.001; const double fac = 0.5 / h; D3vector dx(h,0,0), dy(0,h,0), dz(0,0,h); // compute gradient at r D3vector g1,g2,g3,g4; grad_sigma(r1,r2,r3,r4,g1,g2,g3,g4); const double norm2 = g1*g1 + g2*g2 + g3*g3 +g4*g4; assert(norm2>0.0); const double proj = f1*g1 + f2*g2 + f3*g3 + f4*g4; if ( norm2 == 0.0 ) { force_ = 0.0; return; } force_ = -proj/norm2; // compute weight const double z = m1_inv_ * g1 * g1 + m2_inv_ * g2 * g2 + m3_inv_ * g3 * g3 + m4_inv_ * g4 * g4; assert(z > 0.0); weight_ = 1.0 / sqrt(z); } //////////////////////////////////////////////////////////////////////////////// ostream& TorsionConstraint::print( ostream &os ) { os.setf(ios::left,ios::adjustfield); os << " "; return os; } //////////////////////////////////////////////////////////////////////////////// double TorsionConstraint::sigma(D3vector a, D3vector b, D3vector c, D3vector d) const { // compute the constraint function return torsion_angle(a,b,c,d) - angle_; } //////////////////////////////////////////////////////////////////////////////// void TorsionConstraint::grad_sigma(const D3vector &r1, const D3vector &r2, const D3vector &r3, const D3vector &r4, D3vector &g1, D3vector &g2, D3vector &g3, D3vector &g4) const { const double h = 0.001; const double fac = 0.5 / h; D3vector dx(h,0,0), dy(0,h,0), dz(0,0,h); // compute gradient at r g1.x = fac * ( sigma(r1+dx,r2,r3,r4) - sigma(r1-dx,r2,r3,r4) ); g1.y = fac * ( sigma(r1+dy,r2,r3,r4) - sigma(r1-dy,r2,r3,r4) ); g1.z = fac * ( sigma(r1+dz,r2,r3,r4) - sigma(r1-dz,r2,r3,r4) ); g2.x = fac * ( sigma(r1,r2+dx,r3,r4) - sigma(r1,r2-dx,r3,r4) ); g2.y = fac * ( sigma(r1,r2+dy,r3,r4) - sigma(r1,r2-dy,r3,r4) ); g2.z = fac * ( sigma(r1,r2+dz,r3,r4) - sigma(r1,r2-dz,r3,r4) ); g3.x = fac * ( sigma(r1,r2,r3+dx,r4) - sigma(r1,r2,r3-dx,r4) ); g3.y = fac * ( sigma(r1,r2,r3+dy,r4) - sigma(r1,r2,r3-dy,r4) ); g3.z = fac * ( sigma(r1,r2,r3+dz,r4) - sigma(r1,r2,r3-dz,r4) ); g4.x = fac * ( sigma(r1,r2,r3,r4+dx) - sigma(r1,r2,r3,r4-dx) ); g4.y = fac * ( sigma(r1,r2,r3,r4+dy) - sigma(r1,r2,r3,r4-dy) ); g4.z = fac * ( sigma(r1,r2,r3,r4+dz) - sigma(r1,r2,r3,r4-dz) ); } //////////////////////////////////////////////////////////////////////////////// double TorsionConstraint::torsion_angle(D3vector a, D3vector b, D3vector c, D3vector d) const { // compute the torsion angle defined by vectors a,b,c,d D3vector e12(normalized(a-b)); D3vector e32(normalized(c-b)); D3vector e23(-e32); D3vector e43(normalized(d-c)); const double sin123 = length(e12^e32); const double sin234 = length(e23^e43); double an = 0; if ( sin123 != 0.0 && sin234 != 0.0 ) { D3vector e123 = normalized(e12^e32); D3vector e234 = normalized(e23^e43); double cc = max(min(e123*e234,1.0),-1.0); double ss = max(min((e123^e234)*e32,1.0),-1.0); an = (180.0/M_PI) * atan2(ss,cc); } return an; }