git-svn-id: svn://svn.icms.temple.edu/lammps-ro/trunk@8165 f3b2605a-c512-4ea7-a41b-209d697bcdaa

This commit is contained in:
sjplimp 2012-05-23 16:24:40 +00:00
parent aa8aeb0294
commit 5ea3c4d23a
6 changed files with 1615 additions and 0 deletions

45
src/USER-COLVARS/Install.sh Executable file
View File

@ -0,0 +1,45 @@
# Install/unInstall package files in LAMMPS
# edit 2 Makefile.package files to include/exclude CUDA info
# do not install child files if parent does not exist
if (test $1 = 1) then
if (test -e ../Makefile.package) then
sed -i -e 's/[^ \t]*colvars[^ \t]* //g' ../Makefile.package
sed -i -e 's|^PKG_INC =[ \t]*|&-I..\/..\/lib\/colvars |' ../Makefile.package
sed -i -e 's|^PKG_PATH =[ \t]*|&-L..\/..\/lib\/colvars |' ../Makefile.package
sed -i -e 's|^PKG_LIB =[ \t]*|&-lcolvars |' ../Makefile.package
sed -i -e 's|^PKG_SYSINC =[ \t]*|&$(colvars_SYSINC) |' ../Makefile.package
sed -i -e 's|^PKG_SYSLIB =[ \t]*|&$(colvars_SYSLIB) |' ../Makefile.package
sed -i -e 's|^PKG_SYSPATH =[ \t]*|&$(colvars_SYSPATH) |' ../Makefile.package
fi
if (test -e ../Makefile.package.settings) then
sed -i -e '/^include.*colvars.*$/d' ../Makefile.package.settings
# multiline form needed for BSD sed on Macs
sed -i -e '4 i \
include ..\/..\/lib\/colvars\/Makefile.lammps\
' ../Makefile.package.settings
fi
cp colvarproxy_lammps.h ..
cp colvarproxy_lammps.cpp ..
cp fix_colvars.h ..
cp fix_colvars.cpp ..
elif (test $1 = 0) then
if (test -e ../Makefile.package) then
sed -i -e 's/[^ \t]*colvars[^ \t]* //g' ../Makefile.package
fi
if (test -e ../Makefile.package.settings) then
sed -i -e '/^include.*colvars.*$/d' ../Makefile.package.settings
fi
rm -f ../colvarproxy_lammps.h
rm -f ../colvarproxy_lammps.cpp
rm -f ../fix_colvars.h
rm -f ../fix_colvars.cpp
fi

42
src/USER-COLVARS/README Normal file
View File

@ -0,0 +1,42 @@
This package implements the "fix colvars" command which can be used
in a LAMMPS input script.
This fix allows to use "collective variables" to implement
Adaptive Biasing Force, Metadynamics, Steered MD, Umbrella
Sampling and Restraints. This code consists of two parts:
- a portable collective variable module library written
and maintained by Giacomo Fiorin (ICMS, Temple University,
Philadelphia, PA, USA) and Jerome Henin (LISM, CNRS,
Marseille, France).
This code is located in the directory lib/colvars and
needs to be compiled first. More info about this code
can be found in the publication:
Exploring Multidimensional Free Energy Landscapes Using
Time-Dependent Biases on Collective Variables,
J. Hénin, G. Fiorin, C. Chipot, and M. L. Klein,
J. Chem. Theory Comput., 6, 35-47 (2010).
- the colvars fix and a thin interface layer, which exchanges
information between LAMMPS and the collective variable module.
See the doc page of fix colvars for more details
There are example scripts for using this package in
examples/USER/colvars
This is a very new interface that does not yet support all
features in the module and will see future optimizations
and improvements. The colvars module library is also available
in NAMD has been thoroughly used and tested there. Bugs and
problems are likely due to the interface layers code.
Thus the current version of this package should be considered
beta quality.
The person who created this package is Axel Kohlmeyer at Temple U
(akohlmey at gmail.com). Contact him directly if you have questions.
---------------------------------
version: 0.1 / 2012-03-03

View File

