////////////////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////////////////
//
// DistanceConstraint.C
//
////////////////////////////////////////////////////////////////////////////////
// $Id: DistanceConstraint.C,v 1.6 2008-09-08 15:56:18 fgygi Exp $
#include "DistanceConstraint.h"
#include "AtomSet.h"
#include "Atom.h"
#include "Species.h"
#include
#include
#include
#include
using namespace std;
////////////////////////////////////////////////////////////////////////////////
void DistanceConstraint::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_;
}
////////////////////////////////////////////////////////////////////////////////
void DistanceConstraint::update(double dt)
{
// cout << " DistanceConstraint::update" << endl;
if ( distance_ + velocity_ * dt > 0.0 )
distance_ += velocity_ * dt;
}
////////////////////////////////////////////////////////////////////////////////
bool DistanceConstraint::enforce_r(const vector > &r0,
vector > &rp) const
{
const double* pr1 = &r0[is1_][3*ia1_];
const double* pr2 = &r0[is2_][3*ia2_];
D3vector r1(pr1);
D3vector r2(pr2);
double* pr1p = &rp[is1_][3*ia1_];
double* pr2p = &rp[is2_][3*ia2_];
D3vector r1p(pr1p);
D3vector r2p(pr2p);
// compute gradient at r
D3vector r12(r1-r2);
D3vector g1,g2;
g1 = 2.0 * r12;
g2 = -g1;
double ng = g1*g1 + g2*g2;
assert(ng>=0.0);
// compute gradient at rp
D3vector r12p(r1p-r2p);
D3vector g1p,g2p;
g1p = 2.0 * r12p;
g2p = -g1p;
const double ngp = g1p*g1p + g2p*g2p;
assert(ngp>=0.0);
// test alignment of the gradient at r and at rp
// compute scalar product of normalized gradients
const double sp = ( g1*g1p + g2*g2p ) / sqrt( ng * ngp );
if ( fabs(sp) < 0.5*sqrt(3.0) )
{
g1 = g1p;
g2 = g2p;
#if DEBUG_CONSTRAINTS
cout << " g and gp nearly orthogonal, use gp only" << endl;
#endif
}
const double sigma = r12p*r12p - distance_*distance_;
#if DEBUG_CONSTRAINTS
cout << " DistanceConstraint::enforce_r: " << name1_ << " " << name2_ << endl;
cout << " DistanceConstraint::enforce_r: r1 = " << r1 << endl;
cout << " DistanceConstraint::enforce_r: r2 = " << r2 << endl;
cout << " DistanceConstraint::enforce_r: tol = " << tol_ << endl;
cout << " DistanceConstraint::enforce_r: err = " << sigma << endl;
cout << " DistanceConstraint::enforce_r: g1 = " << g1 << endl;
cout << " DistanceConstraint::enforce_r: g2 = " << g2 << endl;
cout << " DistanceConstraint::enforce_r: g1p = " << g1p << endl;
cout << " DistanceConstraint::enforce_r: g2p = " << g2p << endl;
#endif
if ( fabs(sigma) < tol_ ) return true;
// make one shake iteration
const double den = m1_inv_ * g1 * g1p + m2_inv_ * g2 * g2p;
const double lambda = -sigma / 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;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool DistanceConstraint::enforce_v(const vector > &r0,
vector > &v0) const
{
const double* pr1 = &r0[is1_][3*ia1_];
const double* pr2 = &r0[is2_][3*ia2_];
D3vector r1(pr1);
D3vector r2(pr2);
double* pv1 = &v0[is1_][3*ia1_];
double* pv2 = &v0[is2_][3*ia2_];
D3vector v1(pv1);
D3vector v2(pv2);
// compute gradient at r
D3vector r12(r1-r2);
D3vector g1,g2;
g1 = 2.0 * r12;
g2 = -g1;
const double norm2 = g1*g1 + g2*g2;
assert(norm2>=0.0);
// if the gradient is too small, do not attempt correction
if ( norm2 < 1.e-6 ) return true;
const double proj = v1 * g1 + v2 * g2;
const double err = fabs(proj)/sqrt(norm2);
#if DEBUG_CONSTRAINTS
cout << " DistanceConstraint::enforce_v: " << name1_ << " " << name2_ << endl;
cout << " DistanceConstraint::enforce_v: r1 = " << r1 << endl;
cout << " DistanceConstraint::enforce_v: r2 = " << r2 << endl;
cout << " DistanceConstraint::enforce_v: v1 = "
<< v1[0] << " " << v1[1] << " " << v1[2] << endl;
cout << " DistanceConstraint::enforce_v: v2 = "
<< v2[0] << " " << v2[1] << " " << v2[2] << endl;
cout << " DistanceConstraint::enforce_v: tol = " << tol_ << endl;
cout << " DistanceConstraint::enforce_v: err = " << err
<< endl;
#endif
if ( err < tol_ ) return true;
// make one shake iteration
const double den = m1_inv_ * g1 * g1 +
m2_inv_ * g2 * g2;
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;
return false;
}
////////////////////////////////////////////////////////////////////////////////
void DistanceConstraint::compute_force(const vector > &r0,
const vector > &f)
{
const double* pr1 = &r0[is1_][3*ia1_];
const double* pr2 = &r0[is2_][3*ia2_];
D3vector r1(pr1);
D3vector r2(pr2);
const double* pf1 = &f[is1_][3*ia1_];
const double* pf2 = &f[is2_][3*ia2_];
D3vector f1(pf1);
D3vector f2(pf2);
// compute gradient at r
D3vector r12(r1-r2);
D3vector g1,g2;
g1 = 2.0 * r12;
g2 = -g1;
const double norm2 = g1*g1;
assert(norm2>=0.0);
if ( norm2 == 0.0 )
{
force_ = 0.0;
return;
}
const double norm = sqrt(norm2);
D3vector e1(g1/norm);
D3vector e2(-e1);
const double proj1 = f1*e1;
const double proj2 = f2*e2;
force_ = -0.5*(proj1+proj2);
}
////////////////////////////////////////////////////////////////////////////////
ostream& DistanceConstraint::print( ostream &os )
{
os.setf(ios::left,ios::adjustfield);
os << " ";
return os;
}