Files
lammps/src/MDI/mdi_engine.cpp
Axel Kohlmeyer bfaa1e9ed0 whitespace
2022-04-08 16:28:57 -04:00

1804 lines
54 KiB
C++

/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/ 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: Taylor Barnes (MolSSI)
MolSSI Driver Interface (MDI) support for LAMMPS
------------------------------------------------------------------------- */
#include "mdi_engine.h"
#include <limits>
#include <cstring>
#include "atom.h"
#include "comm.h"
#include "compute.h"
#include "domain.h"
#include "error.h"
#include "fix_mdi_engine.h"
#include "force.h"
#include "group.h"
#include "input.h"
#include "integrate.h"
#include "irregular.h"
#include "library.h"
#include "library_mdi.h"
#include "memory.h"
#include "min.h"
#include "modify.h"
#include "neighbor.h"
#include "output.h"
#include "thermo.h"
#include "timer.h"
#include "update.h"
#include <mdi.h>
using namespace LAMMPS_NS;
enum{NATIVE,REAL,METAL}; // LAMMPS units which MDI supports
enum{DEFAULT,MD,OPT}; // top-level MDI engine modes
// per-atom data which engine commands access
enum{TYPE,CHARGE,MASS,COORD,VELOCITY,FORCE,ADDFORCE};
/* ----------------------------------------------------------------------
trigger LAMMPS to start acting as an MDI engine
either in standalone mode or plugin mode
MDI_Init() for standalone mode is in main.cpp
MDI_Init() for plugin mode is in library_mdi.cpp::MDI_Plugin_init_lammps()
endlessly loop over receiving commands from driver and responding
when EXIT command is received, mdi engine command exits
---------------------------------------------------------------------- */
MDIEngine::MDIEngine(LAMMPS *lmp, int narg, char **arg) : Pointers(lmp)
{
if (narg) error->all(FLERR,"Illegal mdi engine command");
// check requirements for LAMMPS to work with MDI as an engine
if (atom->tag_enable == 0)
error->all(FLERR,"Cannot use MDI engine without atom IDs");
if (atom->natoms && atom->tag_consecutive() == 0)
error->all(FLERR,"MDI engine requires consecutive atom IDs");
// confirm LAMMPS is being run as an engine
int role;
MDI_Get_role(&role);
if (role != MDI_ENGINE)
error->all(FLERR,"Must invoke LAMMPS as an MDI engine to use mdi engine");
// root = 1 for proc 0, otherwise 0
root = (comm->me == 0) ? 1 : 0;
// MDI setup
mdicmd = new char[MDI_COMMAND_LENGTH];
node_engine = new char[MDI_COMMAND_LENGTH];
strncpy(node_engine,"@DEFAULT",MDI_COMMAND_LENGTH);
node_driver = new char[MDI_COMMAND_LENGTH];
strncpy(node_driver,"\0",MDI_COMMAND_LENGTH);
// create computes for KE. PE, pressure
// pressure compute only calculates virial, no kinetic term
id_ke = utils::strdup(std::string("MDI_ENGINE") + "_ke");
modify->add_compute(fmt::format("{} all ke", id_ke));
id_pe = utils::strdup(std::string("MDI_ENGINE") + "_pe");
modify->add_compute(fmt::format("{} all pe", id_pe));
id_press = utils::strdup(std::string("MDI_ENGINE") + "_press");
modify->add_compute(fmt::format("{} all pressure NULL virial", id_press));
int icompute_ke = modify->find_compute(id_ke);
int icompute_pe = modify->find_compute(id_pe);
int icompute_press = modify->find_compute(id_press);
ke = modify->compute[icompute_ke];
pe = modify->compute[icompute_pe];
press = modify->compute[icompute_press];
//pe = modify->get_compute_by_id("thermo_pe");
//press = modify->get_compute_by_id("thermo_press");
// irregular class used if >COORDS change dramatically
irregular = new Irregular(lmp);
// set unit conversion factors
if (strcmp(update->unit_style, "real") == 0) lmpunits = REAL;
else if (strcmp(update->unit_style, "metal") == 0) lmpunits = METAL;
else lmpunits = NATIVE;
unit_conversions();
// internal state of engine
flag_natoms = 0;
flag_types = flag_charges = flag_coords = flag_velocities = 0;
flag_cell = flag_cell_displ = 0;
sys_types = nullptr;
sys_coords = sys_velocities = nullptr;
sys_charges = nullptr;
buf1 = buf1all = nullptr;
buf3 = buf3all = nullptr;
ibuf1 = ibuf1all = nullptr;
maxatom = 0;
sys_natoms = atom->natoms;
reallocate();
nsteps = 0;
etol = ftol = 1.0e-6;
niterate = -1;
max_eval = std::numeric_limits<int>::max();
nbytes = -1;
actionflag = 0;
// define MDI commands that LAMMPS engine recognizes
mdi_commands();
// register the execute_command function with MDI
// only used when engine runs in plugin mode
MDI_Set_execute_command_func(lammps_execute_mdi_command,this);
// one-time operation to establish a connection with the driver
MDI_Accept_communicator(&mdicomm);
if (mdicomm <= 0) error->all(FLERR,"Unable to connect to MDI driver");
// endless engine loop, responding to driver commands
mode = DEFAULT;
node_match = true;
exit_command = false;
while (1) {
// top-level mdi engine only recognizes three nodes
// DEFAULT, INIT_MD, INIT_OPTG
engine_node("@DEFAULT");
// MDI commands for dynamics or minimization
if (strcmp(mdicmd,"@INIT_MD") == 0) {
mdi_md();
if (exit_command) break;
} else if (strcmp(mdicmd,"@INIT_OPTG") == 0) {
mdi_optg();
if (exit_command) break;
} else if (exit_command) {
break;
} else
error->all(FLERR,
fmt::format("MDI engine exited with invalid command: {}",
mdicmd));
}
// clean up
delete [] mdicmd;
delete [] node_engine;
delete [] node_driver;
modify->delete_compute(id_pe);
modify->delete_compute(id_press);
delete [] id_ke;
delete [] id_pe;
delete [] id_press;
delete irregular;
// delete buffers
deallocate();
}
/* ----------------------------------------------------------------------
engine is now at this MDI node
loop over received commands so long as driver is also at this node
return when not the case or EXIT command received
---------------------------------------------------------------------- */
void MDIEngine::engine_node(const char *node)
{
int ierr;
// do not process commands if engine and driver request are not the same
strncpy(node_engine,node,MDI_COMMAND_LENGTH);
if (strcmp(node_driver,"\0") != 0 && strcmp(node_driver,node_engine) != 0)
node_match = false;
// respond to commands from the driver
while (!exit_command && node_match) {
// read the next command from the driver
// all procs call this, but only proc 0 receives the command
ierr = MDI_Recv_command(mdicmd,mdicomm);
if (ierr) error->all(FLERR,"MDI: Unable to receive command from driver");
// broadcast command to the other MPI tasks
MPI_Bcast(mdicmd,MDI_COMMAND_LENGTH,MPI_CHAR,0,world);
// execute the command
execute_command(mdicmd,mdicomm);
// check if driver request is now different than engine node
if (strcmp(node_driver,"\0") != 0 && strcmp(node_driver,node_engine) != 0)
node_match = false;
}
// node exit was triggered so reset node_match
node_match = true;
}
/* ----------------------------------------------------------------------
process a single driver command
called by engine_node() in loop
also called by MDI itself via lib::lammps_execute_mdi_command()
when LAMMPS is running as a plugin
---------------------------------------------------------------------- */
int MDIEngine::execute_command(const char *command, MDI_Comm mdicomm)
{
int ierr;
// confirm this command is supported at this node
// otherwise is error
int command_exists;
if (root) {
ierr = MDI_Check_command_exists(node_engine,command,MDI_COMM_NULL,
&command_exists);
if (ierr)
error->one(FLERR,
"MDI: Unable to check whether current command is supported");
}
MPI_Bcast(&command_exists,1,MPI_INT,0,world);
if (!command_exists)
error->all(FLERR,"MDI: Received command {} unsupported by engine node {}",
command,node_engine);
// ---------------------------------------
// respond to MDI standard commands
// receives first, sends second, node commands third
// ---------------------------------------
if (strcmp(command,">CELL") == 0) {
receive_cell();
} else if (strcmp(command,">CELL_DISPL") == 0) {
receive_cell_displ();
} else if (strcmp(command,">CHARGES") == 0) {
receive_charges();
} else if (strcmp(command,">COORDS") == 0) {
receive_coords();
} else if (strcmp(command,">FORCES") == 0) {
receive_double3(FORCE);
} else if (strcmp(command,">+FORCES") == 0) {
receive_double3(ADDFORCE);
} else if (strcmp(command,">NATOMS") == 0) {
receive_natoms();
} else if (strcmp(command,">NSTEPS") == 0) {
receive_nsteps();
} else if (strcmp(command,">TOLERANCE") == 0) {
receive_tolerance();
} else if (strcmp(command,">TYPES") == 0) {
receive_types();
} else if (strcmp(command,">VELOCITIES") == 0) {
if (strcmp(node_engine,"@DEFAULT") == 0) receive_velocities();
else receive_double3(VELOCITY);
// -----------------------------------------------
} else if (strcmp(command,"<@") == 0) {
ierr = MDI_Send(node_engine,MDI_NAME_LENGTH,MDI_CHAR,mdicomm);
if (ierr) error->all(FLERR,"MDI: <@ data");
} else if (strcmp(command,"<CELL") == 0) {
send_cell();
} else if (strcmp(command,"<CELL_DISPL") == 0) {
send_cell_displ();
} else if (strcmp(command,"<CHARGES") == 0) {
send_double1(CHARGE);
} else if (strcmp(command,"<COORDS") == 0) {
send_double3(COORD);
} else if (strcmp(command,"<ENERGY") == 0) {
if (!actionflag && strcmp(node_engine,"@DEFAULT") == 0) evaluate();
send_total_energy();
} else if (strcmp(command,"<FORCES") == 0) {
if (!actionflag && strcmp(node_engine,"@DEFAULT") == 0) evaluate();
send_double3(FORCE);
} else if (strcmp(command,"<LABELS") == 0) {
send_labels();
} else if (strcmp(command,"<MASSES") == 0) {
send_double1(MASS);
} else if (strcmp(command,"<NATOMS") == 0) {
send_natoms();
} else if (strcmp(command,"<PE") == 0) {
if (!actionflag && strcmp(node_engine,"@DEFAULT") == 0) evaluate();
send_pe();
} else if (strcmp(command,"<STRESS") == 0) {
if (!actionflag && strcmp(node_engine,"@DEFAULT") == 0) evaluate();
send_stress();
} else if (strcmp(command,"<TYPES") == 0) {
send_int1(TYPE);
} else if (strcmp(command,"<VELOCITIES") == 0) {
send_double3(VELOCITY);
// -----------------------------------------------
// MDI action commands at @DEFAULT node
} else if (strcmp(command,"MD") == 0) {
md();
} else if (strcmp(command,"OPTG") == 0) {
optg();
// -----------------------------------------------
// MDI node commands
} else if (strcmp(command,"@INIT_MD") == 0) {
if (mode != DEFAULT)
error->all(FLERR,"MDI: MDI engine is already performing a simulation");
mode = MD;
strncpy(node_driver,command,MDI_COMMAND_LENGTH);
node_match = false;
} else if (strcmp(command,"@INIT_OPTG") == 0) {
if (mode != DEFAULT)
error->all(FLERR,"MDI: MDI engine is already performing a simulation");
mode = OPT;
strncpy(node_driver,command,MDI_COMMAND_LENGTH);
node_match = false;
} else if (strcmp(command,"@") == 0) {
strncpy(node_driver,"\0",MDI_COMMAND_LENGTH);
node_match = false;
} else if (strcmp(command,"@DEFAULT") == 0) {
mode = DEFAULT;
strncpy(node_driver,command,MDI_COMMAND_LENGTH);
node_match = false;
} else if (strcmp(command,"@COORDS") == 0) {
strncpy(node_driver,command,MDI_COMMAND_LENGTH);
node_match = false;
} else if (strcmp(command,"@FORCES") == 0) {
strncpy(node_driver,command,MDI_COMMAND_LENGTH);
node_match = false;
} else if (strcmp(command,"@ENDSTEP") == 0) {
strncpy(node_driver,command,MDI_COMMAND_LENGTH);
node_match = false;
// exit command
} else if (strcmp(command,"EXIT") == 0) {
exit_command = true;
// -------------------------------------------------------
// custom LAMMPS commands
// -------------------------------------------------------
} else if (strcmp(command,"NBYTES") == 0) {
nbytes_command();
} else if (strcmp(command,"COMMAND") == 0) {
single_command();
} else if (strcmp(command,"COMMANDS") == 0) {
many_commands();
} else if (strcmp(command,"INFILE") == 0) {
infile();
} else if (strcmp(command,"<KE") == 0) {
send_ke();
// -------------------------------------------------------
// unknown command
// -------------------------------------------------------
} else {
error->all(FLERR,"MDI: Unknown command {} received from driver",command);
}
return 0;
}
/* ----------------------------------------------------------------------
define which MDI commands the LAMMPS engine recognizes at each node
both standard MDI commands and custom LAMMPS commands
max length for a command is currently 11 chars
---------------------------------------------------------------------- */
void MDIEngine::mdi_commands()
{
// default node, MDI standard commands
MDI_Register_node("@DEFAULT");
MDI_Register_command("@DEFAULT","<@");
MDI_Register_command("@DEFAULT","<CELL");
MDI_Register_command("@DEFAULT","<CELL_DISPL");
MDI_Register_command("@DEFAULT","<CHARGES");
MDI_Register_command("@DEFAULT","<COORDS");
MDI_Register_command("@DEFAULT","<ENERGY");
MDI_Register_command("@DEFAULT","<FORCES");
MDI_Register_command("@DEFAULT","<LABELS");
MDI_Register_command("@DEFAULT","<MASSES");
MDI_Register_command("@DEFAULT","<NATOMS");
MDI_Register_command("@DEFAULT","<PE");
MDI_Register_command("@DEFAULT","<STRESS");
MDI_Register_command("@DEFAULT","<TYPES");
MDI_Register_command("@DEFAULT","<VELOCITIES");
MDI_Register_command("@DEFAULT",">CELL");
MDI_Register_command("@DEFAULT",">CELL_DISPL");
MDI_Register_command("@DEFAULT",">CHARGES");
MDI_Register_command("@DEFAULT",">COORDS");
MDI_Register_command("@DEFAULT",">NATOMS");
MDI_Register_command("@DEFAULT",">NSTEPS");
MDI_Register_command("@DEFAULT",">TOLERANCE");
MDI_Register_command("@DEFAULT",">TYPES");
MDI_Register_command("@DEFAULT",">VELOCITIES");
MDI_Register_command("@DEFAULT","MD");
MDI_Register_command("@DEFAULT","OPTG");
MDI_Register_command("@DEFAULT","@INIT_MD");
MDI_Register_command("@DEFAULT","@INIT_OPTG");
MDI_Register_command("@DEFAULT","EXIT");
// default node, custom commands added by LAMMPS
MDI_Register_command("@DEFAULT","NBYTES");
MDI_Register_command("@DEFAULT","COMMAND");
MDI_Register_command("@DEFAULT","COMMANDS");
MDI_Register_command("@DEFAULT","INFILE");
MDI_Register_command("@DEFAULT","<KE");
// node for setting up and running a dynamics simulation
MDI_Register_node("@INIT_MD");
MDI_Register_command("@INIT_MD","<@");
MDI_Register_command("@INIT_MD","@");
MDI_Register_command("@INIT_MD","@DEFAULT");
MDI_Register_command("@INIT_MD","@COORDS");
MDI_Register_command("@INIT_MD","@FORCES");
MDI_Register_command("@INIT_MD","@ENDSTEP");
MDI_Register_command("@INIT_MD","EXIT");
// node for setting up and running a minimization
MDI_Register_node("@INIT_OPTG");
MDI_Register_command("@INIT_OPTG","<@");
MDI_Register_command("@INIT_OPTG","@");
MDI_Register_command("@INIT_OPTG","@DEFAULT");
MDI_Register_command("@INIT_OPTG","@COORDS");
MDI_Register_command("@INIT_OPTG","@FORCES");
MDI_Register_command("@INIT_OPTG","EXIT");
// node at POST_INTEGRATE location in timestep
// only used if fix MDI/ENGINE is instantiated
MDI_Register_node("@COORDS");
MDI_Register_command("@COORDS","<@");
MDI_Register_command("@COORDS","<COORDS");
MDI_Register_command("@COORDS","<VELOCITIES");
MDI_Register_command("@COORDS",">COORDS");
MDI_Register_command("@COORDS",">VELOCITIES");
MDI_Register_command("@COORDS","@");
MDI_Register_command("@COORDS","@DEFAULT");
MDI_Register_command("@COORDS","@COORDS");
MDI_Register_command("@COORDS","@FORCES");
MDI_Register_command("@COORDS","@ENDSTEP");
MDI_Register_command("@COORDS","EXIT");
// node at POST_FORCE location in timestep
// only used if fix MDI/ENGINE is instantiated
// two register callbacks allow LAMMPS to interact more easily
// with drivers which don't know LAMMPS control flow
MDI_Register_node("@FORCES");
MDI_Register_callback("@FORCES",">FORCES");
MDI_Register_callback("@FORCES",">+FORCES");
MDI_Register_command("@FORCES","<@");
MDI_Register_command("@FORCES","<COORDS");
MDI_Register_command("@FORCES","<ENERGY");
MDI_Register_command("@FORCES","<FORCES");
MDI_Register_command("@FORCES","<KE");
MDI_Register_command("@FORCES","<PE");
MDI_Register_command("@FORCES","<STRESS");
MDI_Register_command("@FORCES","<VELOCITIES");
MDI_Register_command("@FORCES",">FORCES");
MDI_Register_command("@FORCES",">+FORCES");
MDI_Register_command("@FORCES",">VELOCITIES");
MDI_Register_command("@FORCES","@");
MDI_Register_command("@FORCES","@DEFAULT");
MDI_Register_command("@FORCES","@COORDS");
MDI_Register_command("@FORCES","@FORCES");
MDI_Register_command("@FORCES","@ENDSTEP");
MDI_Register_command("@FORCES","EXIT");
// node at END_OF_STEP location in timestep
// only used if fix MDI/ENGINE is instantiated
MDI_Register_node("@ENDSTEP");
MDI_Register_command("@ENDSTEP","<@");
MDI_Register_command("@ENDSTEP","<ENERGY");
MDI_Register_command("@ENDSTEP","<FORCES");
MDI_Register_command("@ENDSTEP","<KE");
MDI_Register_command("@ENDSTEP","<PE");
MDI_Register_command("@ENDSTEP","<STRESS");
MDI_Register_command("@ENDSTEP","@");
MDI_Register_command("@ENDSTEP","@DEFAULT");
MDI_Register_command("@ENDSTEP","@COORDS");
MDI_Register_command("@ENDSTEP","@FORCES");
MDI_Register_command("@ENDSTEP","@ENDSTEP");
MDI_Register_command("@ENDSTEP","EXIT");
}
/* ----------------------------------------------------------------------
run MD simulation one step at a time, controlled by driver
---------------------------------------------------------------------- */
void MDIEngine::mdi_md()
{
// create or update system if requested prior to @INIT_MD
int flag_create = flag_natoms | flag_types;
if (flag_create) create_system();
else {
if (flag_cell || flag_cell_displ) adjust_box();
if (flag_charges) adjust_charges();
if (flag_coords) adjust_coords();
if (flag_velocities) adjust_velocities();
}
// add an instance of fix MDI/ENGINE
// delete the instance before this method returns
modify->add_fix("MDI_ENGINE_INTERNAL all MDI/ENGINE");
FixMDIEngine *mdi_fix =
(FixMDIEngine *) modify->get_fix_by_id("MDI_ENGINE_INTERNAL");
mdi_fix->mdi_engine = this;
// initialize LAMMPS and setup() the simulation
update->whichflag = 1;
timer->init_timeout();
update->nsteps = 1;
update->beginstep = update->firststep = update->ntimestep;
update->endstep = update->laststep = update->ntimestep + update->nsteps;
lmp->init();
// engine is now at @INIT_MD node
// any @ command from driver will start the simulation
engine_node("@INIT_MD");
if (strcmp(mdicmd,"EXIT") == 0) return;
// run one step at a time forever
// driver triggers exit with @ command other than @COORDS,@FORCES,@ENDSTEP
update->integrate->setup(1);
timer->init();
timer->barrier_start();
while (true) {
update->nsteps += 1;
update->laststep += 1;
update->endstep = update->laststep;
output->next = update->ntimestep + 1;
update->integrate->run(1);
if (strcmp(mdicmd,"@COORDS") != 0 &&
strcmp(mdicmd,"@FORCES") != 0 &&
strcmp(mdicmd,"@ENDSTEP") != 0) break;
}
timer->barrier_stop();
update->integrate->cleanup();
modify->delete_fix("MDI_ENGINE_INTERNAL");
// clear flags
flag_natoms = flag_types = 0;
flag_cell = flag_cell_displ = flag_charges = flag_coords = flag_velocities = 0;
}
/* ----------------------------------------------------------------------
run MD simulation for >NSTEPS
---------------------------------------------------------------------- */
void MDIEngine::md()
{
// create or update system if requested prior to MD command
int flag_create = flag_natoms | flag_types;
if (flag_create) create_system();
else {
if (flag_cell || flag_cell_displ) adjust_box();
if (flag_charges) adjust_charges();
if (flag_coords) adjust_coords();
if (flag_velocities) adjust_velocities();
}
// initialize LAMMPS and setup() the simulation
// run the simulation for nsteps
// clean up
update->whichflag = 1;
timer->init_timeout();
update->nsteps = nsteps;
update->beginstep = update->firststep = update->ntimestep;
update->endstep = update->laststep = update->ntimestep + update->nsteps;
lmp->init();
update->integrate->setup(1);
timer->init();
timer->barrier_start();
update->integrate->run(nsteps);
timer->barrier_stop();
update->integrate->cleanup();
// clear flags
flag_natoms = flag_types = 0;
flag_cell = flag_cell_displ = flag_charges = flag_coords = flag_velocities = 0;
actionflag = 1;
}
/* ----------------------------------------------------------------------
perform minimization one iteration at a time, controlled by driver
---------------------------------------------------------------------- */
void MDIEngine::mdi_optg()
{
// create or update system if requested prior to @INIT_OPTG
int flag_create = flag_natoms | flag_types;
if (flag_create) create_system();
else {
if (flag_cell || flag_cell_displ) adjust_box();
if (flag_charges) adjust_charges();
if (flag_coords) adjust_coords();
if (flag_velocities) adjust_velocities();
}
// add an instance of fix MDI/ENGINE
// delete the instance before this method returns
modify->add_fix("MDI_ENGINE_INTERNAL all MDI/ENGINE");
FixMDIEngine *mdi_fix =
(FixMDIEngine *) modify->get_fix_by_id("MDI_ENGINE_INTERNAL");
mdi_fix->mdi_engine = this;
// initialize LAMMPS and setup() the simulation
update->whichflag = 2;
timer->init_timeout();
update->nsteps = (niterate >= 0) ? niterate : max_eval;
update->beginstep = update->firststep = update->ntimestep;
update->endstep = update->laststep = update->firststep + update->nsteps;
update->etol = 0.0;
update->ftol = 0.0;
update->max_eval = std::numeric_limits<int>::max();
lmp->init();
// engine is now at @INIT_OPTG node
// any @ command from driver will start the minimization
engine_node("@INIT_OPTG");
if (strcmp(mdicmd,"EXIT") == 0) return;
// run one iteration at a time forever
// driver triggers exit with @ command other than @COORDS,@FORCES
// two issues with running in this mode:
// @COORDS and @FORCES are not just triggered per min iteration
// but also for line search eng/force evals
// if driver triggers exit on step that is not multiple of thermo output
// then energy/virial not computed, and <PE, <STRESS will fail
update->minimize->setup();
timer->init();
timer->barrier_start();
while (1) {
update->minimize->run(1);
if (strcmp(mdicmd,"@COORDS") != 0 &&
strcmp(mdicmd,"@FORCES") != 0) break;
}
timer->barrier_stop();
update->minimize->cleanup();
modify->delete_fix("MDI_ENGINE_INTERNAL");
// clear flags
flag_natoms = flag_types = 0;
flag_cell = flag_cell_displ = flag_charges = flag_coords = flag_velocities = 0;
}
/* ----------------------------------------------------------------------
perform minimization to convergence using >TOLERANCE settings
---------------------------------------------------------------------- */
void MDIEngine::optg()
{
// create or update system if requested prior to OPTG command
int flag_create = flag_natoms | flag_types;
if (flag_create) create_system();
else {
if (flag_cell || flag_cell_displ) adjust_box();
if (flag_charges) adjust_charges();
if (flag_coords) adjust_coords();
if (flag_velocities) adjust_velocities();
}
// initialize LAMMPS and setup() the simulation
// run the minimization using 4 >TOLERANCE parameters
// clean up
update->whichflag = 2;
timer->init_timeout();
update->nsteps = (niterate >= 0) ? niterate : max_eval;
update->beginstep = update->firststep = update->ntimestep;
update->endstep = update->laststep = update->firststep + update->nsteps;
update->etol = etol;
update->ftol = ftol;
update->max_eval = max_eval;
lmp->init();
update->minimize->setup();
timer->init();
timer->barrier_start();
update->minimize->run(niterate);
timer->barrier_stop();
update->minimize->cleanup();
// clear flags
flag_natoms = flag_types = 0;
flag_cell = flag_cell_displ = flag_charges = flag_coords = flag_velocities = 0;
actionflag = 1;
}
/* ----------------------------------------------------------------------
evaluate() invoked by <ENERGY, <FORCES, <PE, <STRESS
if flag_natoms or flag_types set, create a new system
if any receive flags set, evaulate eng/forces/stress
---------------------------------------------------------------------- */
void MDIEngine::evaluate()
{
int flag_create = flag_natoms | flag_types;
int flag_other = flag_cell | flag_cell_displ | flag_charges |
flag_coords | flag_velocities;
// create or update system if requested
if (flag_create) create_system();
else if (flag_other) {
if (flag_cell || flag_cell_displ) adjust_box();
if (flag_charges) adjust_charges();
if (flag_coords) adjust_coords();
if (flag_velocities) adjust_velocities();
}
// if new system created or first-time eval:
// init LAMMPS and eval eng/force/virial via setup(1)
// else:
// atom coords may or may not be updated incrementally
// incremental: timstepping an MD simulation
// non-incremental: e.g. processing snapshots from a dump file
// advance system by single step
// insure potential energy and virial are tallied on new step
// check if reneighboing needed
// if no, just invoke setup_minimal(0)
// if yes, do an irregular->migrate_check() and migrate_atoms() if needed
// this can only be done if comm->style is not tiled
// also requires atoms be in box and lamda coords (for triclinic)
// finally invoke setup_minimal(1) to trigger exchange() & reneigh()
// NOTE: what this logic still lacks for comm->style tiled,
// is when to invoke migrate_atoms() if necessary, not easy to detect
if (flag_create || neighbor->ago < 0) {
update->whichflag = 1;
lmp->init();
update->integrate->setup(1);
update->whichflag = 0;
} else if (flag_other) {
update->ntimestep++;
pe->addstep(update->ntimestep);
press->addstep(update->ntimestep);
int nflag = neighbor->decide();
if (nflag == 0) {
comm->forward_comm();
update->integrate->setup_minimal(0);
modify->clearstep_compute();
output->thermo->compute(1);
} else {
if (!comm->style) {
if (domain->triclinic) domain->x2lamda(atom->nlocal);
domain->pbc();
domain->reset_box();
if (irregular->migrate_check()) irregular->migrate_atoms();
if (domain->triclinic) domain->lamda2x(atom->nlocal);
}
update->integrate->setup_minimal(1);
modify->clearstep_compute();
output->thermo->compute(1);
}
modify->addstep_compute(update->ntimestep+1);
}
// clear flags that trigger next eval
flag_natoms = flag_types = 0;
flag_cell = flag_cell_displ = flag_charges = flag_coords = flag_velocities = 0;
}
/* ----------------------------------------------------------------------
create a new system
>CELL, >NATOMS, >TYPES, >COORDS commands are required
>CELL_DISPL, >CHARGES, >VELOCITIES commands are optional
---------------------------------------------------------------------- */
void MDIEngine::create_system()
{
// check requirements
if (flag_cell == 0 || flag_natoms == 0 ||
flag_types == 0 || flag_coords == 0)
error->all(FLERR,
"MDI create_system requires >CELL, >NATOMS, >TYPES, >COORDS "
"MDI commands");
// remove all existing atoms via delete_atoms command
lmp->input->one("delete_atoms group all");
// invoke lib->reset_box()
double boxlo[3],boxhi[3];
double xy,yz,xz;
if (flag_cell_displ) {
boxlo[0] = sys_cell_displ[0];
boxlo[1] = sys_cell_displ[1];
boxlo[2] = sys_cell_displ[2];
} else {
boxlo[0] = boxlo[1] = boxlo[2] = 0.0;
}
boxhi[0] = boxlo[0] + sys_cell[0];
boxhi[1] = boxlo[1] + sys_cell[4];
boxhi[2] = boxlo[2] + sys_cell[8];
xy = sys_cell[3];
yz = sys_cell[7];
xz = sys_cell[6];
lammps_reset_box(lmp,boxlo,boxhi,xy,yz,xz);
// invoke lib->create_atoms()
// optionally set charges if specified by ">CHARGES"
if (flag_velocities)
int natoms = lammps_create_atoms(lmp,sys_natoms,NULL,sys_types,
sys_coords,sys_velocities,NULL,1);
else
int natoms = lammps_create_atoms(lmp,sys_natoms,NULL,sys_types,
sys_coords,NULL,NULL,1);
if (flag_charges) lammps_scatter_atoms(lmp,(char *) "q",1,1,sys_charges);
// new system
update->ntimestep = 0;
}
/* ----------------------------------------------------------------------
adjust simulation box
---------------------------------------------------------------------- */
void MDIEngine::adjust_box()
{
// convert atoms to lamda coords before changing box
domain->x2lamda(atom->nlocal);
// if >CELL command received,
// convert celldata to new boxlo, boxhi, and tilt factors
if (flag_cell) {
domain->boxhi[0] = sys_cell[0] + domain->boxlo[0];
domain->boxhi[1] = sys_cell[4] + domain->boxlo[1];
domain->boxhi[2] = sys_cell[8] + domain->boxlo[2];
domain->xy = sys_cell[3];
domain->xz = sys_cell[6];
domain->yz = sys_cell[7];
}
// if >CELL_DISPL command received,
// convert cell_displ to new boxlo and boxhi
if (flag_cell_displ) {
double old_boxlo[3];
old_boxlo[0] = domain->boxlo[0];
old_boxlo[1] = domain->boxlo[1];
old_boxlo[2] = domain->boxlo[2];
domain->boxlo[0] = sys_cell_displ[0];
domain->boxlo[1] = sys_cell_displ[1];
domain->boxlo[2] = sys_cell_displ[2];
domain->boxhi[0] += domain->boxlo[0] - old_boxlo[0];
domain->boxhi[1] += domain->boxlo[1] - old_boxlo[1];
domain->boxhi[2] += domain->boxlo[2] - old_boxlo[2];
}
// reset all Domain variables that depend on box size/shape
// convert atoms from lamda coords back to new box coords
domain->set_global_box();
domain->set_local_box();
domain->lamda2x(atom->nlocal);
}
/* ----------------------------------------------------------------------
overwrite charges
---------------------------------------------------------------------- */
void MDIEngine::adjust_charges()
{
double *q = atom->q;
tagint *tag = atom->tag;
int nlocal = atom->nlocal;
int ilocal;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
q[i] = sys_charges[ilocal];
}
}
/* ----------------------------------------------------------------------
overwrite coords
---------------------------------------------------------------------- */
void MDIEngine::adjust_coords()
{
double **x = atom->x;
tagint *tag = atom->tag;
int nlocal = atom->nlocal;
int ilocal;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
x[i][0] = sys_coords[3*ilocal+0];
x[i][1] = sys_coords[3*ilocal+1];
x[i][2] = sys_coords[3*ilocal+2];
}
}
/* ----------------------------------------------------------------------
overwrite velocities
---------------------------------------------------------------------- */
void MDIEngine::adjust_velocities()
{
double **v = atom->v;
tagint *tag = atom->tag;
int nlocal = atom->nlocal;
int ilocal;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
v[i][0] = sys_velocities[3*ilocal+0];
v[i][1] = sys_velocities[3*ilocal+1];
v[i][2] = sys_velocities[3*ilocal+2];
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------/
// MDI ">" driver commands that send data
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
/* ----------------------------------------------------------------------
>CELL command
reset simulation box edge vectors
in conjunction with >CELL_DISPL this can change box arbitrarily
can be done to create a new box
can be done incrementally during MD or OPTG
---------------------------------------------------------------------- */
void MDIEngine::receive_cell()
{
actionflag = 0;
flag_cell = 1;
int ierr = MDI_Recv(sys_cell,9,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR, "MDI: >CELL data");
MPI_Bcast(sys_cell,9,MPI_DOUBLE,0,world);
for (int icell = 0; icell < 9; icell++)
sys_cell[icell] *= mdi2lmp_length;
// error check that edge vectors match LAMMPS triclinic requirement
if (sys_cell[1] != 0.0 || sys_cell[2] != 0.0 || sys_cell[5] != 0.0)
error->all(FLERR,"MDI: Received cell edges are not LAMMPS compatible");
}
/* ----------------------------------------------------------------------
>CELL_DISPL command
reset simulation box lower left corner
in conjunction with >CELL this can change box arbitrarily
can be done to create a new box
can be done incrementally during MD or OPTG
---------------------------------------------------------------------- */
void MDIEngine::receive_cell_displ()
{
actionflag = 0;
flag_cell_displ = 1;
int ierr = MDI_Recv(sys_cell_displ,3,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: >CELL_DISPLS data");
MPI_Bcast(sys_cell_displ,3,MPI_DOUBLE,0,world);
for (int icell = 0; icell < 3; icell++)
sys_cell_displ[icell] *= mdi2lmp_length;
}
/* ----------------------------------------------------------------------
>CHARGES command
---------------------------------------------------------------------- */
void MDIEngine::receive_charges()
{
actionflag = 0;
flag_charges = 1;
int ierr = MDI_Recv(sys_charges,sys_natoms,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: >CHARGES data");
MPI_Bcast(sys_charges,sys_natoms,MPI_DOUBLE,0,world);
}
/* ----------------------------------------------------------------------
>COORDS command
---------------------------------------------------------------------- */
void MDIEngine::receive_coords()
{
actionflag = 0;
flag_coords = 1;
int n = 3*sys_natoms;
int ierr = MDI_Recv(sys_coords,n,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: >COORDS data");
MPI_Bcast(sys_coords,n,MPI_DOUBLE,0,world);
for (int i = 0; i < n; i++)
sys_coords[i] * mdi2lmp_length;
}
/* ----------------------------------------------------------------------
>NATOMS command
natoms cannot exceed 32-bit int for use with MDI
---------------------------------------------------------------------- */
void MDIEngine::receive_natoms()
{
actionflag = 0;
flag_natoms = 1;
int ierr = MDI_Recv(&sys_natoms,1,MDI_INT,mdicomm);
if (ierr) error->all(FLERR,"MDI: >NATOMS data");
MPI_Bcast(&sys_natoms,1,MPI_INT,0,world);
if (sys_natoms < 0) error->all(FLERR,"MDI received natoms < 0");
reallocate();
}
/* ----------------------------------------------------------------------
>NSTEPS command
receive nsteps for timestepping
---------------------------------------------------------------------- */
void MDIEngine::receive_nsteps()
{
int ierr = MDI_Recv(&nsteps,1,MDI_INT,mdicomm);
if (ierr) error->all(FLERR,"MDI: >NSTEPS data");
MPI_Bcast(&nsteps,1,MPI_INT,0,world);
if (nsteps < 0) error->all(FLERR,"MDI received nsteps < 0");
}
/* ----------------------------------------------------------------------
>TOLERANCE command
receive 4 minimization tolerance params
---------------------------------------------------------------------- */
void MDIEngine::receive_tolerance()
{
double params[4];
int ierr = MDI_Recv(params,4,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: >TOLERANCE data");
MPI_Bcast(params,4,MPI_INT,0,world);
etol = params[0];
ftol = params[1];
niterate = static_cast<int> (params[2]);
max_eval = static_cast<int> (params[3]);
if (etol < 0.0 || ftol < 0.0 || niterate < 0 || max_eval < 0)
error->all(FLERR,"MDI received invalid toleranace parameters");
}
/* ----------------------------------------------------------------------
>TYPES command
---------------------------------------------------------------------- */
void MDIEngine::receive_types()
{
actionflag = 0;
flag_types = 1;
int ierr = MDI_Recv(sys_types,sys_natoms,MDI_INT,mdicomm);
if (ierr) error->all(FLERR,"MDI: >TYPES data");
MPI_Bcast(sys_types,sys_natoms,MPI_INT,0,world);
}
/* ----------------------------------------------------------------------
>VELOCITIES command
---------------------------------------------------------------------- */
void MDIEngine::receive_velocities()
{
actionflag = 0;
flag_velocities = 1;
int n = 3*sys_natoms;
int ierr = MDI_Recv(sys_velocities,n,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: >VELOCITIES data");
MPI_Bcast(sys_velocities,n,MPI_DOUBLE,0,world);
for (int i = 0; i < n; i++)
sys_coords[i] * mdi2lmp_velocity;
}
/* ----------------------------------------------------------------------
receive vector of 3 doubles for all atoms
atoms are ordered by atomID, 1 to Natoms
used by >FORCES command
---------------------------------------------------------------------- */
void MDIEngine::receive_double3(int which)
{
int n = 3*atom->natoms;
int ierr = MDI_Recv(buf3,n,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: <double3 data");
MPI_Bcast(buf3,n,MPI_DOUBLE,0,world);
// use atomID to index into ordered buf3
tagint *tag = atom->tag;
int nlocal = atom->nlocal;
int ilocal;
if (which == FORCE) {
double **f = atom->f;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
f[i][0] = buf3[3*ilocal+0] * mdi2lmp_force;
f[i][1] = buf3[3*ilocal+1] * mdi2lmp_force;
f[i][2] = buf3[3*ilocal+2] * mdi2lmp_force;
}
} else if (which == ADDFORCE) {
double **f = atom->f;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
f[i][0] += buf3[3*ilocal+0] * mdi2lmp_force;
f[i][1] += buf3[3*ilocal+1] * mdi2lmp_force;
f[i][2] += buf3[3*ilocal+2] * mdi2lmp_force;
}
} else if (which == VELOCITY) {
double **v = atom->v;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
v[i][0] = buf3[3*ilocal+0] * mdi2lmp_velocity;
v[i][1] = buf3[3*ilocal+1] * mdi2lmp_velocity;
v[i][2] = buf3[3*ilocal+2] * mdi2lmp_velocity;
}
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// MDI "<" driver commands that request data
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
/* ----------------------------------------------------------------------
<CELL command
send simulation box edge vectors
---------------------------------------------------------------------- */
void MDIEngine::send_cell()
{
double celldata[9];
celldata[0] = domain->boxhi[0] - domain->boxlo[0];
celldata[1] = 0.0;
celldata[2] = 0.0;
celldata[3] = domain->xy;
celldata[4] = domain->boxhi[1] - domain->boxlo[1];
celldata[5] = 0.0;
celldata[6] = domain->xz;
celldata[7] = domain->yz;
celldata[8] = domain->boxhi[2] - domain->boxlo[2];
for (int icell = 0; icell < 9; icell++)
celldata[icell] *= lmp2mdi_length;
int ierr = MDI_Send(celldata,9,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: <CELL data");
}
/* ----------------------------------------------------------------------
<CELL_DISPL command
send simulation box origin = lower-left corner
---------------------------------------------------------------------- */
void MDIEngine::send_cell_displ()
{
double celldata[3];
celldata[0] = domain->boxlo[0];
celldata[1] = domain->boxlo[1];
celldata[2] = domain->boxlo[2];
for (int icell = 0; icell < 3; icell++)
celldata[icell] *= lmp2mdi_length;
int ierr = MDI_Send(celldata,3,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: <CELL_DISPLS data");
}
/* ----------------------------------------------------------------------
<ENERGY command
send total energy = PE + KE
---------------------------------------------------------------------- */
void MDIEngine::send_total_energy()
{
double potential_energy = pe->compute_scalar();
double kinetic_energy = ke->compute_scalar();
double total_energy = potential_energy + kinetic_energy;
total_energy *= lmp2mdi_energy;
int ierr = MDI_Send(&total_energy,1,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: <ENERGY data");
}
/* ----------------------------------------------------------------------
<LABELS command
convert numeric atom type to string for each atom
atoms are ordered by atomID, 1 to Natoms
---------------------------------------------------------------------- */
void MDIEngine::send_labels()
{
char *labels = new char[atom->natoms * MDI_LABEL_LENGTH];
memset(labels,' ',atom->natoms * MDI_LABEL_LENGTH);
memset(ibuf1,0,atom->natoms*sizeof(int));
// use atomID to index into ordered ibuf1
tagint *tag = atom->tag;
int *type = atom->type;
int nlocal = atom->nlocal;
int ilocal;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
ibuf1[ilocal] = type[i];
}
MPI_Reduce(ibuf1,ibuf1all,atom->natoms,MPI_INT,MPI_SUM,0,world);
if (comm->me == 0) {
for (int iatom = 0; iatom < atom->natoms; iatom++) {
std::string label = std::to_string(ibuf1all[iatom]);
int label_len = std::min(int(label.length()), MDI_LABEL_LENGTH);
strncpy(&labels[iatom * MDI_LABEL_LENGTH], label.c_str(), label_len);
}
}
int ierr = MDI_Send(labels,atom->natoms*MDI_LABEL_LENGTH,MDI_CHAR,mdicomm);
if (ierr) error->all(FLERR,"MDI: <LABELS data");
delete [] labels;
}
/* ----------------------------------------------------------------------
<NATOMS command
natoms cannot exceed 32-bit int for use with MDI
---------------------------------------------------------------------- */
void MDIEngine::send_natoms()
{
int natoms = static_cast<int> (atom->natoms);
int ierr = MDI_Send(&natoms,1,MDI_INT,mdicomm);
if (ierr != 0) error->all(FLERR,"MDI: <NATOMS data");
}
/* ----------------------------------------------------------------------
<PE command
send potential energy
---------------------------------------------------------------------- */
void MDIEngine::send_pe()
{
double potential_energy = pe->compute_scalar();
potential_energy *= lmp2mdi_energy;
int ierr = MDI_Send(&potential_energy,1,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: <PE data");
}
/* ----------------------------------------------------------------------
<STRESS command
send 6-component stress tensor (no kinetic energy term)
---------------------------------------------------------------------- */
void MDIEngine::send_stress()
{
double vtensor[6];
press->compute_vector();
for (int i = 0; i < 6; i++)
vtensor[i] = press->vector[i] * lmp2mdi_pressure;
int ierr = MDI_Send(vtensor,6,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: <STRESS data");
}
/* ----------------------------------------------------------------------
send vector of 1 double for all atoms
atoms are ordered by atomID, 1 to Natoms
used by <CHARGE, <MASSES commands
---------------------------------------------------------------------- */
void MDIEngine::send_double1(int which)
{
memset(buf1,0,atom->natoms*sizeof(double));
// use atomID to index into ordered buf1
tagint *tag = atom->tag;
int nlocal = atom->nlocal;
int ilocal;
if (which == CHARGE) {
double *q = atom->q;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
buf1[ilocal] = q[i];
}
} else if (which == MASS) {
double *mass = atom->mass;
double *rmass = atom->rmass;
if (rmass) {
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
buf1[ilocal] = rmass[i];
}
} else {
int *type = atom->type;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
buf1[ilocal] = mass[type[i]];
}
}
}
MPI_Reduce(buf1,buf1all,atom->natoms,MPI_DOUBLE,MPI_SUM,0,world);
int ierr = MDI_Send(buf1all,atom->natoms,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: <double1 data");
}
/* ----------------------------------------------------------------------
send vector of 1 int for all atoms
atoms are ordered by atomID, 1 to Natoms
use by <TYPES command
---------------------------------------------------------------------- */
void MDIEngine::send_int1(int which)
{
memset(ibuf1,0,atom->natoms*sizeof(int));
// use atomID to index into ordered ibuf1
tagint *tag = atom->tag;
int nlocal = atom->nlocal;
int ilocal;
if (which == TYPE) {
int *type = atom->type;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
ibuf1[ilocal] = type[i];
}
}
MPI_Reduce(ibuf1,ibuf1all,atom->natoms,MPI_INT,MPI_SUM,0,world);
int ierr = MDI_Send(ibuf1all,atom->natoms,MDI_INT,mdicomm);
if (ierr) error->all(FLERR,"MDI: <int1 data");
}
/* ----------------------------------------------------------------------
<COORDS, <FORCES, <VELOCITIES commands
send vector of 3 doubles for all atoms
atoms are ordered by atomID, 1 to Natoms
---------------------------------------------------------------------- */
void MDIEngine::send_double3(int which)
{
memset(buf3,0,3*atom->natoms*sizeof(double));
// use atomID to index into ordered buf3
tagint *tag = atom->tag;
int nlocal = atom->nlocal;
int ilocal;
if (which == COORD) {
double **x = atom->x;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
buf3[3*ilocal+0] = x[i][0] * lmp2mdi_length;
buf3[3*ilocal+1] = x[i][1] * lmp2mdi_length;
buf3[3*ilocal+2] = x[i][2] * lmp2mdi_length;
}
} else if (which == FORCE) {
double **f = atom->f;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
buf3[3*ilocal+0] = f[i][0] * lmp2mdi_force;
buf3[3*ilocal+1] = f[i][1] * lmp2mdi_force;
buf3[3*ilocal+2] = f[i][2] * lmp2mdi_force;
}
} else if (which == VELOCITY) {
double **v = atom->v;
for (int i = 0; i < nlocal; i++) {
ilocal = static_cast<int> (tag[i]) - 1;
buf3[3*ilocal+0] = v[i][0] * lmp2mdi_velocity;
buf3[3*ilocal+1] = v[i][1] * lmp2mdi_velocity;
buf3[3*ilocal+2] = v[i][2] * lmp2mdi_velocity;
}
}
MPI_Reduce(buf3,buf3all,3*atom->natoms,MPI_DOUBLE,MPI_SUM,0,world);
int ierr = MDI_Send(buf3all,3*atom->natoms,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: <double3 data");
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// responses to custom LAMMPS MDI commands
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
/* ----------------------------------------------------------------------
NBYTES command
store received value in nbytes
for use by a subsequent command, e.g. ones that send strings
---------------------------------------------------------------------- */
void MDIEngine::nbytes_command()
{
int ierr = MDI_Recv(&nbytes,1,MDI_INT,mdicomm);
if (ierr) error->all(FLERR,"MDI: NBYTES data");
MPI_Bcast(&nbytes,1,MPI_INT,0,world);
}
/* ----------------------------------------------------------------------
COMMAND command
store received value as string of length nbytes
invoke as a LAMMPS command
---------------------------------------------------------------------- */
void MDIEngine::single_command()
{
if (nbytes < 0) error->all(FLERR,"MDI: COMMAND nbytes has not been set");
char *cmd = new char[nbytes+1];
int ierr = MDI_Recv(cmd,nbytes+1,MDI_CHAR,mdicomm);
if (ierr) error->all(FLERR,"MDI: COMMAND data");
MPI_Bcast(cmd,nbytes+1,MPI_CHAR,0,world);
cmd[nbytes] = '\0';
lammps_command(lmp,cmd);
delete [] cmd;
}
/* ----------------------------------------------------------------------
COMMANDS command
store received value as multi-line string of length nbytes
invoke as multiple LAMMPS commands
---------------------------------------------------------------------- */
void MDIEngine::many_commands()
{
if (nbytes < 0) error->all(FLERR,"MDI: COMMANDS nbytes has not been set");
char *cmds = new char[nbytes+1];
int ierr = MDI_Recv(cmds, nbytes+1, MDI_CHAR, mdicomm);
if (ierr) error->all(FLERR,"MDI: COMMANDS data");
MPI_Bcast(cmds,nbytes+1,MPI_CHAR,0,world);
cmds[nbytes] = '\0';
lammps_commands_string(lmp,cmds);
delete [] cmds;
}
/* ----------------------------------------------------------------------
INFILE command
store received value as infile of length length_param
invoke as a LAMMPS input script
---------------------------------------------------------------------- */
void MDIEngine::infile()
{
if (nbytes < 0) error->all(FLERR,"MDI: INFILE nbytes has not been set");
char *infile = new char[nbytes+1];
int ierr = MDI_Recv(infile,nbytes+1,MDI_CHAR,mdicomm);
if (ierr) error->all(FLERR,"MDI: INFILE data");
MPI_Bcast(infile,nbytes+1,MPI_CHAR,0,world);
infile[nbytes] = '\0';
lammps_file(lmp,infile);
delete [] infile;
}
/* ----------------------------------------------------------------------
<KE command
send kinetic energy
---------------------------------------------------------------------- */
void MDIEngine::send_ke()
{
double kinetic_energy = ke->compute_scalar();
kinetic_energy *= lmp2mdi_energy;
int ierr = MDI_Send(&kinetic_energy,1,MDI_DOUBLE,mdicomm);
if (ierr) error->all(FLERR,"MDI: <KE data");
}
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// additional methods
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
/* ----------------------------------------------------------------------
reallocate storage for all atoms if necessary
------------------------------------------------------------------------- */
void MDIEngine::reallocate()
{
if (sys_natoms <= maxatom) return;
if (3*sys_natoms > MAXSMALLINT)
error->all(FLERR,"Natoms too large to use with mdi engine");
maxatom = sys_natoms;
deallocate();
allocate();
}
void MDIEngine::deallocate()
{
memory->destroy(sys_types);
memory->destroy(sys_coords);
memory->destroy(sys_velocities);
memory->destroy(sys_charges);
memory->destroy(ibuf1);
memory->destroy(buf1);
memory->destroy(buf3);
memory->destroy(ibuf1all);
memory->destroy(buf1all);
memory->destroy(buf3all);
}
void MDIEngine::allocate()
{
memory->create(sys_types,maxatom,"mdi:sys_types");
memory->create(sys_coords,3*maxatom,"mdi:sys_coords");
memory->create(sys_velocities,3*maxatom,"mdi:sys_velocities");
memory->create(sys_charges,maxatom,"mdi:sys_charges");
memory->create(ibuf1,maxatom,"mdi:ibuf1");
memory->create(buf1,maxatom,"mdi:buf1");
memory->create(buf3,3*maxatom,"mdi:buf3");
memory->create(ibuf1all,maxatom,"mdi:ibuf1all");
memory->create(buf1all,maxatom,"mdi:buf1all");
memory->create(buf3all,3*maxatom,"mdi:buf3all");
}
/* ----------------------------------------------------------------------
MDI to/from LAMMPS conversion factors
------------------------------------------------------------------------- */
void MDIEngine::unit_conversions()
{
double angstrom_to_bohr,kelvin_to_hartree,ev_to_hartree,second_to_aut;
MDI_Conversion_factor("angstrom","bohr",&angstrom_to_bohr);
MDI_Conversion_factor("kelvin_energy","hartree",&kelvin_to_hartree);
MDI_Conversion_factor("electron_volt","hartree",&ev_to_hartree);
MDI_Conversion_Factor("second","atomic_unit_of_time",&second_to_aut);
// length units
mdi2lmp_length = 1.0;
lmp2mdi_length = 1.0;
if (lmpunits == REAL || lmpunits == METAL) {
lmp2mdi_length = angstrom_to_bohr;
mdi2lmp_length = 1.0 / angstrom_to_bohr;
}
// energy units
mdi2lmp_energy = 1.0;
lmp2mdi_energy = 1.0;
if (lmpunits == REAL) {
lmp2mdi_energy = kelvin_to_hartree / force->boltz;
mdi2lmp_energy = force->boltz / kelvin_to_hartree;
} else if (lmpunits == METAL) {
lmp2mdi_energy = ev_to_hartree;
mdi2lmp_energy = 1.0 / ev_to_hartree;
}
// force units = energy/length
mdi2lmp_force = 1.0;
lmp2mdi_force = 1.0;
if (lmpunits == REAL) {
lmp2mdi_force = (kelvin_to_hartree / force->boltz) / angstrom_to_bohr;
mdi2lmp_force = 1.0 / lmp2mdi_force;
} else if (lmpunits == METAL) {
lmp2mdi_force = ev_to_hartree / angstrom_to_bohr;
mdi2lmp_force = angstrom_to_bohr / ev_to_hartree;
}
// pressure or stress units = force/area = energy/volume
mdi2lmp_pressure = 1.0;
lmp2mdi_pressure = 1.0;
if (lmpunits == REAL) {
lmp2mdi_pressure = (kelvin_to_hartree / force->boltz) /
(angstrom_to_bohr * angstrom_to_bohr * angstrom_to_bohr) / force->nktv2p;
mdi2lmp_pressure = 1.0 / lmp2mdi_pressure;
} else if (lmpunits == METAL) {
lmp2mdi_pressure = ev_to_hartree /
(angstrom_to_bohr * angstrom_to_bohr * angstrom_to_bohr) / force->nktv2p;
mdi2lmp_pressure = 1.0 / lmp2mdi_pressure;
}
// velocity units = distance/time
mdi2lmp_velocity = 1.0;
lmp2mdi_velocity = 1.0;
if (lmpunits == REAL) {
lmp2mdi_velocity = angstrom_to_bohr / (1.0e-15 * second_to_aut);
mdi2lmp_velocity = 1.0 / lmp2mdi_velocity;
} else if (lmpunits == METAL) {
lmp2mdi_velocity = angstrom_to_bohr / (1.0e-12 * second_to_aut);
mdi2lmp_velocity = 1.0 / lmp2mdi_velocity;
}
}