// 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. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- Contributing author: Axel Kohlmeyer (Temple U) ------------------------------------------------------------------------- */ #include "dump_molfile.h" #include "atom.h" #include "comm.h" #include "domain.h" #include "error.h" #include "group.h" #include "memory.h" #include "update.h" #include #include #include "molfile_interface.h" using namespace LAMMPS_NS; typedef MolfileInterface MFI; // syntax: // dump molfile [] // path defaults to "." -> will look for .so files in CWD. // // XXX: potential change: add more options and make them optional // path // template (import name and topology information from file) // bonds (write out bond information) // topology (write out all topology information) /* ---------------------------------------------------------------------- */ DumpMolfile::DumpMolfile(LAMMPS *lmp, int narg, char **arg) : Dump(lmp, narg, arg) { if (narg < 6) error->all(FLERR,"Illegal dump molfile command"); if (binary || compressed || multiproc) error->all(FLERR,"Invalid dump molfile filename"); // required settings sort_flag = 1; sortcol = 0; // storage for collected information size_one = 4; if (atom->molecule_flag) ++size_one; if (atom->q_flag) ++size_one; if (atom->rmass_flag) ++size_one; if (atom->radius_flag) ++size_one; need_structure = 0; unwrap_flag = 0; velocity_flag = 0; topology_flag = 0; ntotal = 0; me = comm->me; coords = vels = masses = charges = radiuses = nullptr; types = molids = nullptr; ntypes = atom->ntypes; typenames = nullptr; // allocate global array for atom coords bigint n = group->count(igroup); if (n > static_cast(MAXSMALLINT/3/sizeof(float))) error->all(FLERR,"Too many atoms for dump molfile"); if (n < 1) error->all(FLERR,"Not enough atoms for dump molfile"); natoms = static_cast(n); if (me == 0) { memory->create(types,natoms,"dump:types"); memory->create(coords,3*natoms,"dump:coords"); if (atom->molecule_flag) memory->create(molids,natoms,"dump:molids"); if (atom->q_flag) memory->create(charges,natoms,"dump:charges"); if (atom->rmass_flag) memory->create(masses,natoms,"dump:masses"); if (atom->radius_flag) memory->create(radiuses,natoms,"dump:radiuses"); mf = new MolfileInterface(arg[5],MFI::M_WRITE); const char *path = (const char *) "."; // NOLINT if (narg > 6) path=arg[6]; if (mf->find_plugin(path)!= MFI::E_MATCH) error->one(FLERR,"No suitable molfile plugin found"); if (screen) fprintf(screen,"Dump '%s' uses molfile plugin: %s\n", id, mf->get_plugin_name()); if (logfile) fprintf(logfile,"Dump '%s' uses molfile plugin: %s\n", id,mf->get_plugin_name()); } } /* ---------------------------------------------------------------------- */ DumpMolfile::~DumpMolfile() { if (me == 0) { mf->close(); memory->destroy(types); memory->destroy(coords); memory->destroy(vels); memory->destroy(masses); memory->destroy(charges); memory->destroy(radiuses); delete mf; } if (typenames) { for (int i = 1; i <= ntypes; i++) delete[] typenames[i]; delete[] typenames; typenames = nullptr; } } /* ---------------------------------------------------------------------- */ void DumpMolfile::init_style() { if (sort_flag == 0 || sortcol != 0) error->all(FLERR,"Dump molfile requires sorting by atom ID"); if (me == 0) { /* initialize typenames array to numeric types by default */ if (typenames == nullptr) { typenames = new char*[ntypes+1]; for (int itype = 1; itype <= ntypes; itype++) { /* a 32-bit int can be maximally 10 digits plus sign */ typenames[itype] = utils::strdup(std::to_string(itype)); } } // open single file, one time only if (multifile == 0) openfile(); } } /* ---------------------------------------------------------------------- */ void DumpMolfile::write() { // simulation box dimensions if (domain->triclinic == 1) { double *h = domain->h; double alen = h[0]; double blen = sqrt(h[5]*h[5] + h[1]*h[1]); double clen = sqrt(h[4]*h[4] + h[3]*h[3] + h[2]*h[2]); cell[0] = alen; cell[1] = blen; cell[2] = clen; cell[3] = (90.0 - asin((h[5]*h[4] + h[1]*h[3]) / blen/clen)); // alpha cell[4] = (90.0 - asin((h[0]*h[4]) / alen/clen)); // beta cell[5] = (90.0 - asin((h[0]*h[5]) / alen/blen)); // gamma } else { cell[0] = domain->xprd; cell[1] = domain->yprd; cell[2] = domain->zprd; cell[3] = cell[4] = cell[5] = 90.0f; } // nme = # of dump lines this proc will contribute to dump nme = count(); bigint bnme = nme; // ntotal = total # of dump lines // nmax = max # of dump lines on any proc int nmax; MPI_Allreduce(&bnme,&ntotal,1,MPI_LMP_BIGINT,MPI_SUM,world); MPI_Allreduce(&nme,&nmax,1,MPI_INT,MPI_MAX,world); // for single file output, the number of atoms must not change. if (natoms != ntotal) { if (multifile == 0) { error->all(FLERR,"Single file molfile dump needs constant #atoms"); } else { natoms = ntotal; } } ntotal = 0; reorderflag = 0; // if file per timestep, open new file if (multifile) openfile(); // insure proc 0 can receive everyone's info // limit nmax*size_one to int since used as arg in MPI_Rsend() below // pack my data into buf // if sorting on IDs also request ID list from pack() // sort buf as needed if (nmax > maxbuf) { if ((bigint) nmax * size_one > MAXSMALLINT) error->all(FLERR,"Too much per-proc info for dump"); maxbuf = nmax; memory->destroy(buf); memory->create(buf,maxbuf*size_one,"dump:buf"); } if (nmax > maxids) { maxids = nmax; memory->destroy(ids); memory->create(ids,maxids,"dump:ids"); } pack(ids); sort(); int tmp,nlines; if (me == 0) { MPI_Status status; MPI_Request request; for (int iproc = 0; iproc < nprocs; iproc++) { if (iproc) { MPI_Irecv(buf,maxbuf*size_one,MPI_DOUBLE,iproc,0,world,&request); MPI_Send(&tmp,0,MPI_INT,iproc,0,world); MPI_Wait(&request,&status); MPI_Get_count(&status,MPI_DOUBLE,&nlines); nlines /= size_one; } else nlines = nme; write_data(nlines,buf); } } else { MPI_Recv(&tmp,0,MPI_INT,0,0,world,MPI_STATUS_IGNORE); MPI_Rsend(buf,nme*size_one,MPI_DOUBLE,0,0,world); } } /* ---------------------------------------------------------------------- */ void DumpMolfile::openfile() { // single file, already opened, so just return if (singlefile_opened) return; if (multifile == 0) singlefile_opened = 1; need_structure = 1; if (me == 0) { // close open file, if needed. if (mf->is_open()) mf->close(); // if one file per timestep, replace '*' with current timestep std::string filecurrent = filename; if (multifile == 1) filecurrent = utils::star_subst(filename, update->ntimestep, padflag); if (mf->open(filecurrent.c_str(), &natoms)) error->one(FLERR,"Cannot open dump file {}: {}", filecurrent, utils::getsyserror()); } } /* ---------------------------------------------------------------------- */ void DumpMolfile::pack(tagint *ids) { int m,n; tagint *tag = atom->tag; int *type = atom->type; double **x = atom->x; imageint *image = atom->image; int *mask = atom->mask; int nlocal = atom->nlocal; m = n = 0; if (unwrap_flag) { double xprd = domain->xprd; double yprd = domain->yprd; double zprd = domain->zprd; double xy = domain->xy; double xz = domain->xz; double yz = domain->yz; for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { int ix = (image[i] & IMGMASK) - IMGMAX; int iy = (image[i] >> IMGBITS & IMGMASK) - IMGMAX; int iz = (image[i] >> IMG2BITS) - IMGMAX; buf[m++] = type[i]; if (domain->triclinic) { buf[m++] = x[i][0] + ix * xprd + iy * xy + iz * xz; buf[m++] = x[i][1] + iy * yprd + iz * yz; buf[m++] = x[i][2] + iz * zprd; } else { buf[m++] = x[i][0] + ix * xprd; buf[m++] = x[i][1] + iy * yprd; buf[m++] = x[i][2] + iz * zprd; } if (atom->molecule_flag) buf[m++] = atom->molecule[i]; if (atom->q_flag) buf[m++] = atom->q[i]; if (atom->rmass_flag) buf[m++] = atom->mass[i]; if (atom->radius_flag) buf[m++] = atom->radius[i]; ids[n++] = tag[i]; } } } else { for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) { buf[m++] = type[i]; buf[m++] = x[i][0]; buf[m++] = x[i][1]; buf[m++] = x[i][2]; if (atom->molecule_flag) buf[m++] = atom->molecule[i]; if (atom->q_flag) buf[m++] = atom->q[i]; if (atom->rmass_flag) buf[m++] = atom->mass[i]; if (atom->radius_flag) buf[m++] = atom->radius[i]; ids[n++] = tag[i]; } } } /* ---------------------------------------------------------------------- */ void DumpMolfile::write_data(int n, double *mybuf) { if (me == 0) { // copy buf atom coords into global arrays int m = 0; for (int i = 0; i < n; i++) { types[ntotal] = static_cast(mybuf[m++]); coords[3*ntotal + 0] = mybuf[m++]; coords[3*ntotal + 1] = mybuf[m++]; coords[3*ntotal + 2] = mybuf[m++]; if (atom->molecule_flag) molids[ntotal] = static_cast(mybuf[m++]); if (atom->q_flag) charges[ntotal] = mybuf[m++]; if (atom->rmass_flag) masses[ntotal] = mybuf[m++]; if (atom->radius_flag) radiuses[ntotal] = mybuf[m++]; ++ntotal; } // if last chunk of atoms in this snapshot, write global arrays to file if (ntotal == natoms) { ntotal = 0; if (need_structure) { mf->property(MFI::P_NAME,types,typenames); mf->property(MFI::P_TYPE,types,typenames); if (atom->molecule_flag) mf->property(MFI::P_RESI,molids); if (atom->rmass_flag) { mf->property(MFI::P_MASS,masses); } else { mf->property(MFI::P_MASS,types,atom->mass); } if (atom->q_flag) mf->property(MFI::P_CHRG,charges); if (atom->radius_flag) mf->property(MFI::P_RADS,radiuses); // update/write structure information in plugin mf->structure(); need_structure = 0; } double simtime = update->ntimestep * update->dt; mf->timestep(coords,nullptr,cell,&simtime); } } } /* ---------------------------------------------------------------------- */ int DumpMolfile::modify_param(int narg, char **arg) { if (strcmp(arg[0],"unwrap") == 0) { if (narg < 2) error->all(FLERR,"Illegal dump_modify command"); unwrap_flag = utils::logical(FLERR,arg[1],false,lmp); return 2; } else if (strcmp(arg[0],"element") == 0) { if (narg < ntypes+1) error->all(FLERR, "Dump modify element names do not match atom types"); if (typenames) { for (int i = 1; i <= ntypes; i++) delete[] typenames[i]; delete[] typenames; typenames = nullptr; } typenames = new char*[ntypes+1]; for (int itype = 1; itype <= ntypes; itype++) typenames[itype] = utils::strdup(arg[itype]); return ntypes+1; } return 0; } /* ---------------------------------------------------------------------- return # of bytes of allocated memory in buf and global coords array ------------------------------------------------------------------------- */ double DumpMolfile::memory_usage() { double bytes = Dump::memory_usage(); bytes += memory->usage(coords,natoms*3); bytes += (double)sizeof(MFI); return bytes; }