@ -0,0 +1,425 @@
#include "mpi.h"
#include "lammps.h"
#include "atom.h"
#include "comm.h"
#include "error.h"
#include "output.h"
#include "random_park.h"
#include "fix_colvars.h"
#include "colvarmodule.h"
#include "colvaratoms.h"
#include "colvarproxy.h"
#include "colvarproxy_lammps.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#define HASH_FAIL -1
////////////////////////////////////////////////////////////////////////
// local helper functions
// safely move filename to filename.extension
static void my_backup_file(const char *filename, const char *extension)
{
struct stat sbuf;
if (stat(filename, &sbuf) == 0) {
if ( ! extension ) extension = ".BAK";
char *backup = new char[strlen(filename)+strlen(extension)+1];
strcpy(backup, filename);
strcat(backup, extension);
#if defined(_WIN32) && !defined(__CYGWIN__)
remove(backup);
#endif
if ( rename(filename,backup) ) {
char *sys_err_msg = strerror(errno);
if ( !sys_err_msg ) sys_err_msg = (char *) "(unknown error)";
fprintf(stderr,"Error renaming file %s to %s: %s\n",
filename, backup, sys_err_msg);
}
delete [] backup;
}
}
////////////////////////////////////////////////////////////////////////
colvarproxy_lammps::colvarproxy_lammps(LAMMPS_NS::LAMMPS *lmp,
const char *conf_file,
const char *inp_name,
const char *out_name,
const int seed,
const double temp,
const int *typemap)
: _lmp(lmp), _typemap(typemap)
{
if (cvm::debug())
log("Initializing the colvars proxy object.\n");
_random = new LAMMPS_NS::RanPark(lmp,seed);
first_timestep=true;
system_force_requested=false;
previous_step=-1;
t_target=temp;
do_exit=false;
// set input restart name and strip the extension, if present
input_prefix_str = std::string(inp_name ? inp_name : "");
if (input_prefix_str.rfind(".colvars.state") != std::string::npos)
input_prefix_str.erase(input_prefix_str.rfind(".colvars.state"),
std::string(".colvars.state").size());
// output prefix is always given
output_prefix_str = std::string(out_name);
// not so for restarts
restart_prefix_str = std::string("rest");
// try to extract a restart prefix from a potential restart command.
if (_lmp->output->restart_every_single > 0) {
restart_prefix_str = std::string(_lmp->output->restart1);
} else if (_lmp->output->restart_every_double > 0) {
restart_prefix_str = std::string(_lmp->output->restart2a);
}
// trim off unwanted stuff from the restart prefix
if (restart_prefix_str.rfind(".*") != std::string::npos)
restart_prefix_str.erase(restart_prefix_str.rfind(".*"),2);
// create the colvarmodule instance
colvars = new colvarmodule(conf_file,this);
if (_lmp->update->ntimestep != 0) {
cvm::log ("Initializing step number as firstTimestep.\n");
colvars->it = colvars->it_restart = _lmp->update->ntimestep;
}
if (cvm::debug()) {
log("colvars_atoms = "+cvm::to_str(colvars_atoms)+"\n");
log("colvars_atoms_ncopies = "+cvm::to_str(colvars_atoms_ncopies)+"\n");
log("positions = "+cvm::to_str(positions)+"\n");
log("applied_forces = "+cvm::to_str(applied_forces)+"\n");
log(cvm::line_marker);
log("Info: done initializing the colvars proxy object.\n");
}
// this is only valid through the constructor.
_typemap = NULL;
}
colvarproxy_lammps::~colvarproxy_lammps()
{
delete _random;
if (colvars != NULL) {
colvars->write_output_files();
delete colvars;
colvars = NULL;
}
}
// trigger colvars computation
double colvarproxy_lammps::compute()
{
if (first_timestep) {
first_timestep = false;
} else {
// Use the time step number inherited from LAMMPS
if ( _lmp->update->ntimestep - previous_step == 1 )
colvars->it++;
// Other cases could mean:
// - run 0
// - beginning of a new run statement
// then the internal counter should not be incremented
}
previous_step = _lmp->update->ntimestep;
if (cvm::debug()) {
cvm::log(cvm::line_marker+
"colvarproxy_lammps, step no. "+cvm::to_str(colvars->it)+"\n"+
"Updating internal data.\n");
}
// call the collective variable module
colvars->calc();
#if 0
for (int i=0; i < colvars_atoms.size(); ++i) {
fprintf(stderr,"CV: atom %d/%d/%d pos: %g %g %g for: %g %g %g\n",
colvars_atoms[i], colvars_atoms_ncopies[i],
positions[i].type, positions[i].x, positions[i].y, positions[i].z,
applied_forces[i].x, applied_forces[i].y, applied_forces[i].z);
}
#endif
return bias_energy;
}
cvm::rvector colvarproxy_lammps::position_distance(cvm::atom_pos const &pos1,
cvm::atom_pos const &pos2)
{
double xtmp = pos2.x - pos1.x;
double ytmp = pos2.y - pos1.y;
double ztmp = pos2.z - pos1.z;
_lmp->domain->minimum_image(xtmp,ytmp,ztmp);
return cvm::rvector(xtmp, ytmp, ztmp);
}
cvm::real colvarproxy_lammps::position_dist2(cvm::atom_pos const &pos1,
cvm::atom_pos const &pos2)
{
double xtmp = pos2.x - pos1.x;
double ytmp = pos2.y - pos1.y;
double ztmp = pos2.z - pos1.z;
_lmp->domain->minimum_image(xtmp,ytmp,ztmp);
return cvm::real(xtmp*xtmp + ytmp*ytmp + ztmp*ztmp);
}
inline void colvarproxy_lammps::select_closest_image(cvm::atom_pos &pos,
cvm::atom_pos const &ref)
{
double xtmp = pos.x - ref.x;
double ytmp = pos.y - ref.y;
double ztmp = pos.z - ref.z;
_lmp->domain->minimum_image(xtmp,ytmp,ztmp);
pos.x = ref.x + xtmp;
pos.y = ref.y + ytmp;
pos.z = ref.z + ztmp;
}
void colvarproxy_lammps::log(std::string const &message)
{
std::istringstream is(message);
std::string line;
while (std::getline(is, line)) {
if (_lmp->screen)
fprintf(_lmp->screen,"colvars: %s\n",line.c_str());
if (_lmp->logfile)
fprintf(_lmp->logfile,"colvars: %s\n",line.c_str());
}
}
void colvarproxy_lammps::fatal_error(std::string const &message)
{
log(message);
if (!cvm::debug())
log("If this error message is unclear, try recompiling the "
"colvars library and LAMMPS with -DCOLVARS_DEBUG.\n");
_lmp->error->one(FLERR,
"Fatal error in the collective variables module.\n");
}
void colvarproxy_lammps::exit(std::string const &message)
{
log(message);
log("Request to exit the simulation made.\n");
do_exit=true;
}
enum e_pdb_field {
e_pdb_none,
e_pdb_occ,
e_pdb_beta,
e_pdb_x,
e_pdb_y,
e_pdb_z,
e_pdb_ntot
};
e_pdb_field pdb_field_str2enum(std::string const &pdb_field_str)
{
e_pdb_field pdb_field = e_pdb_none;
if (colvarparse::to_lower_cppstr(pdb_field_str) ==
colvarparse::to_lower_cppstr("O")) {
pdb_field = e_pdb_occ;
}
if (colvarparse::to_lower_cppstr(pdb_field_str) ==
colvarparse::to_lower_cppstr("B")) {
pdb_field = e_pdb_beta;
}
if (colvarparse::to_lower_cppstr(pdb_field_str) ==
colvarparse::to_lower_cppstr("X")) {
pdb_field = e_pdb_x;
}
if (colvarparse::to_lower_cppstr(pdb_field_str) ==
colvarparse::to_lower_cppstr("Y")) {
pdb_field = e_pdb_y;
}
if (colvarparse::to_lower_cppstr(pdb_field_str) ==
colvarparse::to_lower_cppstr("Z")) {
pdb_field = e_pdb_z;
}
if (pdb_field == e_pdb_none) {
cvm::fatal_error("Error: unsupported PDB field, \""+
pdb_field_str+"\".\n");
}
return pdb_field;
}
void colvarproxy_lammps::load_coords(char const *pdb_filename,
std::vector<cvm::atom_pos> &pos,
const std::vector<int> &indices,
std::string const pdb_field_str,
double const pdb_field_value)
{
cvm::fatal_error("Reading collective variable coordinates "
"from a PDB file is currently not supported.\n");
}
void colvarproxy_lammps::load_atoms(char const *pdb_filename,
std::vector<cvm::atom> &atoms,
std::string const pdb_field_str,
double const pdb_field_value)
{
cvm::fatal_error("Selecting collective variable atoms "
"from a PDB file is currently not supported.\n");
}
void colvarproxy_lammps::backup_file(char const *filename)
{
if (std::string(filename).rfind(std::string(".colvars.state"))
!= std::string::npos) {
my_backup_file(filename, ".old");
} else {
my_backup_file(filename, ".BAK");
}
}
int colvarproxy_lammps::init_lammps_atom(const int &aid, cvm::atom *atom)
{
atom->id = aid;
atom->mass = _lmp->atom->mass[_typemap[aid]];
for (size_t i = 0; i < colvars_atoms.size(); i++) {
if (colvars_atoms[i] == aid) {
// this atom id was already recorded
colvars_atoms_ncopies[i] += 1;
return i;
}
}
// allocate a new slot for this atom
colvars_atoms_ncopies.push_back(1);
colvars_atoms.push_back(aid);
struct commdata c;
c.tag = aid;
c.type = _typemap[aid];
c.x = c.y = c.z = 0.0;
positions.push_back(c);
total_forces.push_back(c);
applied_forces.push_back(c);
return colvars_atoms.size()-1;
}
// atom member functions, LAMMPS specific implementations
cvm::atom::atom(const int &id)
{
if (cvm::debug())
cvm::log("Adding atom "+cvm::to_str(id)+
" for collective variables calculation.\n");
if (id < 0)
cvm::fatal_error("Error: invalid atom ID specified, "+
cvm::to_str(id)+"\n");
int idx = ((colvarproxy_lammps *) cvm::proxy)->init_lammps_atom(id,this);
if (idx < 0)
cvm::fatal_error("Error: atom ID , "+cvm::to_str(id)+" does not exist.\n");
this->index = idx;
if (cvm::debug())
cvm::log("The index of this atom in the colvarproxy_lammps arrays is "+
cvm::to_str(this->index)+".\n");
this->reset_data();
}
/// For AMBER topologies, the segment id is automatically set to
/// "MAIN" (the segment id assigned by NAMD's AMBER topology parser),
/// and is therefore optional when an AMBER topology is used
cvm::atom::atom(cvm::residue_id const &residue,
std::string const &atom_name,
std::string const &segment_id)
{
cvm::fatal_error("Creating collective variable atoms "
"from a PDB file is currently not supported.\n");
}
// copy constructor
cvm::atom::atom(cvm::atom const &a)
: index(a.index), id(a.id), mass(a.mass)
{
// init_lammps_atom() has already been called by a's constructor, no
// need to call it again
// need to increment the counter anyway
colvarproxy_lammps *cp = (colvarproxy_lammps *) cvm::proxy;
cp->colvars_atoms_ncopies[this->index] += 1;
}
cvm::atom::~atom()
{
colvarproxy_lammps *cp = (colvarproxy_lammps *) cvm::proxy;
if (cp->colvars_atoms_ncopies[this->index] > 0)
cp->colvars_atoms_ncopies[this->index] -= 1;
}
void cvm::atom::read_position()
{
colvarproxy_lammps const * const cp = (colvarproxy_lammps *) cvm::proxy;
this->pos.x = cp->positions[this->index].x;
this->pos.y = cp->positions[this->index].y;
this->pos.z = cp->positions[this->index].z;
}
void cvm::atom::read_velocity()
{
cvm::fatal_error("Error: read_velocity is not yet implemented.\n");
}
void cvm::atom::read_system_force()
{
colvarproxy_lammps const * const cp = (colvarproxy_lammps *) cvm::proxy;
this->system_force.x = cp->total_forces[this->index].x
- cp->applied_forces[this->index].x;
this->system_force.y = cp->total_forces[this->index].y
- cp->applied_forces[this->index].y;
this->system_force.z = cp->total_forces[this->index].z
- cp->applied_forces[this->index].z;
}
void cvm::atom::apply_force(cvm::rvector const &new_force)
{
colvarproxy_lammps *cp = (colvarproxy_lammps *) cvm::proxy;
cp->applied_forces[this->index].x = new_force.x;
cp->applied_forces[this->index].y = new_force.y;
cp->applied_forces[this->index].z = new_force.z;
}

