/* ---------------------------------------------------------------------- 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 #include #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 "library.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 using namespace LAMMPS_NS; enum{NATIVE,REAL,METAL}; // LAMMPS units which MDI supports enum{DEFAULT,MD,OPT,SYS,EVAL}; // top-level MDI engine mode enum{EVALPOINT,EVALMD,EVALOPT}; // EVAL mode // per-atom data which engine commands access enum{TYPE,CHARGE,MASS,COORD,VELOCITY,FORCE}; /* ---------------------------------------------------------------------- mdi command: engine NOTE: may later have other MDI command variants? ---------------------------------------------------------------------- */ void MDIEngine::command(int narg, char **arg) { if (narg < 1) error->all(FLERR,"Illegal mdi command"); if (strcmp(arg[0],"engine") == 0) mdi_engine(narg-1,&arg[1]); else error->all(FLERR,"Illegal mdi command"); } /* ---------------------------------------------------------------------- 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 ---------------------------------------------------------------------- */ void MDIEngine::mdi_engine(int narg, char **arg) { // process args enable_fix = 0; int iarg = 0; while (iarg < narg) { if (strcmp(arg[iarg],"nodes") == 0) { if (iarg+2 > narg) error->all(FLERR,"Illegal mdi engine command"); if (strcmp(arg[iarg+1],"yes") == 0) enable_fix = 1; else if (strcmp(arg[iarg+1],"no") == 0) enable_fix = 0; iarg += 2; } else 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]; // 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(); nbytes = -1; // define MDI commands that LAMMPS engine recognizes mdi_commands(); // 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"); printf("ENG post accept MDI comm\n"); // 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, INIT_SYS, EVAL 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_ke); modify->delete_compute(id_pe); modify->delete_compute(id_press); delete [] id_ke; delete [] id_pe; delete [] id_press; // 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; printf("ENG ENODE %s\n",node); // 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 printf("ENG PRE-RECV %d\n",mdicomm); ierr = MDI_Recv_command(mdicmd,mdicomm); if (ierr) error->all(FLERR,"MDI: Unable to receive command from driver"); printf("ENG POST-RECV %s\n",mdicmd); // 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 a command unsupported by engine node"); // --------------------------------------- // 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,">NATOMS") == 0) { receive_natoms(); } else if (strcmp(command,">TYPES") == 0) { receive_types(); } else if (strcmp(command,">VELOCITIES") == 0) { receive_velocities(); // ----------------------------------------------- } 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,"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; // if minimization in progress, force it to quit if (mode == OPT) { update->etol = std::numeric_limits::max(); update->ftol = std::numeric_limits::max(); update->max_eval = 0; } } 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; // if minimization in progress, force it to quit if (mode == OPT) { update->etol = std::numeric_limits::max(); update->ftol = std::numeric_limits::max(); update->max_eval = 0; } // ------------------------------------------------------- // 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,">NITERATE") == 0) { receive_niterate(); } else if (strcmp(command,">TOLERANCE") == 0) { receive_tolerance(); } else if (strcmp(command,"all(FLERR,"MDI: Unknown command received from driver"); } 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", ">NATOMS"); MDI_Register_command("@DEFAULT", ">TYPES"); MDI_Register_command("@DEFAULT", ">VELOCITIES"); 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", ">TOLERANCE"); MDI_Register_command("@DEFAULT", "CELL"); MDI_Register_command("@INIT_MD", ">CELL_DISPL"); MDI_Register_command("@INIT_MD", ">CHARGES"); MDI_Register_command("@INIT_MD", ">COORDS"); MDI_Register_command("@INIT_MD", ">NATOMS"); MDI_Register_command("@INIT_MD", ">NITERATE"); MDI_Register_command("@INIT_MD", ">TYPES"); MDI_Register_command("@INIT_MD", ">VELOCITIES"); 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", "CELL"); MDI_Register_command("@INIT_OPTG", ">CELL_DISPL"); MDI_Register_command("@INIT_OPTG", ">CHARGES"); MDI_Register_command("@INIT_OPTG", ">COORDS"); MDI_Register_command("@INIT_OPTG", ">NATOMS"); MDI_Register_command("@INIT_OPTG", ">TOLERANCE"); MDI_Register_command("@INIT_OPTG", ">TYPES"); MDI_Register_command("@INIT_OPTG", ">VELOCITIES"); 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", "@ENDSTEP"); MDI_Register_command("@INIT_OPTG", "EXIT"); // node at POST_INTEGRATE location in timestep // only if fix MDI/ENGINE is instantiated if (enable_fix) { MDI_Register_node("@COORDS"); MDI_Register_command("@COORDS", "<@"); MDI_Register_command("@COORDS", "COORDS"); 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 if fix MDI/ENGINE is instantiated if (enable_fix) { MDI_Register_node("@FORCES"); MDI_Register_command("@FORCES", "<@"); MDI_Register_command("@FORCES", "FORCES"); MDI_Register_callback("@FORCES", ">+FORCES"); MDI_Register_command("@FORCES", "NITERATE command sets # of timesteps ---------------------------------------------------------------------- */ void MDIEngine::mdi_md() { // engine is now at @INIT_MD node // receive >NITERATE or other commands driver wishes to send // @DEFAULT command from driver will trigger the simulation niterate = 0; engine_node("@INIT_MD"); if (strcmp(mdicmd,"EXIT") == 0) return; // create or update system if requested 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(); } // perform the MD simulation update->whichflag = 1; timer->init_timeout(); update->nsteps = niterate; 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(niterate); 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; } /* ---------------------------------------------------------------------- run MD simulation under control of driver one step at a time use of fix MDI/ENGINE allows MDI comm within timesteps ---------------------------------------------------------------------- */ void MDIEngine::mdi_md_old() { // add an instance of fix MDI/ENGINE // delete the instance before this method returns if (!enable_fix) error->all(FLERR,"MDI engine command did not enable @INIT_MD support"); modify->add_fix("MDI_ENGINE_INTERNAL all MDI/ENGINE"); mdi_fix = (FixMDIEngine *) modify->get_fix_by_id("MDI_ENGINE_INTERNAL"); mdi_fix->mdi_engine = this; // initialize a new MD simulation update->whichflag = 1; timer->init_timeout(); update->nsteps = 1; update->ntimestep = 0; update->firststep = update->ntimestep; update->laststep = update->ntimestep + update->nsteps; update->beginstep = update->firststep; update->endstep = update->laststep; lmp->init(); // engine is now at @INIT_MD node // receive any commands driver wishes to send engine_node("@INIT_MD"); if (strcmp(mdicmd,"@DEFAULT") == 0 || strcmp(mdicmd,"EXIT") == 0) { modify->delete_fix("MDI_ENGINE_INTERNAL"); return; } // setup the MD simulation update->integrate->setup(1); // run MD one step at a time until driver sends @DEFAULT or EXIT // driver can communicate with LAMMPS within each timestep // by sending a node command which matches a method in FixMDIEngine while (true) { update->whichflag = 1; timer->init_timeout(); update->nsteps += 1; update->laststep += 1; update->endstep = update->laststep; output->next = update->ntimestep + 1; // single MD timestep update->integrate->run(1); // driver triggers end of MD loop by sending @DEFAULT or EXIT if (strcmp(mdicmd,"@DEFAULT") == 0 || strcmp(mdicmd,"EXIT") == 0) { modify->delete_fix("MDI_ENGINE_INTERNAL"); return; } } } /* ---------------------------------------------------------------------- perform minimization for at most Niterate steps >TOLERANCE command sets tolerances ---------------------------------------------------------------------- */ void MDIEngine::mdi_optg() { // engine is now at @INIT_OPTG node // receive >TOLERANCE or other commands driver wishes to send // @DEFAULT command from driver will trigger the minimization etol = ftol = 1.0e-6; niterate = max_eval = 1000; engine_node("@INIT_OPTG"); if (strcmp(mdicmd,"EXIT") == 0) return; // create or update system if requested 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(); } // perform the minmization update->etol = etol; update->ftol = ftol; update->nsteps = niterate; update->max_eval = max_eval; update->whichflag = 2; timer->init_timeout(); update->beginstep = update->firststep = update->ntimestep; update->endstep = update->laststep = update->firststep + update->nsteps; lmp->init(); update->minimize->setup(); timer->init(); timer->barrier_start(); update->minimize->run(update->nsteps); 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; } /* ---------------------------------------------------------------------- perform minimization under control of driver one iteration at a time use of fix MDI/ENGINE allows MDI comm within iteration ---------------------------------------------------------------------- */ void MDIEngine::mdi_optg_old() { // add an instance of fix MDI/ENGINE // delete the instance before this method returns if (!enable_fix) error->all(FLERR,"MDI engine command did not enable @INIT_OPTG support"); modify->add_fix("MDI_ENGINE_INTERNAL all MDI/ENGINE"); mdi_fix = (FixMDIEngine *) modify->get_fix_by_id("MDI_ENGINE_INTERNAL"); mdi_fix->mdi_engine = this; // set tolerances to epsilon and iteration limits huge // allows MDI driver to force an exit update->etol = std::numeric_limits::min(); update->ftol = std::numeric_limits::min(); update->nsteps = std::numeric_limits::max(); update->max_eval = std::numeric_limits::max(); update->whichflag = 2; update->beginstep = update->firststep = update->ntimestep; update->endstep = update->laststep = update->firststep + update->nsteps; lmp->init(); // engine is now at @INIT_OPTG node // receive any commands driver wishes to send engine_node("@INIT_OPTG"); if (strcmp(mdicmd,"@DEFAULT") == 0 || strcmp(mdicmd,"EXIT") == 0) { modify->delete_fix("MDI_ENGINE_INTERNAL"); return; } // setup the minimization update->minimize->setup(); if (strcmp(mdicmd,"@DEFAULT") == 0 || strcmp(mdicmd,"EXIT") == 0) return; // start minimization // when the driver sends @DEFAULT or EXIT minimizer tolerances are // set to large values to force it to exit update->minimize->iterate(update->nsteps); // return if driver sends @DEFAULT or EXIT if (strcmp(mdicmd,"@DEFAULT") == 0 || strcmp(mdicmd,"EXIT") == 0) { modify->delete_fix("MDI_ENGINE_INTERNAL"); return; } } /* ---------------------------------------------------------------------- evaluate() invoked by ago < 0) { update->whichflag = 1; lmp->init(); update->integrate->setup(1); update->whichflag = 0; } else { 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); modify->addstep_compute(update->ntimestep+1); } else { 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 ---------------------------------------------------------------------- */ void MDIEngine::create_system() { // >CELL, >NATOMS, >TYPES, >COORDS commands are required // >CELL_DISPL, >CHARGES, >VELOCITIES commands are optional if (flag_cell == 0 || flag_natoms == 0 || flag_types == 0 || flag_coords == 0) error->all(FLERR, "@INIT_SYS requires >CELL, >NATOMS, >TYPES, >COORDS MDI commands"); // clear system via delete_atoms command lmp->input->one("delete_atoms group all"); // 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); // 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 lamda atoms coords back to new box coords domain->set_global_box(); domain->set_local_box(); domain->lamda2x(atom->nlocal); } /* ---------------------------------------------------------------------- adjust charges, coods, velocities ---------------------------------------------------------------------- */ 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 (tag[i]) - 1; q[i] = sys_charges[ilocal]; } } 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 (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]; } } 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 (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]; } } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // responses to ">" 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() { 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() { 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() { 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() { 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() { 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(); } /* ---------------------------------------------------------------------- >TYPES command ---------------------------------------------------------------------- */ void MDIEngine::receive_types() { 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() { 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; } // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- // responses to "<" MDI driver commands that request data // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- /* ---------------------------------------------------------------------- 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: 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: 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: natoms * MDI_LABEL_LENGTH]; memset(labels,' ',atom->natoms * MDI_LABEL_LENGTH); // NOTE: this loop will not work in parallel for (int iatom = 0; iatom < atom->natoms; iatom++) { std::string label = std::to_string(atom->type[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: (atom->natoms); int ierr = MDI_Send(&natoms,1,MDI_INT,mdicomm); if (ierr != 0) error->all(FLERR,"MDI: compute_scalar(); potential_energy *= lmp2mdi_energy; int ierr = MDI_Send(&potential_energy,1,MDI_DOUBLE,mdicomm); if (ierr) error->all(FLERR,"MDI: 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: 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 (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 (tag[i]) - 1; buf1[ilocal] = rmass[i]; } } else { int *type = atom->type; for (int i = 0; i < nlocal; i++) { ilocal = static_cast (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: natoms*sizeof(int)); // use atomID to index into ordered buf1 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 (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: 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 (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 (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 (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: 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; } /* ---------------------------------------------------------------------- >STEPS command send number of tiemsteps ---------------------------------------------------------------------- */ void MDIEngine::receive_niterate() { int ierr = MDI_Recv(&niterate,1,MDI_INT,mdicomm); if (ierr) error->all(FLERR,"MDI: >NITERATE data"); MPI_Bcast(&niterate,1,MPI_INT,0,world); } /* ---------------------------------------------------------------------- >TOLERANCE command send 2 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 (params[2]); max_eval = static_cast (params[3]); } /* ---------------------------------------------------------------------- compute_scalar(); kinetic_energy *= lmp2mdi_energy; int ierr = MDI_Send(&kinetic_energy,1,MDI_DOUBLE,mdicomm); if (ierr) error->all(FLERR,"MDI: 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; } }