334 lines
9.9 KiB
C++
334 lines
9.9 KiB
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.
|
|
------------------------------------------------------------------------- */
|
|
|
|
#include "mpi.h"
|
|
#include <cstring>
|
|
#include "server_md.h"
|
|
#include "atom.h"
|
|
#include "atom_vec.h"
|
|
#include "update.h"
|
|
#include "integrate.h"
|
|
#include "kspace.h"
|
|
#include "force.h"
|
|
#include "pair.h"
|
|
#include "neighbor.h"
|
|
#include "comm.h"
|
|
#include "domain.h"
|
|
#include "error.h"
|
|
|
|
// CSlib interface
|
|
|
|
#include "cslib.h"
|
|
|
|
using namespace LAMMPS_NS;
|
|
using namespace CSLIB_NS;
|
|
|
|
enum{SETUP=1,STEP};
|
|
enum{UNITS=1,DIM,NATOMS,NTYPES,BOXLO,BOXHI,BOXTILT,TYPES,COORDS,CHARGE};
|
|
enum{FORCES=1,ENERGY,VIRIAL};
|
|
|
|
// NOTE: features that could be added to this interface
|
|
// allow client to set periodicity vs shrink-wrap
|
|
// currently just assume server is same as client
|
|
// test that triclinic boxes actually work
|
|
// send new box size/shape every step, for NPT client
|
|
// unit check between client/server with unit conversion if needed
|
|
// option for client to send other per-atom quantities, e.g. rmass
|
|
// more precise request of energy/virial (global or peratom) by client
|
|
// maybe Verlet should have a single(eflag,vflag) method to more easily comply
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
ServerMD::ServerMD(LAMMPS *lmp) : Pointers(lmp)
|
|
{
|
|
if (domain->box_exist == 0)
|
|
error->all(FLERR,"Server command before simulation box is defined");
|
|
|
|
if (!atom->map_style) error->all(FLERR,"Server md mode requires atom map");
|
|
if (atom->tag_enable == 0) error->all(FLERR,"Server md mode requires atom IDs");
|
|
if (sizeof(tagint) != 4) error->all(FLERR,"Server md requires 4-byte atom IDs");
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void ServerMD::loop()
|
|
{
|
|
int i,j,m;
|
|
|
|
// cs = instance of CSlib
|
|
|
|
CSlib *cs = (CSlib *) lmp->cslib;
|
|
|
|
// counters
|
|
|
|
int forcecalls = 0;
|
|
int neighcalls = 0;
|
|
|
|
// loop on messages
|
|
// receive a message, process it, send return message
|
|
|
|
int msgID,nfield;
|
|
int *fieldID,*fieldtype,*fieldlen;
|
|
|
|
while (1) {
|
|
msgID = cs->recv(nfield,fieldID,fieldtype,fieldlen);
|
|
if (msgID < 0) break;
|
|
|
|
// SETUP call at beginning of each run
|
|
// required fields: NATOMS, NTYPES, BOXLO, BOXHI, TYPES, COORDS
|
|
// optional fields: others in enum above
|
|
|
|
if (msgID == SETUP) {
|
|
|
|
int natoms = -1;
|
|
int ntypes = -1;
|
|
double *boxlo = NULL;
|
|
double *boxhi = NULL;
|
|
double *boxtilt = NULL;
|
|
int *types = NULL;
|
|
double *coords = NULL;
|
|
double *charge = NULL;
|
|
|
|
for (int ifield = 0; ifield < nfield; ifield++) {
|
|
if (fieldID[ifield] == UNITS) {
|
|
char *units = cs->unpack_string(UNITS);
|
|
if (strcmp(units,update->unit_style) != 0)
|
|
error->all(FLERR,"Server md units mis-match with client");
|
|
} else if (fieldID[ifield] == DIM) {
|
|
int dim = cs->unpack_int(DIM);
|
|
if (dim != domain->dimension)
|
|
error->all(FLERR,"Server md dimension mis-match with client");
|
|
} else if (fieldID[ifield] == NATOMS) {
|
|
natoms = cs->unpack_int(NATOMS);
|
|
if (3*natoms > INT_MAX)
|
|
error->all(FLERR,"Server md max atoms is 1/3 of 2^31");
|
|
} else if (fieldID[ifield] == NTYPES) {
|
|
ntypes = cs->unpack_int(NTYPES);
|
|
if (ntypes != atom->ntypes)
|
|
error->all(FLERR,"Server md ntypes mis-match with client");
|
|
} else if (fieldID[ifield] == BOXLO) {
|
|
boxlo = (double *) cs->unpack(BOXLO);
|
|
} else if (fieldID[ifield] == BOXHI) {
|
|
boxhi = (double *) cs->unpack(BOXHI);
|
|
} else if (fieldID[ifield] == BOXTILT) {
|
|
boxtilt = (double *) cs->unpack(BOXTILT);
|
|
} else if (fieldID[ifield] == TYPES) {
|
|
types = (int *) cs->unpack(TYPES);
|
|
} else if (fieldID[ifield] == COORDS) {
|
|
coords = (double *) cs->unpack(COORDS);
|
|
} else if (fieldID[ifield] == CHARGE) {
|
|
charge = (double *) cs->unpack(CHARGE);
|
|
} else error->all(FLERR,"Server md setup field unknown");
|
|
}
|
|
|
|
if (natoms < 0 || ntypes < 0 || !boxlo || !boxhi || !types || !coords)
|
|
error->all(FLERR,"Required server md setup field not received");
|
|
|
|
if (charge && atom->q_flag == 0)
|
|
error->all(FLERR,"Server md does not define atom attribute q");
|
|
|
|
// reset box, global and local
|
|
// reset proc decomposition
|
|
|
|
box_change(boxlo,boxhi,boxtilt);
|
|
|
|
domain->set_initial_box();
|
|
domain->set_global_box();
|
|
comm->set_proc_grid();
|
|
domain->set_local_box();
|
|
|
|
// clear all atoms
|
|
|
|
atom->nlocal = 0;
|
|
atom->nghost = 0;
|
|
|
|
// add atoms that are in my sub-box
|
|
|
|
int nlocal = 0;
|
|
for (int i = 0; i < natoms; i++) {
|
|
if (!domain->ownatom(i+1,&coords[3*i],NULL,0)) continue;
|
|
atom->avec->create_atom(types[i],&coords[3*i]);
|
|
atom->tag[nlocal] = i+1;
|
|
if (charge) atom->q[nlocal] = charge[i];
|
|
nlocal++;
|
|
}
|
|
|
|
int ntotal;
|
|
MPI_Allreduce(&atom->nlocal,&ntotal,1,MPI_INT,MPI_SUM,world);
|
|
if (ntotal != natoms)
|
|
error->all(FLERR,"Server md atom cound does not match client");
|
|
|
|
atom->map_init();
|
|
atom->map_set();
|
|
atom->natoms = natoms;
|
|
|
|
// perform system setup() for dynamics
|
|
// also OK for minimization, since client runs minimizer
|
|
// return forces, energy, virial to client
|
|
|
|
update->whichflag = 1;
|
|
lmp->init();
|
|
update->integrate->setup_minimal(1);
|
|
neighcalls++;
|
|
forcecalls++;
|
|
|
|
send_fev(msgID);
|
|
|
|
// STEP call at each timestep of run or minimization
|
|
// required fields: COORDS
|
|
// optional fields: BOXLO, BOXHI, BOXTILT
|
|
|
|
} else if (msgID == STEP) {
|
|
|
|
double *boxlo = NULL;
|
|
double *boxhi = NULL;
|
|
double *boxtilt = NULL;
|
|
double *coords = NULL;
|
|
|
|
for (int ifield = 0; ifield < nfield; ifield++) {
|
|
if (fieldID[ifield] == BOXLO) {
|
|
boxlo = (double *) cs->unpack(BOXLO);
|
|
} else if (fieldID[ifield] == BOXHI) {
|
|
boxhi = (double *) cs->unpack(BOXHI);
|
|
} else if (fieldID[ifield] == BOXTILT) {
|
|
boxtilt = (double *) cs->unpack(BOXTILT);
|
|
} else if (fieldID[ifield] == COORDS) {
|
|
coords = (double *) cs->unpack(COORDS);
|
|
} else error->all(FLERR,"Server md step field unknown");
|
|
}
|
|
|
|
if (!coords)
|
|
error->all(FLERR,"Required server md step field not received");
|
|
|
|
// change box size/shape, only if both box lo/hi received
|
|
|
|
if (boxlo && boxhi) box_change(boxlo,boxhi,boxtilt);
|
|
|
|
// assign received coords to owned atoms
|
|
// closest_image() insures xyz matches current server PBC
|
|
|
|
double **x = atom->x;
|
|
int nlocal = atom->nlocal;
|
|
int nall = atom->natoms;
|
|
|
|
j = 0;
|
|
for (tagint id = 1; id <= nall; id++) {
|
|
m = atom->map(id);
|
|
if (m < 0 || m >= nlocal) j += 3;
|
|
else {
|
|
domain->closest_image(x[m],&coords[j],x[m]);
|
|
j += 3;
|
|
}
|
|
}
|
|
|
|
// if no need to reneighbor, just ghost comm
|
|
// else setup_minimal(1) which includes reneigh
|
|
// setup_minimal computes forces for 0 or 1
|
|
|
|
int nflag = neighbor->decide();
|
|
if (nflag == 0) {
|
|
comm->forward_comm();
|
|
update->integrate->setup_minimal(0);
|
|
} else {
|
|
update->integrate->setup_minimal(1);
|
|
neighcalls++;
|
|
}
|
|
|
|
forcecalls++;
|
|
|
|
send_fev(msgID);
|
|
|
|
} else error->all(FLERR,"Server MD received unrecognized message");
|
|
}
|
|
|
|
// final reply to client
|
|
|
|
cs->send(0,0);
|
|
|
|
// stats
|
|
|
|
if (comm->me == 0) {
|
|
if (screen) {
|
|
fprintf(screen,"Server MD calls = %d\n",forcecalls);
|
|
fprintf(screen,"Server MD reneighborings = %d\n",neighcalls);
|
|
}
|
|
if (logfile) {
|
|
fprintf(logfile,"Server MD calls = %d\n",forcecalls);
|
|
fprintf(logfile,"Server MD reneighborings %d\n",neighcalls);
|
|
}
|
|
}
|
|
|
|
// clean up
|
|
|
|
delete cs;
|
|
lmp->cslib = NULL;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
box change due to received message
|
|
------------------------------------------------------------------------- */
|
|
|
|
void ServerMD::box_change(double *boxlo, double *boxhi, double *boxtilt)
|
|
{
|
|
domain->boxlo[0] = boxlo[0];
|
|
domain->boxhi[0] = boxhi[0];
|
|
domain->boxlo[1] = boxlo[1];
|
|
domain->boxhi[1] = boxhi[1];
|
|
if (domain->dimension == 3) {
|
|
domain->boxlo[2] = boxlo[2];
|
|
domain->boxhi[2] = boxhi[2];
|
|
}
|
|
|
|
if (boxtilt) {
|
|
if (!domain->triclinic)
|
|
error->all(FLERR,"Server md not setup for triclinic box");
|
|
domain->xy = boxtilt[0];
|
|
if (domain->dimension == 3) {
|
|
domain->xz = boxtilt[1];
|
|
domain->yz = boxtilt[2];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
send return message with forces, energy, pressure tensor
|
|
pressure tensor should be just pair style virial
|
|
------------------------------------------------------------------------- */
|
|
|
|
void ServerMD::send_fev(int msgID)
|
|
{
|
|
CSlib *cs = (CSlib *) lmp->cslib;
|
|
|
|
cs->send(msgID,3);
|
|
|
|
double *forces = NULL;
|
|
if (atom->nlocal) forces = &atom->f[0][0];
|
|
cs->pack_parallel(FORCES,4,atom->nlocal,atom->tag,3,forces);
|
|
|
|
double eng = force->pair->eng_vdwl + force->pair->eng_coul;
|
|
double engall;
|
|
MPI_Allreduce(&eng,&engall,1,MPI_DOUBLE,MPI_SUM,world);
|
|
cs->pack_double(ENERGY,engall);
|
|
|
|
double v[6],vall[6];
|
|
for (int i = 0; i < 6; i++)
|
|
v[i] = force->pair->virial[i];
|
|
if (force->kspace)
|
|
for (int i = 0; i < 6; i++)
|
|
v[i] += force->kspace->virial[i];
|
|
|
|
MPI_Allreduce(&v,&vall,6,MPI_DOUBLE,MPI_SUM,world);
|
|
cs->pack(VIRIAL,4,6,vall);
|
|
}
|
|
|