View File

@ -0,0 +1,141 @@
#ifndef COLVARPROXY_LAMMPS_H
#define COLVARPROXY_LAMMPS_H
#include "colvarmodule.h"
#include "colvarproxy.h"
#include "lammps.h"
#include "domain.h"
#include "force.h"
#include "random_park.h"
#include "update.h"
#include <string>
#include <vector>
#include <iostream>
/* struct for packed data communication of coordinates and forces. */
struct commdata {
int tag,type;
double x,y,z;
};
inline std::ostream & operator<< (std::ostream &out, const commdata &cd)
{
out << " (" << cd.tag << "/" << cd.type << ": "
<< cd.x << ", " << cd.y << ", " << cd.z << ") ";
return out;
};
/// \brief Communication between colvars and LAMMPS
/// (implementation of \link colvarproxy \endlink)
class colvarproxy_lammps : public colvarproxy {
// LAMMPS specific data objects and flags
protected:
// pointers to LAMMPS class instances
class LAMMPS_NS::LAMMPS *_lmp;
class LAMMPS_NS::RanPark *_random;
// pointers to LAMMPS provided storage
const int *_typemap;
// state of LAMMPS properties
double t_target;
double bias_energy;
int restart_every;
int previous_step;
std::string input_prefix_str;
std::string output_prefix_str;
std::string restart_prefix_str;
bool first_timestep;
bool system_force_requested;
bool do_exit;
std::vector<int> colvars_atoms;
std::vector<size_t> colvars_atoms_ncopies;
std::vector<struct commdata> positions;
std::vector<struct commdata> total_forces;
std::vector<struct commdata> applied_forces;
public:
friend class cvm::atom;
colvarproxy_lammps (LAMMPS_NS::LAMMPS *lmp, const char *, const char *,
const char *, const int, const double, const int *);
virtual ~colvarproxy_lammps();
// disable default and copy constructor
private:
colvarproxy_lammps() {};
colvarproxy_lammps(const colvarproxy_lammps &) {};
// methods for lammps to move data or trigger actions in the proxy
public:
void set_temperature(double t) { t_target = t; };
bool need_system_forces() const { return system_force_requested; };
bool want_exit() const { return do_exit; };
std::vector<int> * get_tags() { return &colvars_atoms; };
std::vector<struct commdata> * get_coords() { return &positions; };
std::vector<struct commdata> * get_forces() { return &applied_forces; };
std::vector<struct commdata> * get_oforce() { return &total_forces; };
// initialize atom structure
int init_lammps_atom(const int &, cvm::atom *);
// perform colvars computation. returns biasing energy
double compute();
// implementation of pure methods from base class
public:
inline cvm::real unit_angstrom() { return _lmp->force->angstrom; };
cvm::real boltzmann() { return _lmp->force->boltz; };
cvm::real temperature() { return t_target; };
cvm::real dt() { return _lmp->update->dt * _lmp->force->femtosecond; };
inline std::string input_prefix() { return input_prefix_str; };
inline std::string output_prefix() { return output_prefix_str; };
inline std::string restart_output_prefix() { return restart_prefix_str; };
inline size_t restart_frequency() { return restart_every; };
void add_energy (cvm::real energy) { bias_energy = energy; };
void request_system_force (bool yesno) { system_force_requested = yesno; };
void log (std::string const &message);
void fatal_error (std::string const &message);
void exit (std::string const &message);
cvm::rvector position_distance (cvm::atom_pos const &pos1,
cvm::atom_pos const &pos2);
cvm::real position_dist2 (cvm::atom_pos const &pos1,
cvm::atom_pos const &pos2);
void select_closest_image (cvm::atom_pos &pos,
cvm::atom_pos const &ref_pos);
void load_atoms(char const *filename,
std::vector<cvm::atom> &atoms,
std::string const pdb_field,
double const pdb_field_value = 0.0);
void load_coords(char const *filename,
std::vector<cvm::atom_pos> &pos,
const std::vector<int> &indices,
std::string const pdb_field,
double const pdb_field_value = 0.0);
void backup_file(char const *filename);
cvm::real rand_gaussian(void) { return _random->gaussian(); };
};
#endif
// Emacs
// Local Variables:
// mode: C++
// End:

