// clang-format off /* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories LAMMPS development team: developers@lammps.org 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 "lammps.h" #include "style_angle.h" // IWYU pragma: keep #include "style_atom.h" // IWYU pragma: keep #include "style_body.h" // IWYU pragma: keep #include "style_bond.h" // IWYU pragma: keep #include "style_command.h" // IWYU pragma: keep #include "style_compute.h" // IWYU pragma: keep #include "style_dihedral.h" // IWYU pragma: keep #include "style_dump.h" // IWYU pragma: keep #include "style_fix.h" // IWYU pragma: keep #include "style_improper.h" // IWYU pragma: keep #include "style_integrate.h" // IWYU pragma: keep #include "style_kspace.h" // IWYU pragma: keep #include "style_minimize.h" // IWYU pragma: keep #include "style_pair.h" // IWYU pragma: keep #include "style_reader.h" // IWYU pragma: keep #include "style_region.h" // IWYU pragma: keep #include "accelerator_kokkos.h" #include "accelerator_omp.h" #include "atom.h" #include "citeme.h" #include "comm.h" #include "comm_brick.h" #include "domain.h" #include "error.h" #include "force.h" #include "group.h" #include "info.h" #include "input.h" #include "lmppython.h" #include "memory.h" #include "modify.h" #include "neighbor.h" #include "output.h" #include "suffix.h" #include "timer.h" #include "universe.h" #include "update.h" #include "variable.h" #include "version.h" #if defined(LMP_PLUGIN) #include "plugin.h" #endif #include #include #include #include #include "lmpinstalledpkgs.h" #include "lmpgitversion.h" #if defined(LAMMPS_UPDATE) #define UPDATE_STRING " - " LAMMPS_UPDATE #else #define UPDATE_STRING "" #endif static void print_style(FILE *fp, const char *str, int &pos); struct LAMMPS_NS::package_styles_lists { std::map angle_styles; std::map atom_styles; std::map body_styles; std::map bond_styles; std::map command_styles; std::map compute_styles; std::map dihedral_styles; std::map dump_styles; std::map fix_styles; std::map improper_styles; std::map integrate_styles; std::map kspace_styles; std::map minimize_styles; std::map pair_styles; std::map reader_styles; std::map region_styles; }; using namespace LAMMPS_NS; /** \class LAMMPS_NS::LAMMPS * \brief LAMMPS simulation instance * * The LAMMPS class contains pointers of all constituent class instances * and global variables that are used by a LAMMPS simulation. Its contents * represent the entire state of the simulation. * * The LAMMPS class manages the components of an MD simulation by creating, * deleting, and initializing instances of the classes it is composed of, * processing command-line flags, and providing access to some global properties. * The specifics of setting up and running a simulation are handled by the * individual component class instances. */ /** Create a LAMMPS simulation instance * * \param args list of arguments * \param communicator MPI communicator used by this LAMMPS instance */ LAMMPS::LAMMPS(argv & args, MPI_Comm communicator) : LAMMPS(args.size(), argv_pointers(args).data(), communicator) { } /** Create a LAMMPS simulation instance * * The LAMMPS constructor starts up a simulation by allocating all * fundamental classes in the necessary order, parses input switches * and their arguments, initializes communicators, screen and logfile * output FILE pointers. * * \param narg number of arguments * \param arg list of arguments * \param communicator MPI communicator used by this LAMMPS instance */ LAMMPS::LAMMPS(int narg, char **arg, MPI_Comm communicator) : memory(nullptr), error(nullptr), universe(nullptr), input(nullptr), atom(nullptr), update(nullptr), neighbor(nullptr), comm(nullptr), domain(nullptr), force(nullptr), modify(nullptr), group(nullptr), output(nullptr), timer(nullptr), kokkos(nullptr), atomKK(nullptr), memoryKK(nullptr), python(nullptr), citeme(nullptr) { memory = new Memory(this); error = new Error(this); universe = new Universe(this,communicator); version = (const char *) LAMMPS_VERSION; num_ver = utils::date2num(version); restart_ver = -1; // append git descriptor info to update string when compiling development or maintenance version std::string update_string = UPDATE_STRING; if (has_git_info() && ((update_string == " - Development") || (update_string == " - Maintenance"))) update_string += fmt::format(" - {}", git_descriptor()); external_comm = MPI_COMM_NULL; mdicomm = nullptr; skiprunflag = 0; screen = nullptr; logfile = nullptr; infile = nullptr; initclock = platform::walltime(); init_pkg_lists(); #if defined(LMP_PYTHON) && defined(_WIN32) // If the LAMMPSHOME environment variable is set, it should point // to the location of the LAMMPS installation tree where we bundle // the matching Python installation for use with the PYTHON package. // This is currently only used on Windows with the Windows installer packages const char *lmpenv = getenv("LAMMPSHOME"); if (lmpenv) { platform::putenv(fmt::format("PYTHONHOME={}",lmpenv)); } #endif // check if -mpicolor is first arg // if so, then 2 or more apps were launched with one mpirun command // this means passed communicator (e.g. MPI_COMM_WORLD) is bigger than LAMMPS // universe communicator needs to shrink to be just LAMMPS // syntax: -mpicolor color // color = integer for this app, different than any other app(s) // do the following: // perform an MPI_Comm_split() to create a new LAMMPS-only subcomm // NOTE: this assumes other app(s) make same call, else will hang! // re-create universe with subcomm // store comm that all apps belong to in external_comm int iarg = 1; if (narg-iarg >= 2 && (strcmp(arg[iarg],"-mpicolor") == 0 || strcmp(arg[iarg],"-m") == 0)) { int me,nprocs; MPI_Comm_rank(communicator,&me); MPI_Comm_size(communicator,&nprocs); int color = std::stoi(arg[iarg+1]); MPI_Comm subcomm; MPI_Comm_split(communicator,color,me,&subcomm); external_comm = communicator; communicator = subcomm; delete universe; universe = new Universe(this,communicator); } // parse input switches int inflag = 0; int screenflag = 0; int logflag = 0; int partscreenflag = 0; int partlogflag = 0; int kokkosflag = 0; int restart2data = 0; int restart2dump = 0; int restart2info = 0; int restartremap = 0; int citeflag = 1; int citescreen = CiteMe::TERSE; int citelogfile = CiteMe::VERBOSE; char *citefile = nullptr; int helpflag = 0; int nonbufflag = 0; suffix = suffix2 = nullptr; suffix_enable = 0; pair_only_flag = 0; if (arg) exename = utils::strdup(arg[0]); else exename = nullptr; packargs = nullptr; num_package = 0; char *restartfile = nullptr; int wfirst,wlast; int kkfirst,kklast; int npack = 0; int *pfirst = nullptr; int *plast = nullptr; iarg = 1; while (iarg < narg) { if (strcmp(arg[iarg],"-cite") == 0 || strcmp(arg[iarg],"-c") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); if (strcmp(arg[iarg+1],"both") == 0) { citescreen = CiteMe::VERBOSE; citelogfile = CiteMe::VERBOSE; citefile = nullptr; } else if (strcmp(arg[iarg+1],"none") == 0) { citescreen = CiteMe::TERSE; citelogfile = CiteMe::TERSE; citefile = nullptr; } else if (strcmp(arg[iarg+1],"screen") == 0) { citescreen = CiteMe::VERBOSE; citelogfile = CiteMe::TERSE; citefile = nullptr; } else if (strcmp(arg[iarg+1],"log") == 0) { citescreen = CiteMe::TERSE; citelogfile = CiteMe::VERBOSE; citefile = nullptr; } else { citescreen = CiteMe::TERSE; citelogfile = CiteMe::TERSE; citefile = arg[iarg+1]; } iarg += 2; } else if (strcmp(arg[iarg],"-echo") == 0 || strcmp(arg[iarg],"-e") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); iarg += 2; } else if (strcmp(arg[iarg],"-help") == 0 || strcmp(arg[iarg],"-h") == 0) { if (iarg+1 > narg) error->universe_all(FLERR,"Invalid command-line argument"); helpflag = 1; citeflag = 0; inflag = -1; // skip inflag check iarg += 1; } else if (strcmp(arg[iarg],"-in") == 0 || strcmp(arg[iarg],"-i") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); inflag = iarg + 1; iarg += 2; } else if (strcmp(arg[iarg],"-kokkos") == 0 || strcmp(arg[iarg],"-k") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); const std::string kokkosarg = arg[iarg+1]; if ((kokkosarg == "on") || (kokkosarg == "yes") || (kokkosarg == "true")) kokkosflag = 1; else if ((kokkosarg == "off") || (kokkosarg == "no") || (kokkosarg == "false")) kokkosflag = 0; else error->universe_all(FLERR,"Invalid command-line argument"); iarg += 2; // delimit any extra args for the Kokkos instantiation kkfirst = iarg; while (iarg < narg && arg[iarg][0] != '-') iarg++; kklast = iarg; } else if (strcmp(arg[iarg],"-log") == 0 || strcmp(arg[iarg],"-l") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); logflag = iarg + 1; iarg += 2; } else if (strcmp(arg[iarg],"-mpicolor") == 0 || strcmp(arg[iarg],"-m") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); if (iarg != 1) error->universe_all(FLERR,"Invalid command-line argument"); iarg += 2; } else if (strcmp(arg[iarg],"-nocite") == 0 || strcmp(arg[iarg],"-nc") == 0) { citeflag = 0; iarg++; } else if (strcmp(arg[iarg],"-nonbuf") == 0 || strcmp(arg[iarg],"-nb") == 0) { nonbufflag = 1; iarg++; } else if (strcmp(arg[iarg],"-package") == 0 || strcmp(arg[iarg],"-pk") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); memory->grow(pfirst,npack+1,"lammps:pfirst"); memory->grow(plast,npack+1,"lammps:plast"); // delimit args for package command invocation // any package arg with leading "-" will be followed by numeric digit iarg++; pfirst[npack] = iarg; while (iarg < narg) { if (arg[iarg][0] != '-') iarg++; else if (isdigit(arg[iarg][1])) iarg++; else break; } plast[npack++] = iarg; } else if (strcmp(arg[iarg],"-partition") == 0 || strcmp(arg[iarg],"-p") == 0) { universe->existflag = 1; if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); iarg++; while (iarg < narg && arg[iarg][0] != '-') { universe->add_world(arg[iarg]); iarg++; } } else if (strcmp(arg[iarg],"-plog") == 0 || strcmp(arg[iarg],"-pl") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); partlogflag = iarg + 1; iarg += 2; } else if (strcmp(arg[iarg],"-pscreen") == 0 || strcmp(arg[iarg],"-ps") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); partscreenflag = iarg + 1; iarg += 2; } else if (strcmp(arg[iarg],"-reorder") == 0 || strcmp(arg[iarg],"-ro") == 0) { if (iarg+3 > narg) error->universe_all(FLERR,"Invalid command-line argument"); if (universe->existflag) error->universe_all(FLERR,"Cannot use -reorder after -partition"); universe->reorder(arg[iarg+1],arg[iarg+2]); iarg += 3; } else if (strcmp(arg[iarg],"-restart2data") == 0 || strcmp(arg[iarg],"-r2data") == 0) { if (iarg+3 > narg) error->universe_all(FLERR,"Invalid command-line argument"); if (restart2dump) error->universe_all(FLERR, "Cannot use both -restart2data and -restart2dump"); if (restart2info) error->universe_all(FLERR, "Cannot use both -restart2data and -restart2info"); restart2data = 1; restartfile = arg[iarg+1]; inflag = -1; // skip inflag check // check for restart remap flag if (strcmp(arg[iarg+2],"remap") == 0) { if (iarg+4 > narg) error->universe_all(FLERR,"Invalid command-line argument"); restartremap = 1; iarg++; } iarg += 2; // delimit args for the write_data command wfirst = iarg; while (iarg < narg && arg[iarg][0] != '-') iarg++; wlast = iarg; } else if (strcmp(arg[iarg],"-restart2dump") == 0 || strcmp(arg[iarg],"-r2dump") == 0) { if (iarg+3 > narg) error->universe_all(FLERR,"Invalid command-line argument"); if (restart2data) error->universe_all(FLERR, "Cannot use both -restart2data and -restart2dump"); if (restart2info) error->universe_all(FLERR, "Cannot use both -restart2dump and -restart2info"); restart2dump = 1; restartfile = arg[iarg+1]; inflag = -1; // skip inflag check // check for restart remap flag if (strcmp(arg[iarg+2],"remap") == 0) { if (iarg+4 > narg) error->universe_all(FLERR,"Invalid command-line argument"); restartremap = 1; iarg++; } iarg += 2; // delimit args for the write_dump command wfirst = iarg; while (iarg < narg && arg[iarg][0] != '-') iarg++; wlast = iarg; } else if (strcmp(arg[iarg],"-restart2info") == 0 || strcmp(arg[iarg],"-r2info") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); if (restart2data) error->universe_all(FLERR, "Cannot use both -restart2data and -restart2info"); if (restart2dump) error->universe_all(FLERR, "Cannot use both -restart2dump and -restart2info"); restart2info = 1; restartfile = arg[iarg+1]; inflag = -1; // skip inflag check iarg += 2; // delimit args for the info command wfirst = iarg; while (iarg < narg && arg[iarg][0] != '-') iarg++; wlast = iarg; } else if (strcmp(arg[iarg],"-screen") == 0 || strcmp(arg[iarg],"-sc") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); screenflag = iarg + 1; iarg += 2; } else if (strcmp(arg[iarg],"-skiprun") == 0 || strcmp(arg[iarg],"-sr") == 0) { skiprunflag = 1; ++iarg; } else if (strcmp(arg[iarg],"-suffix") == 0 || strcmp(arg[iarg],"-sf") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); delete[] suffix; delete[] suffix2; suffix = suffix2 = nullptr; suffix_enable = 1; // hybrid option to set fall-back for suffix2 if (strcmp(arg[iarg+1],"hybrid") == 0) { if (iarg+4 > narg) error->universe_all(FLERR,"Invalid command-line argument"); suffix = utils::strdup(arg[iarg+2]); suffix2 = utils::strdup(arg[iarg+3]); iarg += 4; } else { suffix = utils::strdup(arg[iarg+1]); iarg += 2; } } else if (strcmp(arg[iarg],"-var") == 0 || strcmp(arg[iarg],"-v") == 0) { if (iarg+3 > narg) error->universe_all(FLERR,"Invalid command-line argument"); iarg += 3; while (iarg < narg && arg[iarg][0] != '-') iarg++; } else { error->universe_all(FLERR, fmt::format("Invalid command-line argument: {}", arg[iarg]) ); } } // if no partition command-line switch, universe is one world with all procs if (universe->existflag == 0) universe->add_world(nullptr); // sum of procs in all worlds must equal total # of procs if (!universe->consistent()) error->universe_all(FLERR,"Processor partitions do not match number of allocated processors"); // universe cannot use stdin for input file if (universe->existflag && inflag == 0) error->universe_all(FLERR,"Must use -in switch with multiple partitions"); // if no partition command-line switch, cannot use -pscreen option if (universe->existflag == 0 && partscreenflag) error->universe_all(FLERR,"Can only use -pscreen with multiple partitions"); // if no partition command-line switch, cannot use -plog option if (universe->existflag == 0 && partlogflag) error->universe_all(FLERR,"Can only use -plog with multiple partitions"); // set universe screen and logfile if (universe->me == 0) { if (screenflag == 0) universe->uscreen = stdout; else if (strcmp(arg[screenflag],"none") == 0) universe->uscreen = nullptr; else { universe->uscreen = fopen(arg[screenflag],"w"); if (universe->uscreen == nullptr) error->universe_one(FLERR,fmt::format("Cannot open universe screen file {}: {}", arg[screenflag],utils::getsyserror())); } if (logflag == 0) { if (helpflag == 0) { universe->ulogfile = fopen("log.lammps","w"); if (universe->ulogfile == nullptr) error->universe_warn(FLERR,"Cannot open log.lammps for writing: " + utils::getsyserror()); } } else if (strcmp(arg[logflag],"none") == 0) universe->ulogfile = nullptr; else { universe->ulogfile = fopen(arg[logflag],"w"); if (universe->ulogfile == nullptr) error->universe_one(FLERR,fmt::format("Cannot open universe log file {}: {}", arg[logflag],utils::getsyserror())); } } if (universe->me > 0) { if (screenflag == 0) universe->uscreen = stdout; else universe->uscreen = nullptr; universe->ulogfile = nullptr; } // make universe and single world the same, since no partition switch // world inherits settings from universe // set world screen, logfile, communicator, infile // open input script if from file if (universe->existflag == 0) { screen = universe->uscreen; logfile = universe->ulogfile; world = universe->uworld; if (universe->me == 0) { if (inflag <= 0) infile = stdin; else if (strcmp(arg[inflag], "none") == 0) infile = stdin; else infile = fopen(arg[inflag],"r"); if (infile == nullptr) error->all(FLERR,"Cannot open input script {}: {}", arg[inflag], utils::getsyserror()); if (!helpflag) utils::logmesg(this,"LAMMPS ({}{})\n", version, update_string); utils::flush_buffers(this); } // universe is one or more worlds, as setup by partition switch // split universe communicator into separate world communicators // set world screen, logfile, communicator, infile // open input script } else { int me; MPI_Comm_split(universe->uworld,universe->iworld,0,&world); MPI_Comm_rank(world,&me); screen = logfile = infile = nullptr; if (me == 0) { std::string str; if (partscreenflag == 0) { if (screenflag == 0) { str = fmt::format("screen.{}",universe->iworld); screen = fopen(str.c_str(),"w"); if (screen == nullptr) error->one(FLERR,"Cannot open screen file {}: {}",str,utils::getsyserror()); } else if (strcmp(arg[screenflag],"none") == 0) { screen = nullptr; } else { str = fmt::format("{}.{}",arg[screenflag],universe->iworld); screen = fopen(str.c_str(),"w"); if (screen == nullptr) error->one(FLERR,"Cannot open screen file {}: {}",arg[screenflag],utils::getsyserror()); } } else if (strcmp(arg[partscreenflag],"none") == 0) { screen = nullptr; } else { str = fmt::format("{}.{}",arg[partscreenflag],universe->iworld); screen = fopen(str.c_str(),"w"); if (screen == nullptr) error->one(FLERR,"Cannot open screen file {}: {}",str,utils::getsyserror()); } if (partlogflag == 0) { if (logflag == 0) { str = fmt::format("log.lammps.{}",universe->iworld); logfile = fopen(str.c_str(),"w"); if (logfile == nullptr) error->one(FLERR,"Cannot open logfile {}: {}",str, utils::getsyserror()); } else if (strcmp(arg[logflag],"none") == 0) { logfile = nullptr; } else { str = fmt::format("{}.{}",arg[logflag],universe->iworld); logfile = fopen(str.c_str(),"w"); if (logfile == nullptr) error->one(FLERR,"Cannot open logfile {}: {}",str, utils::getsyserror()); } } else if (strcmp(arg[partlogflag],"none") == 0) { logfile = nullptr; } else { str = fmt::format("{}.{}",arg[partlogflag],universe->iworld); logfile = fopen(str.c_str(),"w"); if (logfile == nullptr) error->one(FLERR,"Cannot open logfile {}: {}",str, utils::getsyserror()); } if (strcmp(arg[inflag], "none") != 0) { infile = fopen(arg[inflag],"r"); if (infile == nullptr) error->one(FLERR,"Cannot open input script {}: {}",arg[inflag], utils::getsyserror()); } } // make all screen and logfile output unbuffered for debugging crashes if (nonbufflag) { if (universe->uscreen) setbuf(universe->uscreen, nullptr); if (universe->ulogfile) setbuf(universe->ulogfile, nullptr); if (screen) setbuf(screen, nullptr); if (logfile) setbuf(logfile, nullptr); } // screen and logfile messages for universe and world std::string update_string = UPDATE_STRING; if (has_git_info() && ((update_string == " - Development") || (update_string == " - Maintenance"))) update_string += fmt::format(" - {}", git_descriptor()); if ((universe->me == 0) && (!helpflag)) { constexpr char fmt[] = "LAMMPS ({}{})\nRunning on {} partitions of processors\n"; if (universe->uscreen) utils::print(universe->uscreen, fmt, version, update_string, universe->nworlds); if (universe->ulogfile) utils::print(universe->ulogfile, fmt, version, update_string, universe->nworlds); } if ((me == 0) && (!helpflag)) utils::logmesg(this,"LAMMPS ({}{})\nProcessor partition = {}\n", version, update_string, universe->iworld); } // check consistency of datatype settings in lmptype.h if (sizeof(smallint) != sizeof(int)) error->all(FLERR,"Smallint setting in lmptype.h is invalid"); if (sizeof(imageint) < sizeof(smallint)) error->all(FLERR,"Imageint setting in lmptype.h is invalid"); if (sizeof(tagint) < sizeof(smallint)) error->all(FLERR,"Tagint setting in lmptype.h is invalid"); if (sizeof(bigint) < sizeof(imageint) || sizeof(bigint) < sizeof(tagint)) error->all(FLERR,"Bigint setting in lmptype.h is invalid"); int mpisize; MPI_Type_size(MPI_LMP_TAGINT,&mpisize); if (mpisize != sizeof(tagint)) error->all(FLERR,"MPI_LMP_TAGINT and tagint in lmptype.h are not compatible"); MPI_Type_size(MPI_LMP_BIGINT,&mpisize); if (mpisize != sizeof(bigint)) error->all(FLERR,"MPI_LMP_BIGINT and bigint in lmptype.h are not compatible"); #ifdef LAMMPS_SMALLBIG if (sizeof(smallint) != 4 || sizeof(imageint) != 4 || sizeof(tagint) != 4 || sizeof(bigint) != 8) error->all(FLERR,"Small to big integers are not sized correctly"); #endif #ifdef LAMMPS_BIGBIG if (sizeof(smallint) != 4 || sizeof(imageint) != 8 || sizeof(tagint) != 8 || sizeof(bigint) != 8) error->all(FLERR,"Small to big integers are not sized correctly"); #endif // create Kokkos class if KOKKOS installed, unless explicitly switched off // instantiation creates dummy Kokkos class if KOKKOS is not installed // add args between kkfirst and kklast to Kokkos instantiation kokkos = nullptr; if (kokkosflag == 1) { kokkos = new KokkosLMP(this,kklast-kkfirst,&arg[kkfirst]); if (!kokkos->kokkos_exists) error->all(FLERR,"Cannot use -kokkos on without KOKKOS installed"); } // allocate CiteMe class if enabled if (citeflag) citeme = new CiteMe(this,citescreen,citelogfile,citefile); else citeme = nullptr; // allocate input class now that MPI is fully setup input = new Input(this,narg,arg); // copy package cmdline arguments if (npack > 0) { num_package = npack; packargs = new char**[npack]; for (int i=0; i < npack; ++i) { int n = plast[i] - pfirst[i]; packargs[i] = new char*[n+1]; for (int j=0; j < n; ++j) packargs[i][j] = utils::strdup(arg[pfirst[i]+j]); packargs[i][n] = nullptr; } memory->destroy(pfirst); memory->destroy(plast); } // if helpflag set, print help and quit with "success" status // otherwise allocate top level classes. if (helpflag) { if (universe->me == 0 && screen) help(); error->done(0); } else { create(); post_create(); } // if either restart conversion option was used, invoke 2 commands and quit // add args between wfirst and wlast to write_data or write_data command // add "noinit" to write_data to prevent a system init if (restart2data || restart2dump || restart2info) { std::string cmd = fmt::format("read_restart {}",restartfile); if (restartremap) cmd += " remap\n"; input->one(cmd); if (restart2data) cmd = "write_data "; else if (restart2dump) cmd = "write_dump"; else cmd = "info system group compute fix"; for (iarg = wfirst; iarg < wlast; iarg++) cmd += fmt::format(" {}", arg[iarg]); if (restart2data || restart2dump) cmd += " noinit"; input->one(cmd); error->done(0); } } /** Shut down a LAMMPS simulation instance * * The LAMMPS destructor shuts down the simulation by deleting top-level class * instances, closing screen and log files for the global instance (aka "world") * and files and MPI communicators in sub-partitions ("universes"). Then it * deletes the fundamental class instances and copies of data inside the class. */ LAMMPS::~LAMMPS() noexcept(false) { const int me = comm->me; delete citeme; destroy(); if (num_package) { for (int i = 0; i < num_package; i++) { for (char **ptr = packargs[i]; *ptr != nullptr; ++ptr) delete[] *ptr; delete[] packargs[i]; } delete[] packargs; } num_package = 0; packargs = nullptr; double totalclock = platform::walltime() - initclock; if ((me == 0) && (screen || logfile)) { int seconds = fmod(totalclock,60.0); totalclock = (totalclock - seconds) / 60.0; int minutes = fmod(totalclock,60.0); int hours = (totalclock - minutes) / 60.0; utils::logmesg(this, "Total wall time: {}:{:02d}:{:02d}\n", hours, minutes, seconds); } if (universe->nworlds == 1) { if (screen && screen != stdout) fclose(screen); if (logfile) fclose(logfile); logfile = nullptr; if (screen != stdout) screen = nullptr; } else { if (screen && screen != stdout) fclose(screen); if (logfile) fclose(logfile); if (universe->ulogfile) fclose(universe->ulogfile); logfile = nullptr; if (screen != stdout) screen = nullptr; } if (infile && infile != stdin) fclose(infile); if (world != universe->uworld) MPI_Comm_free(&world); delete python; delete kokkos; delete[] suffix; delete[] suffix2; // free the MPI comm created by -mpicolor cmdline arg processed in constructor // it was passed to universe as if original universe world // may have been split later by partitions, universe will free the splits // free a copy of uorig here, so check in universe destructor will still work MPI_Comm copy = universe->uorig; if (external_comm != MPI_COMM_NULL) MPI_Comm_free(©); delete input; delete universe; delete error; delete memory; delete pkg_lists; delete[] exename; } /* ---------------------------------------------------------------------- allocate single instance of top-level classes fundamental classes are allocated in constructor some classes have package variants ------------------------------------------------------------------------- */ void LAMMPS::create() { force = nullptr; // Domain->Lattice checks if Force exists // Comm class must be created before Atom class // so that nthreads is defined when create_avec invokes grow() if (kokkos) comm = new CommKokkos(this); else comm = new CommBrick(this); if (kokkos) neighbor = new NeighborKokkos(this); else neighbor = new Neighbor(this); if (kokkos) domain = new DomainKokkos(this); #ifdef LMP_OPENMP else domain = new DomainOMP(this); #else else domain = new Domain(this); #endif if (kokkos) atom = new AtomKokkos(this); else atom = new Atom(this); if (kokkos) atom->create_avec("atomic/kk",0,nullptr,1); else atom->create_avec("atomic",0,nullptr,1); if (kokkos) group = new GroupKokkos(this); else group = new Group(this); force = new Force(this); // must be after group, to create temperature if (kokkos) modify = new ModifyKokkos(this); else modify = new Modify(this); output = new Output(this); // must be after group, so "all" exists // must be after modify so can create Computes update = new Update(this); // must be after output, force, neighbor timer = new Timer(this); python = new Python(this); // restore and auto-load plugins #if defined(LMP_PLUGIN) plugin_restore(this, true); plugin_auto_load(this); #endif } /* ---------------------------------------------------------------------- check suffix consistency with installed packages invoke package-specific default package commands only invoke if suffix is set and enabled also check if suffix2 is set called from LAMMPS constructor and after clear() command so that package-specific core classes have been instantiated ------------------------------------------------------------------------- */ void LAMMPS::post_create() { if (skiprunflag) input->one("timer timeout 0 every 1"); // Don't unnecessarily reissue a package command via suffix int package_issued = Suffix::NONE; // default package command triggered by "-k on" if (kokkos && kokkos->kokkos_exists) input->one("package kokkos"); // invoke any command-line package commands if (num_package) { std::string str; for (int i = 0; i < num_package; i++) { str = "package"; char *pkg_name = *(packargs[i]); if (pkg_name != nullptr) { if (strcmp("gpu", pkg_name) == 0) package_issued |= Suffix::GPU; if (strcmp("omp", pkg_name) == 0) package_issued |= Suffix::OMP; if (strcmp("intel", pkg_name) == 0) package_issued |= Suffix::INTEL; } for (char **ptr = packargs[i]; *ptr != nullptr; ++ptr) { str += " "; str += *ptr; } input->one(str); } } // check that KOKKOS package classes were instantiated // check that GPU, INTEL, OPENMP fixes were compiled with LAMMPS // do not re-issue package command if already issued if (suffix_enable) { if (strcmp(suffix,"gpu") == 0 && !modify->check_package("GPU")) error->all(FLERR,"Using suffix gpu without GPU package installed"); if (strcmp(suffix,"intel") == 0 && !modify->check_package("INTEL")) error->all(FLERR,"Using suffix intel without INTEL package installed"); if (strcmp(suffix,"kk") == 0 && (kokkos == nullptr || kokkos->kokkos_exists == 0)) error->all(FLERR,"Using suffix kk without KOKKOS package enabled"); if (strcmp(suffix,"omp") == 0 && !modify->check_package("OMP")) error->all(FLERR,"Using suffix omp without OPENMP package installed"); if (strcmp(suffix,"gpu") == 0 && !(package_issued & Suffix::GPU)) input->one("package gpu 0"); if (strcmp(suffix,"intel") == 0 && !(package_issued & Suffix::INTEL)) input->one("package intel 1"); if (strcmp(suffix,"omp") == 0 && !(package_issued & Suffix::OMP)) input->one("package omp 0"); if (suffix2) { if (strcmp(suffix2,"gpu") == 0 && !(package_issued & Suffix::GPU)) input->one("package gpu 0"); if (strcmp(suffix2,"intel") == 0 && !(package_issued & Suffix::INTEL)) input->one("package intel 1"); if (strcmp(suffix2,"omp") == 0 && !(package_issued & Suffix::OMP)) input->one("package omp 0"); } } } /* ---------------------------------------------------------------------- initialize top-level classes do not initialize Timer class, other classes like Run() do that explicitly ------------------------------------------------------------------------- */ void LAMMPS::init() { update->init(); force->init(); // pair must come after update due to minimizer domain->init(); atom->init(); // atom must come after force and domain // atom deletes extra array // used by fix shear_history::unpack_restart() // when force->pair->gran_history creates fix // atom_vec init uses deform_vremap modify->init(); // modify must come after update, force, atom, domain neighbor->init(); // neighbor must come after force, modify comm->init(); // comm must come after force, modify, neighbor, atom output->init(); // output must come after domain, force, modify } /* ---------------------------------------------------------------------- delete single instance of top-level classes fundamental classes are deleted in destructor ------------------------------------------------------------------------- */ void LAMMPS::destroy() { delete update; update = nullptr; delete neighbor; neighbor = nullptr; delete force; force = nullptr; delete group; group = nullptr; delete output; output = nullptr; // undefine atomfile variables because they use a fix for backing storage input->variable->purge_atomfile(); delete modify; // modify must come after output, force, update // since they delete fixes modify = nullptr; delete comm; // comm must come after modify // since fix destructors may access comm comm = nullptr; delete domain; // domain must come after modify // since fix destructors access domain domain = nullptr; delete atom; // atom must come after modify, neighbor // since fixes delete callbacks in atom atom = nullptr; delete timer; timer = nullptr; delete python; python = nullptr; restart_ver = -1; // reset last restart version id } /* ---------------------------------------------------------------------- initialize lists of styles in packages ------------------------------------------------------------------------- */ void _noopt LAMMPS::init_pkg_lists() { pkg_lists = new package_styles_lists; #define PACKAGE "UNKNOWN" #define ANGLE_CLASS #define AngleStyle(key,Class) \ pkg_lists->angle_styles[#key] = PACKAGE; #include "packages_angle.h" #undef AngleStyle #undef ANGLE_CLASS #define ATOM_CLASS #define AtomStyle(key,Class) \ pkg_lists->atom_styles[#key] = PACKAGE; #include "packages_atom.h" #undef AtomStyle #undef ATOM_CLASS #define BODY_CLASS #define BodyStyle(key,Class) \ pkg_lists->body_styles[#key] = PACKAGE; #include "packages_body.h" #undef BodyStyle #undef BODY_CLASS #define BOND_CLASS #define BondStyle(key,Class) \ pkg_lists->bond_styles[#key] = PACKAGE; #include "packages_bond.h" #undef BondStyle #undef BOND_CLASS #define COMMAND_CLASS #define CommandStyle(key,Class) \ pkg_lists->command_styles[#key] = PACKAGE; #include "packages_command.h" #undef CommandStyle #undef COMMAND_CLASS #define COMPUTE_CLASS #define ComputeStyle(key,Class) \ pkg_lists->compute_styles[#key] = PACKAGE; #include "packages_compute.h" #undef ComputeStyle #undef COMPUTE_CLASS #define DIHEDRAL_CLASS #define DihedralStyle(key,Class) \ pkg_lists->dihedral_styles[#key] = PACKAGE; #include "packages_dihedral.h" #undef DihedralStyle #undef DIHEDRAL_CLASS #define DUMP_CLASS #define DumpStyle(key,Class) \ pkg_lists->dump_styles[#key] = PACKAGE; #include "packages_dump.h" #undef DumpStyle #undef DUMP_CLASS #define FIX_CLASS #define FixStyle(key,Class) \ pkg_lists->fix_styles[#key] = PACKAGE; #include "packages_fix.h" #undef FixStyle #undef FIX_CLASS #define IMPROPER_CLASS #define ImproperStyle(key,Class) \ pkg_lists->improper_styles[#key] = PACKAGE; #include "packages_improper.h" #undef ImproperStyle #undef IMPROPER_CLASS #define INTEGRATE_CLASS #define IntegrateStyle(key,Class) \ pkg_lists->integrate_styles[#key] = PACKAGE; #include "packages_integrate.h" #undef IntegrateStyle #undef INTEGRATE_CLASS #define KSPACE_CLASS #define KSpaceStyle(key,Class) \ pkg_lists->kspace_styles[#key] = PACKAGE; #include "packages_kspace.h" #undef KSpaceStyle #undef KSPACE_CLASS #define MINIMIZE_CLASS #define MinimizeStyle(key,Class) \ pkg_lists->minimize_styles[#key] = PACKAGE; #include "packages_minimize.h" #undef MinimizeStyle #undef MINIMIZE_CLASS #define PAIR_CLASS #define PairStyle(key,Class) \ pkg_lists->pair_styles[#key] = PACKAGE; #include "packages_pair.h" #undef PairStyle #undef PAIR_CLASS #define READER_CLASS #define ReaderStyle(key,Class) \ pkg_lists->reader_styles[#key] = PACKAGE; #include "packages_reader.h" #undef ReaderStyle #undef READER_CLASS #define REGION_CLASS #define RegionStyle(key,Class) \ pkg_lists->region_styles[#key] = PACKAGE; #include "packages_region.h" #undef RegionStyle #undef REGION_CLASS } /** Return true if a LAMMPS package is enabled in this binary * * \param pkg name of package * \return true if yes, else false */ bool LAMMPS::is_installed_pkg(const char *pkg) { for (int i=0; installed_packages[i] != nullptr; ++i) if (strcmp(installed_packages[i],pkg) == 0) return true; return false; } #define check_for_match(style,list,name) \ if (strcmp(list,#style) == 0) { \ std::map &styles(pkg_lists-> style ## _styles); \ if (styles.find(name) != styles.end()) { \ return styles[name].c_str(); \ } \ } /** \brief Return name of package that a specific style belongs to * * This function checks the given name against all list of styles * for all types of styles and if the name and the style match, it * returns which package this style belongs to. * * \param style Type of style (e.g. atom, pair, fix, etc.) * \param name Name of style * \return Name of the package this style is part of */ const char *LAMMPS::match_style(const char *style, const char *name) { check_for_match(angle,style,name); check_for_match(atom,style,name); check_for_match(body,style,name); check_for_match(bond,style,name); check_for_match(command,style,name); check_for_match(compute,style,name); check_for_match(dihedral,style,name); check_for_match(dump,style,name); check_for_match(fix,style,name); check_for_match(improper,style,name); check_for_match(integrate,style,name); check_for_match(kspace,style,name); check_for_match(minimize,style,name); check_for_match(pair,style,name); check_for_match(reader,style,name); check_for_match(region,style,name); return nullptr; } #undef check_for_match /** \brief Return suffix for non-pair styles depending on pair_only_flag * * \return suffix or null pointer */ const char *LAMMPS::non_pair_suffix() const { const char *mysuffix; if (pair_only_flag) { #ifdef LMP_KOKKOS_GPU if (utils::strmatch(suffix,"^kk")) mysuffix = "kk/host"; else mysuffix = nullptr; #else mysuffix = nullptr; #endif } else { mysuffix = suffix; } return mysuffix; } /* ---------------------------------------------------------------------- help message for command-line options and styles present in executable ------------------------------------------------------------------------- */ void _noopt LAMMPS::help() { FILE *fp = screen; const char *pager = nullptr; // if output is a console, use a pipe to a pager for paged output. // this will avoid the most important help text to rush past the // user. scrollback buffers are often not large enough. this is most // beneficial to windows users, who are not used to command-line. int use_pager = platform::is_console(fp); // cannot use this with OpenMPI since its console is non-functional #if defined(OPEN_MPI) use_pager = 0; #endif if (use_pager) { pager = getenv("PAGER"); if (pager == nullptr) pager = "more"; fp = platform::popen(pager,"w"); // reset to original state, if pipe command failed if (fp == nullptr) { fp = screen; pager = nullptr; } } // general help message about command-line and flags if (has_git_info()) { fprintf(fp,"\nLarge-scale Atomic/Molecular Massively Parallel Simulator - " LAMMPS_VERSION UPDATE_STRING "\nGit info (%s / %s)\n\n",git_branch(), git_descriptor()); } else { fprintf(fp,"\nLarge-scale Atomic/Molecular Massively Parallel Simulator - " LAMMPS_VERSION UPDATE_STRING "\n\n"); } fprintf(fp, "Usage example: %s -var t 300 -echo screen -in in.alloy\n\n" "List of command-line options supported by this LAMMPS executable:\n\n" "-echo none/screen/log/both : echoing of input script (-e)\n" "-help : print this help message (-h)\n" "-in none/filename : read input from file or stdin (default) (-i)\n" "-kokkos on/off ... : turn KOKKOS mode on or off (-k)\n" "-log none/filename : where to send log output (-l)\n" "-mdi '' : pass flags to the MolSSI Driver Interface\n" "-mpicolor color : which exe in a multi-exe mpirun cmd (-m)\n" "-cite : select citation reminder style (-c)\n" "-nocite : disable citation reminder (-nc)\n" "-nonbuf : disable screen/logfile buffering (-nb)\n" "-package style ... : invoke package command (-pk)\n" "-partition size1 size2 ... : assign partition sizes (-p)\n" "-plog basename : basename for partition logs (-pl)\n" "-pscreen basename : basename for partition screens (-ps)\n" "-restart2data rfile dfile ... : convert restart to data file (-r2data)\n" "-restart2dump rfile dgroup dstyle dfile ... \n" " : convert restart to dump file (-r2dump)\n" "-restart2info rfile : print info about restart rfile (-r2info)\n" "-reorder topology-specs : processor reordering (-r)\n" "-screen none/filename : where to send screen output (-sc)\n" "-skiprun : skip loops in run and minimize (-sr)\n" "-suffix gpu/intel/kk/opt/omp: style suffix to apply (-sf)\n" "-var varname value : set index style variable (-v)\n\n", exename); print_config(fp); fprintf(fp,"List of individual style options included in this LAMMPS executable\n\n"); int pos = 80; fprintf(fp,"* Atom styles:\n"); #define ATOM_CLASS #define AtomStyle(key,Class) print_style(fp,#key,pos); #include "style_atom.h" // IWYU pragma: keep #undef ATOM_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Integrate styles:\n"); #define INTEGRATE_CLASS #define IntegrateStyle(key,Class) print_style(fp,#key,pos); #include "style_integrate.h" // IWYU pragma: keep #undef INTEGRATE_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Minimize styles:\n"); #define MINIMIZE_CLASS #define MinimizeStyle(key,Class) print_style(fp,#key,pos); #include "style_minimize.h" // IWYU pragma: keep #undef MINIMIZE_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Pair styles:\n"); #define PAIR_CLASS #define PairStyle(key,Class) print_style(fp,#key,pos); #include "style_pair.h" // IWYU pragma: keep #undef PAIR_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Bond styles:\n"); #define BOND_CLASS #define BondStyle(key,Class) print_style(fp,#key,pos); #include "style_bond.h" // IWYU pragma: keep #undef BOND_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Angle styles:\n"); #define ANGLE_CLASS #define AngleStyle(key,Class) print_style(fp,#key,pos); #include "style_angle.h" // IWYU pragma: keep #undef ANGLE_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Dihedral styles:\n"); #define DIHEDRAL_CLASS #define DihedralStyle(key,Class) print_style(fp,#key,pos); #include "style_dihedral.h" // IWYU pragma: keep #undef DIHEDRAL_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Improper styles:\n"); #define IMPROPER_CLASS #define ImproperStyle(key,Class) print_style(fp,#key,pos); #include "style_improper.h" // IWYU pragma: keep #undef IMPROPER_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* KSpace styles:\n"); #define KSPACE_CLASS #define KSpaceStyle(key,Class) print_style(fp,#key,pos); #include "style_kspace.h" // IWYU pragma: keep #undef KSPACE_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Fix styles\n"); #define FIX_CLASS #define FixStyle(key,Class) print_style(fp,#key,pos); #include "style_fix.h" // IWYU pragma: keep #undef FIX_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Compute styles:\n"); #define COMPUTE_CLASS #define ComputeStyle(key,Class) print_style(fp,#key,pos); #include "style_compute.h" // IWYU pragma: keep #undef COMPUTE_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Region styles:\n"); #define REGION_CLASS #define RegionStyle(key,Class) print_style(fp,#key,pos); #include "style_region.h" // IWYU pragma: keep #undef REGION_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Dump styles:\n"); #define DUMP_CLASS #define DumpStyle(key,Class) print_style(fp,#key,pos); #include "style_dump.h" // IWYU pragma: keep #undef DUMP_CLASS fprintf(fp,"\n\n"); pos = 80; fprintf(fp,"* Command styles\n"); #define COMMAND_CLASS #define CommandStyle(key,Class) print_style(fp,#key,pos); #include "style_command.h" // IWYU pragma: keep #undef COMMAND_CLASS fprintf(fp,"\n\n"); // close pipe to pager, if active if (pager != nullptr) platform::pclose(fp); } /* ---------------------------------------------------------------------- print style names in columns skip any internal style that starts with an upper-case letter also skip "redundant" KOKKOS styles ending in kk/host or kk/device ------------------------------------------------------------------------- */ void print_style(FILE *fp, const char *str, int &pos) { if (isupper(str[0]) || utils::strmatch(str,"/kk/host$") || utils::strmatch(str,"/kk/device$")) return; int len = strlen(str); if (pos+len > 80) { fprintf(fp,"\n"); pos = 0; } if (len < 16) { fprintf(fp,"%-16s",str); pos += 16; } else if (len < 32) { fprintf(fp,"%-32s",str); pos += 32; } else if (len < 48) { fprintf(fp,"%-48s",str); pos += 48; } else if (len < 64) { fprintf(fp,"%-64s",str); pos += 64; } else { fprintf(fp,"%-80s",str); pos += 80; } } void LAMMPS::print_config(FILE *fp) { const char *pkg; int ncword, ncline = 0; utils::print(fp,"OS: {}\n\n",platform::os_info()); utils::print(fp,"Compiler: {} with {}\nC++ standard: {}\n", platform::compiler_info(),platform::openmp_standard(), platform::cxx_standard()); fputs(Info::get_fmt_info().c_str(),fp); fputs(Info::get_json_info().c_str(),fp); int major,minor; std::string infobuf = platform::mpi_info(major,minor); utils::print(fp,"\nMPI v{}.{}: {}\n\n",major,minor,infobuf); utils::print(fp,"Accelerator configuration:\n\n{}\n", Info::get_accelerator_info()); #if defined(LMP_GPU) utils::print(fp,"Compatible GPU present: {}\n\n",Info::has_gpu_device() ? "yes" : "no"); #endif fputs("FFT information:\n\n",fp); fputs(Info::get_fft_info().c_str(),fp); fputs("\nActive compile time flags:\n\n",fp); if (Info::has_gzip_support()) fputs("-DLAMMPS_GZIP\n",fp); if (Info::has_png_support()) fputs("-DLAMMPS_PNG\n",fp); if (Info::has_jpeg_support()) fputs("-DLAMMPS_JPEG\n",fp); if (Info::has_ffmpeg_support()) fputs("-DLAMMPS_FFMPEG\n",fp); if (Info::has_curl_support()) fputs("-DLAMMPS_CURL\n",fp); if (Info::has_fft_single_support()) fputs("-DFFT_SINGLE\n",fp); #if defined(LAMMPS_BIGBIG) fputs("-DLAMMPS_BIGBIG\n",fp); #elif defined(LAMMPS_SMALLBIG) fputs("-DLAMMPS_SMALLBIG\n",fp); #endif utils::print(fp,"sizeof(smallint): {}-bit\n" "sizeof(imageint): {}-bit\n" "sizeof(tagint): {}-bit\n" "sizeof(bigint): {}-bit\n", sizeof(smallint)*8, sizeof(imageint)*8, sizeof(tagint)*8, sizeof(bigint)*8); if (Info::has_gzip_support()) utils::print(fp,"\n{}\n",platform::compress_info()); fputs("\nInstalled packages:\n\n",fp); for (int i = 0; nullptr != (pkg = installed_packages[i]); ++i) { ncword = strlen(pkg); if (ncline + ncword > 78) { ncline = 0; fputs("\n",fp); } fprintf(fp,"%s ",pkg); ncline += ncword + 1; } fputs("\n\n",fp); } /** Create vector of argv string pointers including terminating nullptr element * * \param args list of arguments */ std::vector LAMMPS::argv_pointers(argv & args){ std::vector r; r.reserve(args.size()+1); for(auto & a : args) { r.push_back((char*)a.data()); } r.push_back(nullptr); return r; }