// clang-format off /* ---------------------------------------------------------------------- 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: Norbert Podhorszki (Oak Ridge National Laboratory) ------------------------------------------------------------------------- */ #include "reader_adios.h" #include "comm.h" #include "error.h" #include "memory.h" #include #include #include #include "adios2.h" #include "math_const.h" using namespace LAMMPS_NS; using namespace MathConst; // also in read_dump.cpp enum { ID, TYPE, X, Y, Z, VX, VY, VZ, Q, IX, IY, IZ, FX, FY, FZ }; enum { UNSET, NOSCALE_NOWRAP, NOSCALE_WRAP, SCALE_NOWRAP, SCALE_WRAP }; #define SMALL 1.0e-6 // true if the difference between two floats is "small". // cannot use fabsf() since it is not fully portable. static bool is_smalldiff(const float &val1, const float &val2) { return (fabs(static_cast(val1 - val2)) < SMALL); } namespace LAMMPS_NS { class ReadADIOSInternal { public: ReadADIOSInternal() {}; ~ReadADIOSInternal() = default; // name of adios group, referrable in adios2_config.xml const std::string ioName = "read_dump"; 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 // ADIOS input variables we need to change every step adios2::Variable varNtimestep; adios2::Variable varNatoms; adios2::Variable varAtoms; // list of column names for the atom table // (individual list of 'columns' string) std::vector columnNames; float timeout = 0.0; }; } // namespace LAMMPS_NS /* ---------------------------------------------------------------------- */ ReaderADIOS::ReaderADIOS(LAMMPS *lmp) : Reader(lmp) { fieldindex = nullptr; nAtoms = 0; nAtomsTotal = 0; atomOffset = 0; nstep = 0; nid = 0; me = comm->me; internal = new ReadADIOSInternal(); try { internal->ad = new adios2::ADIOS("adios2_config.xml", world, adios2::DebugON); } catch (std::ios_base::failure &e) { char str[256]; snprintf(str, sizeof(str), "ADIOS initialization failed with error: %s", e.what()); error->one(FLERR, str); } /* Define the group holding all variables and attributes */ internal->io = internal->ad->DeclareIO(internal->ioName); } /* ---------------------------------------------------------------------- */ ReaderADIOS::~ReaderADIOS() { if (me == 0) { memory->destroy(fieldindex); } internal->columnNames.clear(); if (internal->fh) { internal->fh.Close(); } delete internal->ad; delete internal; } /* ---------------------------------------------------------------------- pass on settings to find and load the proper plugin Called by all processors. ------------------------------------------------------------------------- */ void ReaderADIOS::settings(int narg, char **arg) { int idx = 0; while (idx < narg) { if (!strcmp(arg[idx], "timeout")) { if (idx + 1 < narg) { internal->timeout = std::stof(arg[idx + 1]); internal->io.SetParameter("OpenTimeoutSecs", arg[idx + 1]); ++idx; } else { char str[128]; snprintf(str, sizeof(str), "Missing value for 'timeout' option for ADIOS " "read_dump command"); error->one(FLERR, str); } } ++idx; } } /* ---------------------------------------------------------------------- try to open given file Every process must call this Collective operation ------------------------------------------------------------------------- */ void ReaderADIOS::open_file(const char *file) { int rv; char str[1024]; // close open file, if needed. if (internal->fh) internal->fh.Close(); try { internal->fh = internal->io.Open(file, adios2::Mode::Read, world); } catch (std::ios_base::failure &e) { char str[256]; snprintf(str, sizeof(str), "%s", e.what()); error->one(FLERR, str); } if (!internal->fh) { snprintf(str, sizeof(str), "Cannot open file %s using ADIOS", file); error->one(FLERR, str); } } /* ---------------------------------------------------------------------- close current file Every process must call this Collective operation ------------------------------------------------------------------------- */ void ReaderADIOS::close_file() { // close open file, if needed. if (internal->fh) { internal->fh.Close(); } } /* ---------------------------------------------------------------------- read and return time stamp from dump file if first read reaches end-of-file, return 1 so caller can open next file Called by all processors. ------------------------------------------------------------------------- */ int ReaderADIOS::read_time(bigint &ntimestep) { char str[1024]; adios2::StepStatus status = internal->fh.BeginStep(adios2::StepMode::Read, internal->timeout); switch (status) { case adios2::StepStatus::EndOfStream: case adios2::StepStatus::NotReady: case adios2::StepStatus::OtherError: return 1; default: break; } internal->varNtimestep = internal->io.InquireVariable("ntimestep"); if (!internal->varNtimestep) { snprintf(str, sizeof(str), "Did not find 'ntimestep' variable in ADIOS file %s", internal->fh.Name().c_str()); error->one(FLERR, str); } ntimestep = static_cast(internal->varNtimestep.Max()); // std::cerr << " **** ReaderADIOS::read_time found step " << ntimestep // << " **** " << std::endl; return 0; } /* ---------------------------------------------------------------------- skip snapshot for given timestamp Called by all processors. ------------------------------------------------------------------------- */ void ReaderADIOS::skip() { internal->fh.EndStep(); } /* ---------------------------------------------------------------------- read remaining header info: return natoms box bounds, triclinic (inferred), fieldflag (1 if any fields not found), xyz flags = from input scaleflag & wrapflag if fieldflag set: match Nfield fields to per-atom column labels allocate and set fieldindex = which column each field maps to fieldtype = X,VX,IZ etc fieldlabel = user-specified label or nullptr if use fieldtype default xyz flag = scaledflag if has fieldlabel name, else set by x,xs,xu,xsu only called by proc 0 ------------------------------------------------------------------------- */ bigint ReaderADIOS::read_header(double box[3][3], int &boxinfo, int &triclinic, int fieldinfo, int nfield, int *fieldtype, char **fieldlabel, int scaleflag, int wrapflag, int &fieldflag, int &xflag, int &yflag, int &zflag) { char str[1024]; nid = 0; // signal that we have no box info at all so far. internal->varNatoms = internal->io.InquireVariable("natoms"); if (!internal->varNatoms) { snprintf(str, sizeof(str), "Did not find 'natoms' variable in ADIOS file %s", internal->fh.Name().c_str()); error->one(FLERR, str); } /* nAtoms */ nAtomsTotal = internal->varNatoms.Max(); uint64_t rem = nAtomsTotal % comm->nprocs; nAtoms = nAtomsTotal / comm->nprocs; atomOffset = comm->me * nAtoms; if (comm->me < rem) { ++nAtoms; atomOffset += comm->me; } else { atomOffset += rem; } /* triclinic */ adios2::Attribute attTriclinic = internal->io.InquireAttribute("triclinic"); if (!attTriclinic) { snprintf(str, sizeof(str), "Did not find 'triclinic' attribute in ADIOS file %s", internal->fh.Name().c_str()); error->one(FLERR, str); } triclinic = attTriclinic.Data()[0]; /* read Box */ adios2::Variable varBoxxlo = internal->io.InquireVariable("boxxlo"); adios2::Variable varBoxxhi = internal->io.InquireVariable("boxxhi"); adios2::Variable varBoxylo = internal->io.InquireVariable("boxylo"); adios2::Variable varBoxyhi = internal->io.InquireVariable("boxyhi"); adios2::Variable varBoxzlo = internal->io.InquireVariable("boxzlo"); adios2::Variable varBoxzhi = internal->io.InquireVariable("boxzhi"); box[0][0] = varBoxxlo.Max(); box[0][1] = varBoxxhi.Max(); box[0][2] = 0.0; box[1][0] = varBoxylo.Max(); box[1][1] = varBoxyhi.Max(); box[1][2] = 0.0; box[2][0] = varBoxzlo.Max(); box[2][1] = varBoxzhi.Max(); box[2][2] = 0.0; if (triclinic) { adios2::Variable varBoxxy = internal->io.InquireVariable("boxxy"); adios2::Variable varBoxxz = internal->io.InquireVariable("boxxz"); adios2::Variable varBoxyz = internal->io.InquireVariable("boxyz"); box[0][2] = varBoxxy.Max(); box[1][2] = varBoxxz.Max(); box[2][2] = varBoxyz.Max(); } boxinfo = 1; // if no field info requested, just return if (!fieldinfo) return nAtoms; memory->create(fieldindex, nfield, "read_dump:fieldindex"); /* Columns */ adios2::Attribute attColumns = internal->io.InquireAttribute("columns"); std::vector labelVector = attColumns.Data(); int nwords = labelVector.size(); std::map labels; for (int i = 0; i < nwords; ++i) { labels.emplace(labelVector[i], i); } int s_index, u_index, su_index; xflag = UNSET; yflag = UNSET; zflag = UNSET; // copy fieldtype list for supported fields for (int i = 0; i < nfield; i++) { if (fieldlabel[i]) { fieldindex[i] = find_label(fieldlabel[i], labels); if (fieldtype[i] == X) xflag = 2 * scaleflag + wrapflag + 1; else if (fieldtype[i] == Y) yflag = 2 * scaleflag + wrapflag + 1; else if (fieldtype[i] == Z) zflag = 2 * scaleflag + wrapflag + 1; } else if (fieldtype[i] == ID) fieldindex[i] = find_label("id", labels); else if (fieldtype[i] == TYPE) fieldindex[i] = find_label("type", labels); else if (fieldtype[i] == X) { fieldindex[i] = find_label("x", labels); xflag = NOSCALE_WRAP; if (fieldindex[i] < 0) { fieldindex[i] = nwords; s_index = find_label("xs", labels); u_index = find_label("xu", labels); su_index = find_label("xsu", labels); if (s_index >= 0 && s_index < fieldindex[i]) { fieldindex[i] = s_index; xflag = SCALE_WRAP; } if (u_index >= 0 && u_index < fieldindex[i]) { fieldindex[i] = u_index; xflag = NOSCALE_NOWRAP; } if (su_index >= 0 && su_index < fieldindex[i]) { fieldindex[i] = su_index; xflag = SCALE_NOWRAP; } } if (fieldindex[i] == nwords) fieldindex[i] = -1; } else if (fieldtype[i] == Y) { fieldindex[i] = find_label("y", labels); yflag = NOSCALE_WRAP; if (fieldindex[i] < 0) { fieldindex[i] = nwords; s_index = find_label("ys", labels); u_index = find_label("yu", labels); su_index = find_label("ysu", labels); if (s_index >= 0 && s_index < fieldindex[i]) { fieldindex[i] = s_index; yflag = SCALE_WRAP; } if (u_index >= 0 && u_index < fieldindex[i]) { fieldindex[i] = u_index; yflag = NOSCALE_NOWRAP; } if (su_index >= 0 && su_index < fieldindex[i]) { fieldindex[i] = su_index; yflag = SCALE_NOWRAP; } } if (fieldindex[i] == nwords) fieldindex[i] = -1; } else if (fieldtype[i] == Z) { fieldindex[i] = find_label("z", labels); zflag = NOSCALE_WRAP; if (fieldindex[i] < 0) { fieldindex[i] = nwords; s_index = find_label("zs", labels); u_index = find_label("zu", labels); su_index = find_label("zsu", labels); if (s_index >= 0 && s_index < fieldindex[i]) { fieldindex[i] = s_index; zflag = SCALE_WRAP; } if (u_index >= 0 && u_index < fieldindex[i]) { fieldindex[i] = u_index; zflag = NOSCALE_NOWRAP; } if (su_index >= 0 && su_index < fieldindex[i]) { fieldindex[i] = su_index; zflag = SCALE_NOWRAP; } } if (fieldindex[i] == nwords) fieldindex[i] = -1; } else if (fieldtype[i] == VX) fieldindex[i] = find_label("vx", labels); else if (fieldtype[i] == VY) fieldindex[i] = find_label("vy", labels); else if (fieldtype[i] == VZ) fieldindex[i] = find_label("vz", labels); else if (fieldtype[i] == FX) fieldindex[i] = find_label("fx", labels); else if (fieldtype[i] == FY) fieldindex[i] = find_label("fy", labels); else if (fieldtype[i] == FZ) fieldindex[i] = find_label("fz", labels); else if (fieldtype[i] == Q) fieldindex[i] = find_label("q", labels); else if (fieldtype[i] == IX) fieldindex[i] = find_label("ix", labels); else if (fieldtype[i] == IY) fieldindex[i] = find_label("iy", labels); else if (fieldtype[i] == IZ) fieldindex[i] = find_label("iz", labels); } // set fieldflag = -1 if any unfound fields fieldflag = 0; for (int i = 0; i < nfield; i++) if (fieldindex[i] < 0) fieldflag = -1; return nAtoms; } /* ---------------------------------------------------------------------- read N atom lines from dump file stores appropriate values in fields array return 0 if success, 1 if error only called by proc 0 ------------------------------------------------------------------------- */ void ReaderADIOS::read_atoms(int n, int nfield, double **fields) { char str[1024]; /* Read Atoms */ /* This is the firsts (and last) read of array data, so we * call EndStep() here instead of PerformGets() */ adios2::Variable varAtoms = internal->io.InquireVariable("atoms"); if (n != nAtoms) { snprintf( str, sizeof(str), "ReaderADIOS::read_atoms() expects 'n=%d' equal to the number of " "atoms (=%" PRIu64 ") for process %d in ADIOS file %s.", n, nAtoms, comm->me, internal->fh.Name().c_str()); error->one(FLERR, str); } size_t ncols = varAtoms.Count()[1]; varAtoms.SetSelection({{atomOffset, 0}, {nAtoms, ncols}}); std::vector table; internal->fh.Get(varAtoms, table); // EndStep or PerformGets required to make the read happen internal->fh.EndStep(); size_t idx; for (int i = 0; i < nAtoms; i++) { idx = i * ncols; for (int m = 0; m < nfield; m++) { fields[i][m] = table[idx + fieldindex[m]]; } } } /* ---------------------------------------------------------------------- match label to any of N labels return index of match or -1 if no match ------------------------------------------------------------------------- */ int ReaderADIOS::find_label(const std::string &label, const std::map &labels) { std::map::const_iterator it = labels.find(label); if (it != labels.end()) { return it->second; } return -1; }