View File

@ -0,0 +1,864 @@
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
http://lammps.sandia.gov, Sandia National Laboratories
Steve Plimpton, sjplimp@sandia.gov
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: Axel Kohlmeyer (TempleU)
------------------------------------------------------------------------- */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "fix_colvars.h"
#include "atom.h"
#include "comm.h"
#include "domain.h"
#include "error.h"
#include "group.h"
#include "memory.h"
#include "modify.h"
#include "respa.h"
#include "update.h"
#include "colvarproxy_lammps.h"
/* re-usable integer hash table code with static linkage. */
/** hash table top level data structure */
typedef struct inthash_t {
struct inthash_node_t **bucket; /* array of hash nodes */
int size; /* size of the array */
int entries; /* number of entries in table */
int downshift; /* shift cound, used in hash function */
int mask; /* used to select bits for hashing */
} inthash_t;
/** hash table node data structure */
typedef struct inthash_node_t {
int data; /* data in hash node */
int key; /* key for hash lookup */
struct inthash_node_t *next; /* next node in hash chain */
} inthash_node_t;
#define HASH_FAIL -1
#define HASH_LIMIT 0.5
/* initialize new hash table */
static void inthash_init(inthash_t *tptr, int buckets);
/* lookup entry in hash table */
static int inthash_lookup(void *tptr, int key);
/* insert an entry into hash table. */
static int inthash_insert(inthash_t *tptr, int key, int data);
/* delete the hash table */
static void inthash_destroy(inthash_t *tptr);
/* adapted sort for in-place sorting of map indices. */
static void id_sort(int *idmap, int left, int right);
/************************************************************************
* integer hash code:
************************************************************************/
/* inthash() - Hash function returns a hash number for a given key.
* tptr: Pointer to a hash table, key: The key to create a hash number for */
static int inthash(const inthash_t *tptr, int key) {
int hashvalue;
hashvalue = (((key*1103515249)>>tptr->downshift) & tptr->mask);
if (hashvalue < 0) {
hashvalue = 0;
}
return hashvalue;
}
/*
* rebuild_table_int() - Create new hash table when old one fills up.
*
* tptr: Pointer to a hash table
*/
static void rebuild_table_int(inthash_t *tptr) {
inthash_node_t **old_bucket, *old_hash, *tmp;
int old_size, h, i;
old_bucket=tptr->bucket;
old_size=tptr->size;
/* create a new table and rehash old buckets */
inthash_init(tptr, old_size<<1);
for (i=0; i<old_size; i++) {
old_hash=old_bucket[i];
while(old_hash) {
tmp=old_hash;
old_hash=old_hash->next;
h=inthash(tptr, tmp->key);
tmp->next=tptr->bucket[h];
tptr->bucket[h]=tmp;
tptr->entries++;
} /* while */
} /* for */
/* free memory used by old table */
free(old_bucket);
return;
}
/*
* inthash_init() - Initialize a new hash table.
*
* tptr: Pointer to the hash table to initialize
* buckets: The number of initial buckets to create
*/
void inthash_init(inthash_t *tptr, int buckets) {
/* make sure we allocate something */
if (buckets==0)
buckets=16;
/* initialize the table */
tptr->entries=0;
tptr->size=2;
tptr->mask=1;
tptr->downshift=29;
/* ensure buckets is a power of 2 */
while (tptr->size<buckets) {
tptr->size<<=1;
tptr->mask=(tptr->mask<<1)+1;
tptr->downshift--;
} /* while */
/* allocate memory for table */
tptr->bucket=(inthash_node_t **) calloc(tptr->size, sizeof(inthash_node_t *));
return;
}
/*
* inthash_lookup() - Lookup an entry in the hash table and return a pointer to
* it or HASH_FAIL if it wasn't found.
*
* tptr: Pointer to the hash table
* key: The key to lookup
*/
int inthash_lookup(void *ptr, int key) {
const inthash_t *tptr = (const inthash_t *) ptr;
int h;
inthash_node_t *node;
/* find the entry in the hash table */
h=inthash(tptr, key);
for (node=tptr->bucket[h]; node!=NULL; node=node->next) {
if (node->key == key)
break;
}
/* return the entry if it exists, or HASH_FAIL */
return(node ? node->data : HASH_FAIL);
}
/*
* inthash_insert() - Insert an entry into the hash table. If the entry already
* exists return a pointer to it, otherwise return HASH_FAIL.
*
* tptr: A pointer to the hash table
* key: The key to insert into the hash table
* data: A pointer to the data to insert into the hash table
*/
int inthash_insert(inthash_t *tptr, int key, int data) {
int tmp;
inthash_node_t *node;
int h;
/* check to see if the entry exists */
if ((tmp=inthash_lookup(tptr, key)) != HASH_FAIL)
return(tmp);
/* expand the table if needed */
while (tptr->entries>=HASH_LIMIT*tptr->size)
rebuild_table_int(tptr);
/* insert the new entry */
h=inthash(tptr, key);
node=(struct inthash_node_t *) malloc(sizeof(inthash_node_t));
node->data=data;
node->key=key;
node->next=tptr->bucket[h];
tptr->bucket[h]=node;
tptr->entries++;
return HASH_FAIL;
}
/*
* inthash_destroy() - Delete the entire table, and all remaining entries.
*
*/
void inthash_destroy(inthash_t *tptr) {
inthash_node_t *node, *last;
int i;
for (i=0; i<tptr->size; i++) {
node = tptr->bucket[i];
while (node != NULL) {
last = node;
node = node->next;
free(last);
}
}
/* free the entire array of buckets */
if (tptr->bucket != NULL) {
free(tptr->bucket);
memset(tptr, 0, sizeof(inthash_t));
}
}
/************************************************************************
* integer list sort code:
************************************************************************/
/* sort for integer map. initial call id_sort(idmap, 0, natoms - 1); */
static void id_sort(int *idmap, int left, int right)
{
int pivot, l_hold, r_hold;
l_hold = left;
r_hold = right;
pivot = idmap[left];
while (left < right) {
while ((idmap[right] >= pivot) && (left < right))
right--;
if (left != right) {
idmap[left] = idmap[right];
left++;
}
while ((idmap[left] <= pivot) && (left < right))
left++;
if (left != right) {
idmap[right] = idmap[left];
right--;
}
}
idmap[left] = pivot;
pivot = left;
left = l_hold;
right = r_hold;
if (left < pivot)
id_sort(idmap, left, pivot-1);
if (right > pivot)
id_sort(idmap, pivot+1, right);
}
/***************************************************************/
using namespace LAMMPS_NS;
using namespace FixConst;
// initialize static class members
int FixColvars::instances=0;
/***************************************************************
create class and parse arguments in LAMMPS script. Syntax:
fix ID group-ID colvars <config_file> [optional flags...]
optional keyword value pairs:
input <input prefix> (for restarting/continuing, defaults to
NULL, but set to <output prefix> at end)
output <output prefix> (defaults to 'out')
seed <integer> (seed for RNG, defaults to '1966')
tstat <fix label> (label of thermostatting fix)
TODO: add (optional) arguments for RNG seed, temperature compute
***************************************************************/
FixColvars::FixColvars(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg)
{
if (narg < 4)
error->all(FLERR,"Illegal fix colvars command: too few arguments");
if (atom->tag_enable == 0)
error->all(FLERR,"Cannot use fix colvars without atom IDs defined");
if (atom->rmass_flag)
error->all(FLERR,"Cannot use fix colvars for atoms with rmass attribute");
if (instances)
error->all(FLERR,"Only one fix colvars can be active at a time");
++instances;
scalar_flag = 1;
global_freq = 1;
nevery = 1;
extscalar = 1;
me = comm->me;
conf_file = strdup(arg[3]);
rng_seed = 1966;
inp_name = NULL;
out_name = NULL;
tmp_name = NULL;
/* parse optional arguments */
int argsdone = 4;
while (argsdone+1 < narg) {
if (0 == strcmp(arg[argsdone], "input")) {
inp_name = strdup(arg[argsdone+1]);
} else if (0 == strcmp(arg[argsdone], "output")) {
out_name = strdup(arg[argsdone+1]);
} else if (0 == strcmp(arg[argsdone], "seed")) {
rng_seed = atoi(arg[argsdone+1]);
} else if (0 == strcmp(arg[argsdone], "tstat")) {
tmp_name = strdup(arg[argsdone+1]);
} else {
error->all(FLERR,"Unknown fix colvars parameter");
}
++argsdone; ++argsdone;
}
if (!out_name) out_name = strdup("out");
/* initialize various state variables. */
tstat_id = -1;
energy = 0.0;
nlevels_respa = 0;
num_coords = 0;
coords = forces = oforce = NULL;
comm_buf = NULL;
force_buf = NULL;
proxy = NULL;
idmap = NULL;
/* storage required to communicate a single coordinate or force. */
size_one = sizeof(struct commdata);
}
/*********************************
* Clean up on deleting the fix. *
*********************************/
FixColvars::~FixColvars()
{
memory->sfree(conf_file);
memory->sfree(inp_name);
memory->sfree(out_name);
memory->sfree(tmp_name);
deallocate();
--instances;
}
/* ---------------------------------------------------------------------- */
void FixColvars::deallocate()
{
memory->destroy(comm_buf);
if (proxy) {
delete proxy;
inthash_t *hashtable = (inthash_t *)idmap;
inthash_destroy(hashtable);
delete hashtable;
}
proxy = NULL;
idmap = NULL;
coords = NULL;
forces = NULL;
oforce = NULL;
comm_buf = NULL;
}
/* ---------------------------------------------------------------------- */
void FixColvars::post_run()
{
deallocate();
memory->sfree(inp_name);
inp_name = strdup(out_name);
}
/* ---------------------------------------------------------------------- */
int FixColvars::setmask()
{
int mask = 0;
mask |= THERMO_ENERGY;
mask |= MIN_POST_FORCE;
mask |= POST_FORCE;
mask |= POST_FORCE_RESPA;
mask |= POST_RUN;
mask |= END_OF_STEP;
return mask;
}
/* ---------------------------------------------------------------------- */
// initial setup of colvars run.
void FixColvars::init()
{
if (strstr(update->integrate_style,"respa"))
nlevels_respa = ((Respa *) update->integrate)->nlevels;
int i,nme,tmp,ndata;
MPI_Status status;
MPI_Request request;
// collect a list of atom type by atom id for the entire system.
// the colvar module requires this information to set masses. :-(
int *typemap,*type_buf;
int nlocal_max,tag_max,max;
const int * const tag = atom->tag;
const int * const type = atom->type;
int nlocal = atom->nlocal;
max=0;
for (i = 0; i < nlocal; i++) max = MAX(max,tag[i]);
MPI_Allreduce(&max,&tag_max,1,MPI_INT,MPI_MAX,world);
MPI_Allreduce(&nlocal,&nlocal_max,1,MPI_INT,MPI_MAX,world);
if (me == 0) {
typemap = new int[tag_max+1];
memset(typemap,0,sizeof(int)*tag_max);
}
type_buf = new int[2*nlocal_max];
if (me == 0) {
for (i=0; i<nlocal; ++i)
typemap[tag[i]] = type[i];
// loop over procs to receive and apply remote data
for (i=1; i < comm->nprocs; ++i) {
MPI_Irecv(type_buf, 2*nlocal_max, MPI_INT, i, 0, world, &request);
MPI_Send(&tmp, 0, MPI_INT, i, 0, world);
MPI_Wait(&request, &status);
MPI_Get_count(&status, MPI_INT, &ndata);
for (int k=0; k<ndata; k+=2)
typemap[type_buf[k]] = type_buf[k+1];
}
} else { // me != 0
// copy tag/type data into communication buffer
nme = 0;
for (i=0; i<nlocal; ++i) {
type_buf[nme] = tag[i];
type_buf[nme+1] = type[i];
nme +=2;
}
/* blocking receive to wait until it is our turn to send data. */
MPI_Recv(&tmp, 0, MPI_INT, 0, 0, world, &status);
MPI_Rsend(type_buf, nme, MPI_INT, 0, 0, world);
}
// now create and initialize the colvar proxy
if (me == 0) {
if (inp_name) {
if (strcmp(inp_name,"NULL") == 0) {
memory->sfree(inp_name);
inp_name = NULL;
}
}
double t_target = 0.0;
if (tmp_name) {
if (strcmp(tmp_name,"NULL") == 0)
tstat_id = -1;
else {
tstat_id = modify->find_fix(tmp_name);
if (tstat_id < 0) error->one(FLERR,"Could not find tstat fix ID");
double *tt = (double*)modify->fix[tstat_id]->extract("t_target",tmp);
if (tt) t_target = *tt;
}
}
proxy = new colvarproxy_lammps(lmp,conf_file,inp_name,out_name,
rng_seed,t_target,typemap);
coords = proxy->get_coords();
forces = proxy->get_forces();
oforce = proxy->get_oforce();
num_coords = coords->size();
}
// send the list of all colvar atom IDs to all nodes.
// also initialize and build hashtable on master.
MPI_Bcast(&num_coords, 1, MPI_INT, 0, world);
memory->create(taglist,num_coords,"colvars:taglist");
memory->create(force_buf,3*num_coords,"colvars:force_buf");
if (me == 0) {
std::vector<int> *tags_list = proxy->get_tags();
std::vector<int> &tl = *tags_list;
inthash_t *hashtable=new inthash_t;
inthash_init(hashtable, num_coords);
idmap = (void *)hashtable;
for (i=0; i < num_coords; ++i) {
taglist[i] = tl[i];
inthash_insert(hashtable, tl[i], i);
}
}
MPI_Bcast(taglist, num_coords, MPI_INT, 0, world);
// determine size of comm buffer
nme=0;
for (i=0; i < num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal))
++nme;
}
MPI_Allreduce(&nme,&nmax,1,MPI_INT,MPI_MAX,world);
memory->create(comm_buf,nmax,"colvars:comm_buf");
const double * const * const x = atom->x;
if (me == 0) {
std::vector<struct commdata> &cd = *coords;
std::vector<struct commdata> &of = *oforce;
// store coordinate data in holding array, clear old forces
for (i=0; i<num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal)) {
of[i].tag = cd[i].tag = tag[k];
of[i].type = cd[i].type = type[k];
cd[i].x = x[k][0];
cd[i].y = x[k][1];
cd[i].z = x[k][2];
of[i].x = of[i].y = of[i].z = 0.0;
}
}
// loop over procs to receive and apply remote data
for (i=1; i < comm->nprocs; ++i) {
int maxbuf = nmax*size_one;
MPI_Irecv(comm_buf, maxbuf, MPI_BYTE, i, 0, world, &request);
MPI_Send(&tmp, 0, MPI_INT, i, 0, world);
MPI_Wait(&request, &status);
MPI_Get_count(&status, MPI_BYTE, &ndata);
ndata /= size_one;
for (int k=0; k<ndata; ++k) {
const int j = inthash_lookup(idmap, comm_buf[k].tag);
if (j != HASH_FAIL) {
of[j].tag = cd[j].tag = comm_buf[k].tag;
of[j].type = cd[j].type = comm_buf[k].type;
cd[j].x = comm_buf[k].x;
cd[j].y = comm_buf[k].y;
cd[j].z = comm_buf[k].z;
of[j].x = of[j].y = of[j].z = 0.0;
}
}
}
} else { // me != 0
// copy coordinate data into communication buffer
nme = 0;
for (i=0; i<num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal)) {
comm_buf[nme].tag = tag[k];
comm_buf[nme].type = type[k];
comm_buf[nme].x = x[k][0];
comm_buf[nme].y = x[k][1];
comm_buf[nme].z = x[k][2];
++nme;
}
}
/* blocking receive to wait until it is our turn to send data. */
MPI_Recv(&tmp, 0, MPI_INT, 0, 0, world, &status);
MPI_Rsend(comm_buf, nme*size_one, MPI_BYTE, 0, 0, world);
}
// clear temporary storage
if (me == 0) delete typemap;
delete type_buf;
}
/* ---------------------------------------------------------------------- */
void FixColvars::setup(int vflag)
{
if (strstr(update->integrate_style,"verlet"))
post_force(vflag);
else
post_force_respa(vflag,0,0);
}
/* ---------------------------------------------------------------------- */
/* Main colvars handler:
* Send coodinates and add colvar forces to atoms. */
void FixColvars::post_force(int vflag)
{
// some housekeeping: update status of the proxy as needed.
if (me == 0) {
if (proxy->want_exit())
error->one(FLERR,"Run halted on request from colvars module.\n");
if (tstat_id < 0) {
proxy->set_temperature(0.0);
} else {
int tmp;
// get thermostat target temperature from corresponding fix,
// if the fix supports extraction.
double *tt = (double *) modify->fix[tstat_id]->extract("t_target",tmp);
if (tt)
proxy->set_temperature(*tt);
else
proxy->set_temperature(0.0);
}
}
const int * const tag = atom->tag;
const double * const * const x = atom->x;
double * const * const f = atom->f;
const int nlocal = atom->nlocal;
/* check and potentially grow local communication buffers. */
int i,nmax_new,nme=0;
for (i=0; i < num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal))
++nme;
}
MPI_Allreduce(&nme,&nmax_new,1,MPI_INT,MPI_MAX,world);
if (nmax_new > nmax) {
nmax = nmax_new;
memory->grow(comm_buf,nmax,"colvars:comm_buf");
}
MPI_Status status;
MPI_Request request;
int tmp, ndata;
if (me == 0) {
std::vector<struct commdata> &cd = *coords;
// store coordinate data
for (i=0; i<num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal)) {
cd[i].x = x[k][0];
cd[i].y = x[k][1];
cd[i].z = x[k][2];
}
}
/* loop over procs to receive remote data */
for (i=1; i < comm->nprocs; ++i) {
int maxbuf = nmax*size_one;
MPI_Irecv(comm_buf, maxbuf, MPI_BYTE, i, 0, world, &request);
MPI_Send(&tmp, 0, MPI_INT, i, 0, world);
MPI_Wait(&request, &status);
MPI_Get_count(&status, MPI_BYTE, &ndata);
ndata /= size_one;
for (int k=0; k<ndata; ++k) {
const int j = inthash_lookup(idmap, comm_buf[k].tag);
if (j != HASH_FAIL) {
cd[j].x = comm_buf[k].x;
cd[j].y = comm_buf[k].y;
cd[j].z = comm_buf[k].z;
}
}
}
} else { // me != 0
/* copy coordinate data into communication buffer */
nme = 0;
for (i=0; i<num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal)) {
comm_buf[nme].tag = tag[k];
comm_buf[nme].x = x[k][0];
comm_buf[nme].y = x[k][1];
comm_buf[nme].z = x[k][2];
++nme;
}
}
/* blocking receive to wait until it is our turn to send data. */
MPI_Recv(&tmp, 0, MPI_INT, 0, 0, world, &status);
MPI_Rsend(comm_buf, nme*size_one, MPI_BYTE, 0, 0, world);
}
////////////////////////////////////////////////////////////////////////
// call our workhorse and retrieve additional information.
if (me == 0) {
energy = proxy->compute();
store_forces = proxy->need_system_forces();
}
////////////////////////////////////////////////////////////////////////
// broadcast store_forces flag and energy data to all processors
MPI_Bcast(&energy, 1, MPI_DOUBLE, 0, world);
MPI_Bcast(&store_forces, 1, MPI_INT, 0, world);
// broadcast and apply biasing forces
if (me == 0) {
std::vector<struct commdata> &fo = *forces;
double *fbuf = force_buf;
for (int j=0; j < num_coords; ++j) {
*fbuf++ = fo[j].x;
*fbuf++ = fo[j].y;
*fbuf++ = fo[j].z;
}
}
MPI_Bcast(force_buf, 3*num_coords, MPI_DOUBLE, 0, world);
for (int i=0; i < num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal)) {
f[k][0] += force_buf[3*i+0];
f[k][1] += force_buf[3*i+1];
f[k][2] += force_buf[3*i+2];
}
}
}
/* ---------------------------------------------------------------------- */
void FixColvars::min_post_force(int vflag)
{
post_force(vflag);
}
/* ---------------------------------------------------------------------- */
void FixColvars::post_force_respa(int vflag, int ilevel, int iloop)
{
/* only process colvar forces on the outmost RESPA level. */
if (ilevel == nlevels_respa-1) post_force(vflag);
return;
}
/* ---------------------------------------------------------------------- */
void FixColvars::end_of_step()
{
if (store_forces) {
const int * const tag = atom->tag;
double * const * const f = atom->f;
const int nlocal = atom->nlocal;
/* check and potentially grow local communication buffers. */
int i,nmax_new,nme=0;
for (i=0; i < num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal))
++nme;
}
MPI_Allreduce(&nme,&nmax_new,1,MPI_INT,MPI_MAX,world);
if (nmax_new > nmax) {
nmax = nmax_new;
memory->grow(comm_buf,nmax,"colvars:comm_buf");
}
MPI_Status status;
MPI_Request request;
int tmp, ndata;
if (me == 0) {
// store old force data
std::vector<struct commdata> &of = *oforce;
for (i=0; i<num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal)) {
const int j = inthash_lookup(idmap, tag[k]);
if (j != HASH_FAIL) {
of[j].x = f[k][0];
of[j].y = f[k][1];
of[j].z = f[k][2];
}
}
}
/* loop over procs to receive remote data */
for (i=1; i < comm->nprocs; ++i) {
int maxbuf = nmax*size_one;
MPI_Irecv(comm_buf, maxbuf, MPI_BYTE, i, 0, world, &request);
MPI_Send(&tmp, 0, MPI_INT, i, 0, world);
MPI_Wait(&request, &status);
MPI_Get_count(&status, MPI_BYTE, &ndata);
ndata /= size_one;
for (int k=0; k<ndata; ++k) {
const int j = inthash_lookup(idmap, comm_buf[k].tag);
if (j != HASH_FAIL) {
of[j].x = comm_buf[k].x;
of[j].y = comm_buf[k].y;
of[j].z = comm_buf[k].z;
}
}
}
} else { // me != 0
/* copy total force data into communication buffer */
nme = 0;
for (i=0; i<num_coords; ++i) {
const int k = atom->map(taglist[i]);
if ((k >= 0) && (k < nlocal)) {
comm_buf[nme].tag = tag[k];
comm_buf[nme].x = f[k][0];
comm_buf[nme].y = f[k][1];
comm_buf[nme].z = f[k][2];
++nme;
}
}
/* blocking receive to wait until it is our turn to send data. */
MPI_Recv(&tmp, 0, MPI_INT, 0, 0, world, &status);
MPI_Rsend(comm_buf, nme*size_one, MPI_BYTE, 0, 0, world);
}
}
}
/* ---------------------------------------------------------------------- */
double FixColvars::compute_scalar()
{
return energy;
}
/* ---------------------------------------------------------------------- */
/* local memory usage. approximately. */
double FixColvars::memory_usage(void)
{
double bytes = (double) (num_coords * (2*sizeof(int)+3*sizeof(double)));
bytes += (double) (nmax*size_one) + sizeof(this);
return bytes;
}

