/* ---------------------------------------------------------------------- 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: Norbert Podhorszki (ORNL) ------------------------------------------------------------------------- */ #include "dump_atom_adios.h" #include "atom.h" #include "domain.h" #include "error.h" #include "group.h" #include "memory.h" #include "universe.h" #include "update.h" #include #include "adios2.h" #include "adios_common.h" using namespace LAMMPS_NS; namespace LAMMPS_NS { class DumpAtomADIOSInternal { public: DumpAtomADIOSInternal() = default; ~DumpAtomADIOSInternal() = default; // name of adios group, referrable in adios2_config.xml const std::string ioName = "atom"; adios2::ADIOS *ad = nullptr; // adios object adios2::IO io; // adios group of variables and attributes in this dump adios2::Engine fh; // adios file/stream handle object // one ADIOS output variable we need to change every step adios2::Variable varAtoms; }; } // namespace LAMMPS_NS /* ---------------------------------------------------------------------- */ DumpAtomADIOS::DumpAtomADIOS(LAMMPS *lmp, int narg, char **arg) : DumpAtom(lmp, narg, arg) { // create a default adios2_config.xml if it doesn't exist yet. FILE *cfgfp = fopen("adios2_config.xml", "r"); if (!cfgfp) { cfgfp = fopen("adios2_config.xml", "w"); if (cfgfp) fputs(default_config, cfgfp); } if (cfgfp) fclose(cfgfp); internal = new DumpAtomADIOSInternal(); try { #if defined(MPI_STUBS) internal->ad = new adios2::ADIOS("adios2_config.xml", adios2::DebugON); #else internal->ad = new adios2::ADIOS("adios2_config.xml", world, adios2::DebugON); #endif } catch (std::ios_base::failure &e) { error->all(FLERR, "ADIOS initialization failed with error: {}", e.what()); } } /* ---------------------------------------------------------------------- */ DumpAtomADIOS::~DumpAtomADIOS() { if (internal->fh) internal->fh.Close(); delete internal->ad; delete internal; } /* ---------------------------------------------------------------------- */ void DumpAtomADIOS::openfile() { if (multifile) { // if one file per timestep, replace '*' with current timestep auto filecurrent = utils::star_subst(filename, update->ntimestep, padflag); #if defined(MPI_STUBS) internal->fh = internal->io.Open(filecurrent, adios2::Mode::Write); #else internal->fh = internal->io.Open(filecurrent, adios2::Mode::Write, world); #endif if (!internal->fh) error->one(FLERR, "Cannot open dump file {}", filecurrent); } else { if (!singlefile_opened) { #if defined(MPI_STUBS) internal->fh = internal->io.Open(filename, adios2::Mode::Write); #else internal->fh = internal->io.Open(filename, adios2::Mode::Write, world); #endif if (!internal->fh) error->one(FLERR, "Cannot open dump file {}", filename); singlefile_opened = 1; } } } /* ---------------------------------------------------------------------- */ void DumpAtomADIOS::write() { if (domain->triclinic == 0) { boxxlo = domain->boxlo[0]; boxxhi = domain->boxhi[0]; boxylo = domain->boxlo[1]; boxyhi = domain->boxhi[1]; boxzlo = domain->boxlo[2]; boxzhi = domain->boxhi[2]; } else { boxxlo = domain->boxlo_bound[0]; boxxhi = domain->boxhi_bound[0]; boxylo = domain->boxlo_bound[1]; boxyhi = domain->boxhi_bound[1]; boxzlo = domain->boxlo_bound[2]; boxzhi = domain->boxhi_bound[2]; boxxy = domain->xy; boxxz = domain->xz; boxyz = domain->yz; } // nme = # of dump lines this proc contributes to dump nme = count(); // ntotal = total # of atoms in snapshot // atomOffset = sum of # of atoms up to this proc (exclusive prefix sum) bigint bnme = nme; MPI_Allreduce(&bnme, &ntotal, 1, MPI_LMP_BIGINT, MPI_SUM, world); bigint atomOffset; // sum of all atoms on processes 0..me-1 MPI_Scan(&bnme, &atomOffset, 1, MPI_LMP_BIGINT, MPI_SUM, world); atomOffset -= nme; // exclusive prefix sum needed // Now we know the global size and the local subset size and offset // of the atoms table auto nAtomsGlobal = static_cast(ntotal); auto startRow = static_cast(atomOffset); auto nAtomsLocal = static_cast(nme); auto nColumns = static_cast(size_one); internal->varAtoms.SetShape({nAtomsGlobal, nColumns}); internal->varAtoms.SetSelection({{startRow, 0}, {nAtomsLocal, nColumns}}); // ensure buf is sized for packing // adios does not limit per-process data size so nme*size_one is not // constrained to int // if sorting on IDs also request ID list from pack() // sort buf as needed if (nme > maxbuf) { maxbuf = nme; memory->destroy(buf); memory->create(buf, (maxbuf * size_one), "dump:buf"); } if (sort_flag && sortcol == 0 && nme > maxids) { maxids = nme; memory->destroy(ids); memory->create(ids, maxids, "dump:ids"); } if (sort_flag && sortcol == 0) pack(ids); else pack(nullptr); if (sort_flag) sort(); openfile(); internal->fh.BeginStep(); // write info on data as scalars (by me==0) if (me == 0) { internal->fh.Put("ntimestep", update->ntimestep); internal->fh.Put("nprocs", nprocs); internal->fh.Put("boxxlo", boxxlo); internal->fh.Put("boxxhi", boxxhi); internal->fh.Put("boxylo", boxylo); internal->fh.Put("boxyhi", boxyhi); internal->fh.Put("boxzlo", boxzlo); internal->fh.Put("boxzhi", boxzhi); if (domain->triclinic) { internal->fh.Put("boxxy", boxxy); internal->fh.Put("boxxz", boxxz); internal->fh.Put("boxyz", boxyz); } } // Everyone needs to write scalar variables that are used as dimensions and // offsets of arrays internal->fh.Put("natoms", ntotal); internal->fh.Put("ncolumns", size_one); internal->fh.Put("nme", bnme); internal->fh.Put("offset", atomOffset); // now write the atoms internal->fh.Put(internal->varAtoms, buf); internal->fh.EndStep(); // I/O will happen now... if (multifile) internal->fh.Close(); } /* ---------------------------------------------------------------------- */ void DumpAtomADIOS::init_style() { if (image_flag == 0) size_one = 5; else size_one = 8; // setup boundary string domain->boundary_string(boundstr); // remove % from filename since ADIOS always writes a global file with // data/metadata. char *ptr = strchr(filename, '%'); if (ptr) { while (*ptr) { ptr[0] = ptr[1]; ++ptr; } } // setup column string std::vector columnNames; if (scale_flag == 0 && image_flag == 0) { columns = (char *) "id type x y z"; columnNames = {"id", "type", "x", "y", "z"}; } else if (scale_flag == 0 && image_flag == 1) { columns = (char *) "id type x y z ix iy iz"; columnNames = {"id", "type", "x", "y", "z", "ix", "iy", "iz"}; } else if (scale_flag == 1 && image_flag == 0) { columns = (char *) "id type xs ys zs"; columnNames = {"id", "type", "xs", "ys", "zs"}; } else if (scale_flag == 1 && image_flag == 1) { columns = (char *) "id type xs ys zs ix iy iz"; columnNames = {"id", "type", "xs", "ys", "zs", "ix", "iy", "iz"}; } for (int icol = 0; icol < (int) columnNames.size(); ++icol) if (keyword_user[icol].size()) columnNames[icol] = keyword_user[icol]; // setup function ptrs if (scale_flag == 1 && image_flag == 0 && domain->triclinic == 0) pack_choice = &DumpAtomADIOS::pack_scale_noimage; else if (scale_flag == 1 && image_flag == 1 && domain->triclinic == 0) pack_choice = &DumpAtomADIOS::pack_scale_image; else if (scale_flag == 1 && image_flag == 0 && domain->triclinic == 1) pack_choice = &DumpAtomADIOS::pack_scale_noimage_triclinic; else if (scale_flag == 1 && image_flag == 1 && domain->triclinic == 1) pack_choice = &DumpAtomADIOS::pack_scale_image_triclinic; else if (scale_flag == 0 && image_flag == 0) pack_choice = &DumpAtomADIOS::pack_noscale_noimage; else if (scale_flag == 0 && image_flag == 1) pack_choice = &DumpAtomADIOS::pack_noscale_image; /* Define the group of variables for the atom style here since it's a fixed set */ if (!internal->io) { internal->io = internal->ad->DeclareIO(internal->ioName); if (!internal->io.InConfigFile()) { // if not defined by user, we can change the default settings // BPFile is the default writer internal->io.SetEngine("BPFile"); int num_aggregators = multiproc; if (num_aggregators == 0) num_aggregators = 1; auto nstreams = std::to_string(num_aggregators); internal->io.SetParameters({{"substreams", nstreams}}); if (me == 0) utils::logmesg(lmp, "ADIOS method for {} is n-to-m (aggregation with {} writers)\n", filename, nstreams); } internal->io.DefineVariable("ntimestep"); internal->io.DefineVariable("natoms"); internal->io.DefineVariable("nprocs"); internal->io.DefineVariable("ncolumns"); internal->io.DefineVariable("boxxlo"); internal->io.DefineVariable("boxxhi"); internal->io.DefineVariable("boxylo"); internal->io.DefineVariable("boxyhi"); internal->io.DefineVariable("boxzlo"); internal->io.DefineVariable("boxzhi"); internal->io.DefineVariable("boxxy"); internal->io.DefineVariable("boxxz"); internal->io.DefineVariable("boxyz"); internal->io.DefineAttribute("triclinic", domain->triclinic); internal->io.DefineAttribute("scaled", scale_flag); internal->io.DefineAttribute("image", image_flag); int *boundaryptr = reinterpret_cast(domain->boundary); internal->io.DefineAttribute("boundary", boundaryptr, 6); auto nColumns = static_cast(size_one); internal->io.DefineAttribute("columns", columnNames.data(), nColumns); internal->io.DefineAttribute("columnstr", columns); internal->io.DefineAttribute("boundarystr", boundstr); internal->io.DefineAttribute("LAMMPS/dump_style", "atom"); internal->io.DefineAttribute("LAMMPS/version", lmp->version); internal->io.DefineAttribute("LAMMPS/num_ver", std::to_string(lmp->num_ver)); // local dimension variables internal->io.DefineVariable("nme", {adios2::LocalValueDim}); internal->io.DefineVariable("offset", {adios2::LocalValueDim}); // atom table size is not known at the moment // it will be correctly defined at the moment of write size_t UnknownSizeYet = 1; internal->varAtoms = internal->io.DefineVariable( "atoms", {UnknownSizeYet, nColumns}, {UnknownSizeYet, 0}, {UnknownSizeYet, nColumns}); } }