////////////////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////////////////
//
// ConstraintSet.C
//
////////////////////////////////////////////////////////////////////////////////
#include "ConstraintSet.h"
#include "PositionConstraint.h"
#include "DistanceConstraint.h"
#include "AngleConstraint.h"
#include "TorsionConstraint.h"
#include "Atom.h"
#include "AtomSet.h"
#include "Context.h"
#include
#include
#include // atof
using namespace std;
const int constraints_maxiter = 50;
////////////////////////////////////////////////////////////////////////////////
ConstraintSet::~ConstraintSet(void)
{
for ( int ic = 0; ic < constraint_list.size(); ic++ )
delete constraint_list[ic];
}
////////////////////////////////////////////////////////////////////////////////
bool ConstraintSet::define_constraint(AtomSet &atoms, int argc, char **argv)
{
enum constraint_type { unknown, position_type, distance_type,
angle_type, torsion_type }
type = unknown;
const double position_tolerance = 1.0e-7;
const double distance_tolerance = 1.0e-7;
const double angle_tolerance = 1.0e-4;
const bool onpe0 = ctxt_.onpe0();
// argv[0] == "constraint"
// argv[1] == "define"
// argv[2] == {"position", "distance", "angle", "torsion"}
// argv[3] == constraint name
// argv[4-(5,6,7)] == atom names
// argv[{5,6,7}] == {distance,angle,angle}
// argv[{6,7,8}] == velocity
if ( argc < 2 )
{
if ( onpe0 )
{
cout << " Use: constraint define position constraint_name atom_name"
<< endl;
cout << " Use: constraint define distance constraint_name "
<< "atom_name1 atom_name2 distance_value [velocity]"
<< endl;
cout << " constraint define angle constraint_name "
<< "name1 name2 name3 angle_value [velocity]"
<< endl;
cout << " constraint define torsion constraint_name "
<< "name1 name2 name3 name4 angle_value"
<< " [velocity] "
<< endl;
}
return false;
}
const string constraint_type = argv[2];
if ( constraint_type == "position" )
{
type = position_type;
}
else if ( constraint_type == "distance" )
{
type = distance_type;
}
else if ( constraint_type == "angle" )
{
type = angle_type;
}
else if ( constraint_type == "torsion" )
{
type = torsion_type;
}
else
{
if ( onpe0 )
cout << " Incorrect constraint type " << constraint_type << endl;
return false;
}
if ( type == position_type )
{
// define position name A
if ( argc != 5 )
{
if ( onpe0 )
cout << " Incorrect number of arguments for position constraint"
<< endl;
return false;
}
string name = argv[3];
string name1 = argv[4];
Atom *a1 = atoms.findAtom(name1);
if ( a1 == 0 )
{
if ( onpe0 )
{
cout << " ConstraintSet: could not find atom " << name1 << endl;
cout << " ConstraintSet: could not define constraint" << endl;
}
return false;
}
// check if constraint is already defined
bool found = false;
Constraint *pc = 0;
for ( int i = 0; i < constraint_list.size(); i++ )
{
pc = constraint_list[i];
assert(pc != 0);
// check if a constraint with same name or with same atom is defined
if ( pc->type() == "position" )
found = ( pc->name() == name ) || ( pc->names(0) == name1 );
}
if ( found )
{
if ( onpe0 )
cout << " ConstraintSet: constraint is already defined:\n"
<< " cannot define constraint" << endl;
return false;
}
else
{
PositionConstraint *c =
new PositionConstraint(name,name1,position_tolerance);
constraint_list.push_back(c);
}
}
else if ( type == distance_type )
{
// define distance name A B value
// define distance name A B value velocity
if ( argc < 7 || argc > 8 )
{
if ( onpe0 )
cout << " Incorrect number of arguments for distance constraint"
<< endl;
return false;
}
double distance, velocity=0.0;
string name = argv[3];
string name1 = argv[4];
string name2 = argv[5];
Atom *a1 = atoms.findAtom(name1);
Atom *a2 = atoms.findAtom(name2);
if ( a1 == 0 || a2 == 0 )
{
if ( onpe0 )
{
if ( a1 == 0 )
cout << " ConstraintSet: could not find atom " << name1 << endl;
if ( a2 == 0 )
cout << " ConstraintSet: could not find atom " << name2 << endl;
cout << " ConstraintSet: could not define constraint" << endl;
}
return false;
}
if ( name1 == name2 )
{
if ( onpe0 )
cout << " ConstraintSet: cannot define distance constraint between "
<< name1 << " and " << name2 << endl;
return false;
}
distance = atof(argv[6]);
if ( argc == 8 )
{
velocity = atof(argv[7]);
}
if ( distance <= 0.0 )
{
if ( onpe0 )
cout << " ConstraintSet: distance must be positive" << endl
<< " ConstraintSet: could not define constraint" << endl;
return false;
}
// check if constraint is already defined
bool found = false;
Constraint *pc = 0;
for ( int i = 0; i < constraint_list.size(); i++ )
{
pc = constraint_list[i];
assert(pc != 0);
// check if a constraint (name1,name2) or (name2,name1) is defined
if ( pc->type() == "distance" )
found = ( pc->names(0) == name1 && pc->names(1) == name2 ) ||
( pc->names(1) == name1 && pc->names(0) == name2 ) ||
( pc->name() == name );
}
if ( found )
{
if ( onpe0 )
cout << " ConstraintSet: constraint is already defined:\n"
<< " cannot define constraint" << endl;
return false;
}
else
{
DistanceConstraint *c =
new DistanceConstraint(name,name1,name2,distance,
velocity,distance_tolerance);
constraint_list.push_back(c);
}
}
else if ( type == angle_type )
{
// constraint define angle name A B C value
// constraint define angle name A B C value velocity
if ( argc < 8 || argc > 9 )
{
if ( onpe0 )
cout << " Incorrect number of arguments for angle constraint"
<< endl;
return false;
}
string name = argv[3];
string name1 = argv[4];
string name2 = argv[5];
string name3 = argv[6];
Atom *a1 = atoms.findAtom(name1);
Atom *a2 = atoms.findAtom(name2);
Atom *a3 = atoms.findAtom(name3);
if ( a1 == 0 || a2 == 0 || a3 == 0 )
{
if ( onpe0 )
{
if ( a1 == 0 )
cout << " ConstraintSet: could not find atom " << name1 << endl;
if ( a2 == 0 )
cout << " ConstraintSet: could not find atom " << name2 << endl;
if ( a3 == 0 )
cout << " ConstraintSet: could not find atom " << name3 << endl;
cout << " ConstraintSet: could not define constraint" << endl;
}
return false;
}
if ( name1 == name2 || name1 == name3 || name2 == name3)
{
if ( onpe0 )
cout << " ConstraintSet: cannot define angle constraint between "
<< name1 << " " << name2 << " and " << name3 << endl;
return false;
}
const double angle = atof(argv[7]);
double velocity = 0.0;
if ( argc == 9 )
{
velocity = atof(argv[8]);
}
if ( angle < 0.0 || angle > 180.0 )
{
if ( onpe0 )
cout << " ConstraintSet: angle must be in [0,180]" << endl
<< " ConstraintSet: could not define constraint" << endl;
return false;
}
// check if equivalent constraint is already defined
bool found = false;
Constraint *pc = 0;
for ( int i = 0; i < constraint_list.size(); i++ )
{
pc = constraint_list[i];
assert(pc != 0);
// check if a constraint (name1,name2,name3) or
// (name3,name2,name1) is defined
if ( pc->type() == "angle" )
found = ( pc->names(0) == name1 &&
pc->names(1) == name2 &&
pc->names(2) == name3 ) ||
( pc->names(0) == name3 &&
pc->names(1) == name2 &&
pc->names(2) == name1 ) ||
( pc->name() == name );
}
if ( found )
{
if ( onpe0 )
cout << " ConstraintSet:set_constraint: an angle constraint "
<< name1 << " " << name2 << " " << name3
<< " was found" << endl
<< " ConstraintSet: cannot define constraint" << endl;
return false;
}
else
{
AngleConstraint *c =
new AngleConstraint(name, name1,name2,name3,angle,
velocity,angle_tolerance);
constraint_list.push_back(c);
}
}
else if ( type == torsion_type )
{
// constraint define torsion name A B C D angle
// constraint define torsion name A B C D angle velocity
if ( argc < 9 || argc > 10 )
{
if ( onpe0 )
cout << " Incorrect number of arguments for torsion constraint"
<< endl;
return false;
}
string name = argv[3];
string name1 = argv[4];
string name2 = argv[5];
string name3 = argv[6];
string name4 = argv[7];
Atom *a1 = atoms.findAtom(name1);
Atom *a2 = atoms.findAtom(name2);
Atom *a3 = atoms.findAtom(name3);
Atom *a4 = atoms.findAtom(name4);
if ( a1 == 0 || a2 == 0 || a3 == 0 || a4 == 0 )
{
if ( onpe0 )
{
if ( a1 == 0 )
cout << " ConstraintSet: could not find atom " << name1 << endl;
if ( a2 == 0 )
cout << " ConstraintSet: could not find atom " << name2 << endl;
if ( a3 == 0 )
cout << " ConstraintSet: could not find atom " << name3 << endl;
if ( a4 == 0 )
cout << " ConstraintSet: could not find atom " << name4 << endl;
cout << " ConstraintSet: could not define constraint" << endl;
}
return false;
}
if ( name1 == name2 || name1 == name3 || name1 == name4 ||
name2 == name3 || name2 == name4 || name3 == name4 )
{
if ( onpe0 )
cout << " ConstraintSet: cannot define torsion constraint using "
<< name1 << " " << name2 << " " << name3 << " " << name4
<< endl;
return false;
}
double angle = atof(argv[8]);
if ( angle > 180.0 )
while ( angle > 180.0 ) angle -= 360.0;
else if ( angle < -180.0 )
while ( angle < -180.0 ) angle += 360.0;
double velocity = 0.0;
if ( argc == 10 )
{
velocity = atof(argv[9]);
}
// check if equivalent constraint is already defined
bool found = false;
Constraint *pc = 0;
for ( int i = 0; i < constraint_list.size(); i++ )
{
pc = constraint_list[i];
assert(pc != 0);
// check if an equivalent constraint (name1,name2,name3,name4) or
// (name4,name3,name2,name1) is defined
if ( pc->type() == "angle" )
found = ( pc->names(0) == name1 &&
pc->names(1) == name2 &&
pc->names(2) == name3 &&
pc->names(3) == name4 ) ||
( pc->names(0) == name4 &&
pc->names(1) == name3 &&
pc->names(2) == name2 &&
pc->names(3) == name1 ) ||
( pc->name() == name );
}
if ( found )
{
if ( onpe0 )
cout << " ConstraintSet: a torsion constraint "
<< name1 << " " << name2 << " " << name3 << " " << name4
<< " is already defined" << endl
<< " ConstraintSet: cannot define constraint" << endl;
return false;
}
else
{
TorsionConstraint *c =
new TorsionConstraint(name,name1,name2,name3,name4,
angle,velocity,angle_tolerance);
constraint_list.push_back(c);
}
}
else
{
if ( onpe0 )
cout << " ConstraintSet::set_constraint: internal error" << endl;
return false;
}
// update total number of blocked degrees of freedom
ndofs_ += constraint_list.back()->dofs();
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool ConstraintSet::set_constraint(int argc, char **argv)
{
const bool onpe0 = ctxt_.onpe0();
assert(argc==4||argc==5);
// argv[0] == "constraint"
// argv[1] == "set"
// argv[2] == constraint_name
// argv[3] == value
// argv[4] (optional) == velocity
string name = argv[2];
const double value = atof(argv[3]);
double velocity = 0.0;
const bool set_velocity = ( argc == 5 );
if ( set_velocity ) velocity = atof(argv[4]);
// check if constraint is already defined
bool found = false;
vector::iterator i = constraint_list.begin();
while ( !found && i != constraint_list.end() )
{
Constraint *pc = *i;
assert(pc != 0);
if ( pc->name() == name )
{
found = true;
pc->set_value(value);
if ( set_velocity ) pc->set_velocity(velocity);
}
i++;
}
if ( !found )
{
if ( onpe0 )
cout << " ConstraintSet: no such constraint" << endl;
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
bool ConstraintSet::delete_constraint(int argc, char **argv)
{
assert(argc==3);
// argv[0] == "constraint"
// argv[1] == "delete"
// argv[2] == constraint_name
string name = argv[2];
const bool onpe0 = ctxt_.onpe0();
bool found = false;
// note next loop in reverse: avoid use of invalidated iterators
// after erase operation
vector::iterator i = constraint_list.begin();
while ( !found && i != constraint_list.end() )
{
Constraint *pc = *i;
assert(pc != 0);
// note structure of if else test to avoid incrementing
// invalidated iterator after erase (see Meyers STL, p.45)
if ( pc->name() == name )
{
found = true;
// update total number of blocked degrees of freedom
ndofs_ -= pc->dofs();
delete pc;
// remove constraint pointer from the list
// note: iterator is incremented before erasing, remains valid
constraint_list.erase(i++);
}
else
{
i++;
}
}
if ( !found )
{
if ( onpe0 ) cout << " No such constraint" << endl;
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
void ConstraintSet::list_constraints(ostream &os)
{
if ( !constraint_list.empty() )
{
os << " " << endl;
for ( int i = 0; i < constraint_list.size(); i++ )
{
Constraint *c = constraint_list[i];
os << *c << endl;
}
os << " " << endl;
}
}
////////////////////////////////////////////////////////////////////////////////
void ConstraintSet::enforce(AtomSet& atoms)
{
vector > r0,rp,v0;
setup(atoms);
atoms.get_positions(r0);
rp=r0;
atoms.get_velocities(v0);
enforce_r(r0,rp);
atoms.set_positions(rp);
enforce_v(r0,v0);
atoms.set_velocities(v0);
}
////////////////////////////////////////////////////////////////////////////////
void ConstraintSet::enforce_r(const vector > &r0,
vector > &rp)
{
const bool onpe0 = ctxt_.onpe0();
int iter = 0;
bool done = false;
while ( !done && (iter < constraints_maxiter) )
{
done = true;
for ( int i = 0; i < constraint_list.size(); i++ )
{
Constraint *c = constraint_list[i];
bool b = c->enforce_r(r0,rp);
done &= b;
}
iter++;
}
if ( !done )
{
if ( onpe0 )
cout << " ConstraintSet: could not enforce position constraints in "
<< constraints_maxiter << " iterations" << endl;
}
}
////////////////////////////////////////////////////////////////////////////////
void ConstraintSet::enforce_v(const vector > &r0,
vector > &v0)
{
const bool onpe0 = ctxt_.onpe0();
int iter = 0;
bool done = false;
while ( !done && (iter < constraints_maxiter) )
{
done = true;
for ( int i = 0; i < constraint_list.size(); i++ )
{
bool b = constraint_list[i]->enforce_v(r0,v0);
done &= b;
}
iter++;
}
if ( !done )
{
if ( onpe0 )
cout << " ConstraintSet: could not enforce velocity constraints in "
<< constraints_maxiter << " iterations" << endl;
}
}
////////////////////////////////////////////////////////////////////////////////
void ConstraintSet::compute_forces(const vector > &r0,
const vector > &f)
{
for ( int i = 0; i < constraint_list.size(); i++ )
{
constraint_list[i]->compute_force(r0,f);
}
}
////////////////////////////////////////////////////////////////////////////////
void ConstraintSet::update_constraints(double dt)
{
for ( int i = 0; i < constraint_list.size(); i++ )
{
constraint_list[i]->update(dt);
}
}
////////////////////////////////////////////////////////////////////////////////
void ConstraintSet::setup(AtomSet& atoms)
{
for ( int i = 0; i < constraint_list.size(); i++ )
{
constraint_list[i]->setup(atoms);
}
}
////////////////////////////////////////////////////////////////////////////////
void ConstraintSet::reset(void)
{
for ( int i = 0; i < constraint_list.size(); i++ )
delete constraint_list[i];
ndofs_ = 0;
constraint_list.resize(0);
}