View File

@ -0,0 +1,98 @@
/* -*- c++ -*- ----------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
http://lammps.sandia.gov, Sandia National Laboratories
Steve Plimpton, sjplimp@sandia.gov
Copyright (2003) Sandia Corporation. Under the terms of Contract
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
certain rights in this software. This software is distributed under
the GNU General Public License.
See the README file in the top-level LAMMPS directory.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing author: Axel Kohlmeyer (Temple U)
------------------------------------------------------------------------- */
#ifdef FIX_CLASS
FixStyle(colvars,FixColvars)
#else
#ifndef LMP_FIX_COLVARS_H
#define LMP_FIX_COLVARS_H
#include "fix.h"
#include <vector>
// forward declaration
class colvarproxy_lammps;
struct commdata;
namespace LAMMPS_NS {
class FixColvars : public Fix {
public:
FixColvars(class LAMMPS *, int, char **);
virtual ~FixColvars();
virtual int setmask();
virtual void init();
virtual void setup(int);
virtual void min_post_force(int);
virtual void post_force(int);
virtual void post_force_respa(int, int, int);
virtual void post_run();
virtual void end_of_step();
virtual double compute_scalar();
virtual double memory_usage();
protected:
void deallocate(); // free internal buffers
protected:
class colvarproxy_lammps *proxy; // pointer to the colvars proxy class
char *conf_file; // name of colvars config file
char *inp_name; // name/prefix of colvars restart file
char *out_name; // prefix string for all output files
char *tmp_name; // name of thermostat fix.
int rng_seed; // seed to initialize random number generator
int tstat_id; // id of the thermostat fix
double energy; // biasing energy of the fix
int me; // my MPI rank in this "world".
int num_coords; // total number of atoms controlled by this fix
int *taglist; // list of all atom IDs referenced by colvars.
std::vector<struct commdata> *coords; // coordinates of colvar atoms
std::vector<struct commdata> *forces; // received forces of colvar atoms
std::vector<struct commdata> *oforce; // old total forces of colvar atoms
int nmax; // size of atom communication buffer.
int size_one; // bytes per atom in communication buffer.
struct commdata *comm_buf; // communication buffer
double *force_buf; // communication buffer
void *idmap; // hash for mapping atom indices to consistent order.
int *rev_idmap; // list of the hash keys for reverse mapping.
int nlevels_respa; // flag to determine respa levels.
int store_forces; // flag to determine whether to store total forces
static int instances; // count fix instances, since colvars currently
// only supports one instance at a time
};
}
#endif
#endif
// Local Variables:
// mode: c++
// compile-command: "make -j4 openmpi"
// c-basic-offset: 2
// fill-column: 76
// indent-tabs-mode: nil
// End: