/* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ #include "mpi.h" #include "string.h" #include "lammps.h" #include "style_angle.h" #include "style_atom.h" #include "style_bond.h" #include "style_command.h" #include "style_compute.h" #include "style_dihedral.h" #include "style_dump.h" #include "style_fix.h" #include "style_improper.h" #include "style_integrate.h" #include "style_kspace.h" #include "style_minimize.h" #include "style_pair.h" #include "style_region.h" #include "universe.h" #include "input.h" #include "atom.h" #include "update.h" #include "neighbor.h" #include "comm.h" #include "comm_brick.h" #include "domain.h" #include "force.h" #include "modify.h" #include "group.h" #include "output.h" #include "citeme.h" #include "accelerator_cuda.h" #include "accelerator_kokkos.h" #include "accelerator_omp.h" #include "timer.h" #include "memory.h" #include "error.h" #include "version.h" #include using namespace LAMMPS_NS; // for help flag output static void help_message(FILE *); static void print_columns(const char **, const int, FILE *); /* ---------------------------------------------------------------------- start up LAMMPS allocate fundamental classes (memory, error, universe, input) parse input switches initialize communicators, screen & logfile output input is allocated at end after MPI info is setup ------------------------------------------------------------------------- */ LAMMPS::LAMMPS(int narg, char **arg, MPI_Comm communicator) { memory = new Memory(this); error = new Error(this); universe = new Universe(this,communicator); output = NULL; screen = NULL; logfile = NULL; infile = NULL; // parse input switches int inflag = 0; int screenflag = 0; int logflag = 0; int partscreenflag = 0; int partlogflag = 0; int cudaflag = 0; int kokkosflag = 0; int restartflag = 0; int restartremapflag = 0; int citeflag = 1; int helpflag = 0; suffix = NULL; suffix_enable = 0; char *rfile = NULL; char *dfile = NULL; int wdfirst,wdlast; int kkfirst,kklast; #if 0 // read .lammpsrc in home and current directory for overriding defaults const char *rcpath; char *homepath = NULL; #ifdef _WIN32 const char rcname[] = "lammps.rc"; #else const char rcname[] = ".lammpsrc"; const char *homedir = getenv("HOME"); if (homedir) { int len = strlen(homedir) + strlen(rcname); homepath = new char[len+2]; strcpy(homepath,homedir); strcat(homepath,"/"); strcat(homepath,rcname); } #endif FILE *fd; do { if (homepath) rcpath = homepath; else rcpath = rcname; fd = fopen(rcpath,"r"); if (fd) { char linebuf[1024]; char *key, *value; // loop through file while(1) { fgets(linebuf,1024,fd); if (feof(fd) || ferror(fd)) break; // truncate line at comment character, if present if ((key = strstr(linebuf,"#"))) *key = '\0'; key = strtok(linebuf," \t\n\r\f"); value = strtok(NULL," \t\n\r\f"); // skip empty lines if (key == NULL) continue; } fclose(fd); } if (homepath) { delete[] homepath; homepath = NULL; } } while(rcpath != rcname); #endif // parsing command line flags int iarg = 1; while (iarg < narg) { 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],"-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],"-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],"-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],"-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 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],"-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],"-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],"-cuda") == 0 || strcmp(arg[iarg],"-c") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); if (strcmp(arg[iarg+1],"on") == 0) cudaflag = 1; else if (strcmp(arg[iarg+1],"off") == 0) cudaflag = 0; else error->universe_all(FLERR,"Invalid command-line argument"); 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"); if (strcmp(arg[iarg+1],"on") == 0) kokkosflag = 1; else if (strcmp(arg[iarg+1],"off") == 0) 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],"-suffix") == 0 || strcmp(arg[iarg],"-sf") == 0) { if (iarg+2 > narg) error->universe_all(FLERR,"Invalid command-line argument"); delete [] suffix; int n = strlen(arg[iarg+1]) + 1; suffix = new char[n]; strcpy(suffix,arg[iarg+1]); suffix_enable = 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],"-restart") == 0 || strcmp(arg[iarg],"-r") == 0) { if (iarg+3 > narg) error->universe_all(FLERR,"Invalid command-line argument"); restartflag = 1; rfile = arg[iarg+1]; dfile = arg[iarg+2]; // check for restart remap flag if (strcmp(dfile,"remap") == 0) { if (iarg+4 > narg) error->universe_all(FLERR,"Invalid command-line argument"); restartremapflag = 1; dfile = arg[iarg+3]; iarg++; } iarg += 3; // delimit any extra args for the write_data command wdfirst = iarg; while (iarg < narg && arg[iarg][0] != '-') iarg++; wdlast = iarg; } else if (strcmp(arg[iarg],"-nocite") == 0 || strcmp(arg[iarg],"-nc") == 0) { citeflag = 0; iarg++; } 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; iarg += 1; } else error->universe_all(FLERR,"Invalid command-line argument"); } // if no partition command-line switch, universe is one world with all procs if (universe->existflag == 0) universe->add_world(NULL); // sum of procs in all worlds must equal total # of procs if (!universe->consistent()) error->universe_all(FLERR,"Processor partitions are inconsistent"); // 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 = NULL; else { universe->uscreen = fopen(arg[screenflag],"w"); if (universe->uscreen == NULL) error->universe_one(FLERR,"Cannot open universe screen file"); } if (logflag == 0) { if (helpflag == 0) { universe->ulogfile = fopen("log.lammps","w"); if (universe->ulogfile == NULL) error->universe_warn(FLERR,"Cannot open log.lammps for writing"); } } else if (strcmp(arg[logflag],"none") == 0) universe->ulogfile = NULL; else { universe->ulogfile = fopen(arg[logflag],"w"); if (universe->ulogfile == NULL) error->universe_one(FLERR,"Cannot open universe log file"); } } if (universe->me > 0) { if (screenflag == 0) universe->uscreen = stdout; else universe->uscreen = NULL; universe->ulogfile = NULL; } // 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 infile = fopen(arg[inflag],"r"); if (infile == NULL) { char str[128]; sprintf(str,"Cannot open input script %s",arg[inflag]); error->one(FLERR,str); } } if (universe->me == 0) { if (screen) fprintf(screen,"LAMMPS (%s)\n",universe->version); if (logfile) fprintf(logfile,"LAMMPS (%s)\n",universe->version); } // 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); if (me == 0) if (partscreenflag == 0) if (screenflag == 0) { char str[32]; sprintf(str,"screen.%d",universe->iworld); screen = fopen(str,"w"); if (screen == NULL) error->one(FLERR,"Cannot open screen file"); } else if (strcmp(arg[screenflag],"none") == 0) screen = NULL; else { char str[128]; sprintf(str,"%s.%d",arg[screenflag],universe->iworld); screen = fopen(str,"w"); if (screen == NULL) error->one(FLERR,"Cannot open screen file"); } else if (strcmp(arg[partscreenflag],"none") == 0) screen = NULL; else { char str[128]; sprintf(str,"%s.%d",arg[partscreenflag],universe->iworld); screen = fopen(str,"w"); if (screen == NULL) error->one(FLERR,"Cannot open screen file"); } else screen = NULL; if (me == 0) if (partlogflag == 0) if (logflag == 0) { char str[32]; sprintf(str,"log.lammps.%d",universe->iworld); logfile = fopen(str,"w"); if (logfile == NULL) error->one(FLERR,"Cannot open logfile"); } else if (strcmp(arg[logflag],"none") == 0) logfile = NULL; else { char str[128]; sprintf(str,"%s.%d",arg[logflag],universe->iworld); logfile = fopen(str,"w"); if (logfile == NULL) error->one(FLERR,"Cannot open logfile"); } else if (strcmp(arg[partlogflag],"none") == 0) logfile = NULL; else { char str[128]; sprintf(str,"%s.%d",arg[partlogflag],universe->iworld); logfile = fopen(str,"w"); if (logfile == NULL) error->one(FLERR,"Cannot open logfile"); } else logfile = NULL; if (me == 0) { infile = fopen(arg[inflag],"r"); if (infile == NULL) { char str[128]; sprintf(str,"Cannot open input script %s",arg[inflag]); error->one(FLERR,str); } } else infile = NULL; // screen and logfile messages for universe and world if (universe->me == 0) { if (universe->uscreen) { fprintf(universe->uscreen,"LAMMPS (%s)\n",universe->version); fprintf(universe->uscreen,"Running on %d partitions of processors\n", universe->nworlds); } if (universe->ulogfile) { fprintf(universe->ulogfile,"LAMMPS (%s)\n",universe->version); fprintf(universe->ulogfile,"Running on %d partitions of processors\n", universe->nworlds); } } if (me == 0) { if (screen) { fprintf(screen,"LAMMPS (%s)\n",universe->version); fprintf(screen,"Processor partition = %d\n",universe->iworld); } if (logfile) { fprintf(logfile,"LAMMPS (%s)\n",universe->version); fprintf(logfile,"Processor partition = %d\n",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 #ifdef LAMMPS_SMALLSMALL if (sizeof(smallint) != 4 || sizeof(imageint) != 4 || sizeof(tagint) != 4 || sizeof(bigint) != 4) error->all(FLERR,"Small to big integers are not sized correctly"); #endif // error check on accelerator packages if (cudaflag == 1 && kokkosflag == 1) error->all(FLERR,"Cannot use -cuda on and -kokkos on together"); // create Cuda class if USER-CUDA installed, unless explicitly switched off // instantiation creates dummy Cuda class if USER-CUDA is not installed cuda = NULL; if (cudaflag == 1) { cuda = new Cuda(this); if (!cuda->cuda_exists) error->all(FLERR,"Cannot use -cuda on without USER-CUDA installed"); } int me; MPI_Comm_rank(world,&me); if (cuda && me == 0) error->message(FLERR,"USER-CUDA mode is enabled"); // 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 = NULL; 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"); } MPI_Comm_rank(world,&me); if (kokkos && me == 0) error->message(FLERR,"KOKKOS mode is enabled"); // allocate CiteMe class if enabled if (citeflag) citeme = new CiteMe(this); else citeme = NULL; // allocate input class now that MPI is fully setup input = new Input(this,narg,arg); // allocate top-level classes create(); post_create(); // if helpflag set, print help and quit if (helpflag) { if (universe->me == 0) help_message(screen); error->done(); } // if restartflag set, invoke 2 commands and quit // add args between wdfirst and wdlast to write_data command // also add "noinit" to prevent write_data from doing system init if (restartflag) { char cmd[128]; sprintf(cmd,"read_restart %s\n",rfile); if (restartremapflag) strcat(cmd," remap\n"); input->one(cmd); sprintf(cmd,"write_data %s",dfile); for (iarg = wdfirst; iarg < wdlast; iarg++) sprintf(&cmd[strlen(cmd)]," %s",arg[iarg]); strcat(cmd," noinit\n"); input->one(cmd); error->done(); } } /* ---------------------------------------------------------------------- shutdown LAMMPS delete top-level classes close screen and log files in world and universe output files were already closed in destroy() delete fundamental classes ------------------------------------------------------------------------- */ LAMMPS::~LAMMPS() { destroy(); delete citeme; if (universe->nworlds == 1) { if (screen && screen != stdout) fclose(screen); if (logfile) fclose(logfile); logfile = NULL; if (screen != stdout) screen = NULL; } else { if (screen && screen != stdout) fclose(screen); if (logfile) fclose(logfile); if (universe->ulogfile) fclose(universe->ulogfile); logfile = NULL; if (screen != stdout) screen = NULL; } if (infile && infile != stdin) fclose(infile); if (world != universe->uworld) MPI_Comm_free(&world); delete cuda; delete kokkos; delete [] suffix; delete input; delete universe; delete error; delete memory; } /* ---------------------------------------------------------------------- allocate single instance of top-level classes fundamental classes are allocated in constructor some classes have package variants ------------------------------------------------------------------------- */ void LAMMPS::create() { // Comm class must be created before Atom class // so that nthreads is defined when create_avec invokes grow() if (cuda) comm = new CommCuda(this); else if (kokkos) comm = new CommKokkos(this); else comm = new CommBrick(this); if (cuda) neighbor = new NeighborCuda(this); else if (kokkos) neighbor = new NeighborKokkos(this); else neighbor = new Neighbor(this); if (cuda) domain = new DomainCuda(this); else if (kokkos) domain = new DomainKokkos(this); #ifdef LMP_USER_OMP else domain = new DomainOMP(this); #else else domain = new Domain(this); #endif if (kokkos) atom = new AtomKokkos(this); else atom = new Atom(this); atom->create_avec("atomic",0,NULL,suffix); group = new Group(this); force = new Force(this); // must be after group, to create temperature if (cuda) modify = new ModifyCuda(this); else 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); } /* ---------------------------------------------------------------------- invoke package-specific setup commands called from LAMMPS constructor and after clear() command only invoke if suffix is set and enabled ------------------------------------------------------------------------- */ void LAMMPS::post_create() { if (suffix && suffix_enable) { if (strcmp(suffix,"gpu") == 0) input->one("package gpu force/neigh 0 0 1"); if (strcmp(suffix,"omp") == 0) input->one("package omp *"); } } /* ---------------------------------------------------------------------- initialize top-level classes do not initialize Timer class, other classes like Run() do that explicitly ------------------------------------------------------------------------- */ void LAMMPS::init() { if (cuda) cuda->accelerator(0,NULL); if (kokkos) kokkos->accelerator(0,NULL); 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; delete neighbor; delete comm; delete force; delete group; delete output; delete modify; // modify must come after output, force, update // since they delete fixes delete domain; // domain must come after modify // since fix destructors access domain delete atom; // atom must come after modify, neighbor // since fixes delete callbacks in atom delete timer; modify = NULL; // necessary since input->variable->varreader // will be destructed later } /* ---------------------------------------------------------------------- for each style, print name of all child classes built into executable ------------------------------------------------------------------------- */ void help_message(FILE *fp) { const int nmax = 500; const char *pager = NULL; const char **styles = new const char *[nmax]; if (fp == NULL) return; // if output is stdout, use pipe to pager if (fp == stdout) { pager = getenv("PAGER"); if (pager == NULL) pager = "more"; #if defined(_WIN32) fp = _popen(pager,"w"); #else fp = popen(pager,"w"); #endif // reset to original state, if pipe command fails if (fp == NULL) { fp = stdout; pager = NULL; } } // general help message about command line and flags fputs("\nLarge-scale Atomic/Molecular Massively Parallel Simulator - " LAMMPS_VERSION "-ICMS\n\n" "Usage example: lmp_g++ -v t 300 -log none -nc " "-echo screen -in in.alloy\n\n",fp); fputs("List of command line options supported by this LAMMPS executable:\n" " -cuda on/off : turn CUDA mode on or off (-c)\n" " -echo none/screen/log/both : select how to echo input (-e)\n" " -in : read input from file not stdin (-i)\n" " -help : print this help message (-h)\n" " -kokkos on/off ... : turn KOKKOS mode on or off (-k)\n" " -log none/ : where to send log output (-l)\n" " -nocite : disable writing log.cite file (-nc)\n" " -partition : assign partition sizes (-p)\n" " -plog : basename for partition logs (-pl)\n" " -pscreen : basename for partition screens (-ps)\n" " -restart : convert restart to data file (-r)\n" " -reorder : processor reordering (-ro)\n" " -screen none/ : where to send screen output (-sc)\n" " -suffix cuda/gpu/opt/omp : style suffix to apply (-sf)\n" " -var : set index style variable (-v)\n",fp); fputs("\nList of style options included in this LAMMPS executable:\n\n",fp); fputs("* Atom styles:",fp); int n=0; #define ATOM_CLASS #define AtomStyle(key,Class) if (n 80) { fprintf(fp,"\n"); pos = 0; } if (len < 16) { fprintf(fp,"%-16s",styles[i]); pos += 16; } else if (len < 32) { fprintf(fp,"%-32s",styles[i]); pos += 32; } else if (len < 48) { fprintf(fp,"%-48s",styles[i]); pos += 48; } else if (len < 64) { fprintf(fp,"%-64s",styles[i]); pos += 64; } else { fprintf(fp,"%-80s",styles[i]); pos += 80; } } }