update and improve ADIOS support

- modernize code
- remove dead code and unused definitions, enums, and includes
- create default adios2_config.xml file if it doesn't exist
- enable and apply clang-format
- update documentation
This commit is contained in:
Axel Kohlmeyer
2022-03-29 00:31:12 -04:00
parent 333e3b0491
commit b211f97efa
6 changed files with 787 additions and 924 deletions

View File

@ -1,4 +1,3 @@
// clang-format off
/* ----------------------------------------------------------------------
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
https://www.lammps.org/, Sandia National Laboratories
@ -17,152 +16,77 @@
------------------------------------------------------------------------- */
#include "dump_custom_adios.h"
#include "atom.h"
#include "compute.h"
#include "domain.h"
#include "error.h"
#include "fix.h"
#include "force.h"
#include "group.h"
#include "input.h"
#include "memory.h"
#include "modify.h"
#include "region.h"
#include "universe.h"
#include "update.h"
#include "variable.h"
#include <cmath>
#include <cstring>
#include "adios2.h"
#include "adios_common.h"
using namespace LAMMPS_NS;
#define MAX_TEXT_HEADER_SIZE 4096
#define DUMP_BUF_CHUNK_SIZE 16384
#define DUMP_BUF_INCREMENT_SIZE 4096
namespace LAMMPS_NS {
class DumpCustomADIOSInternal {
enum {
ID,
MOL,
TYPE,
ELEMENT,
MASS,
X,
Y,
Z,
XS,
YS,
ZS,
XSTRI,
YSTRI,
ZSTRI,
XU,
YU,
ZU,
XUTRI,
YUTRI,
ZUTRI,
XSU,
YSU,
ZSU,
XSUTRI,
YSUTRI,
ZSUTRI,
IX,
IY,
IZ,
VX,
VY,
VZ,
FX,
FY,
FZ,
Q,
MUX,
MUY,
MUZ,
MU,
RADIUS,
DIAMETER,
OMEGAX,
OMEGAY,
OMEGAZ,
ANGMOMX,
ANGMOMY,
ANGMOMZ,
TQX,
TQY,
TQZ,
SPIN,
ERADIUS,
ERVEL,
ERFORCE,
COMPUTE,
FIX,
VARIABLE
public:
DumpCustomADIOSInternal(){};
~DumpCustomADIOSInternal() = default;
// name of adios group, referrable in adios2_config.xml
const std::string ioName = "custom";
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<double> varAtoms;
// list of column names for the atom table
// (individual list of 'columns' string)
std::vector<std::string> columnNames;
};
enum { LT, LE, GT, GE, EQ, NEQ };
enum { INT, DOUBLE, STRING, BIGINT }; // same as in DumpCustom
namespace LAMMPS_NS
{
class DumpCustomADIOSInternal
{
public:
DumpCustomADIOSInternal() {};
~DumpCustomADIOSInternal() = default;
// name of adios group, referrable in adios2_config.xml
const std::string ioName = "custom";
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<double> varAtoms;
// list of column names for the atom table
// (individual list of 'columns' string)
std::vector<std::string> columnNames;
};
} // namespace LAMMPS_NS
} // namespace LAMMPS_NS
/* ---------------------------------------------------------------------- */
DumpCustomADIOS::DumpCustomADIOS(LAMMPS *lmp, int narg, char **arg)
: DumpCustom(lmp, narg, arg)
DumpCustomADIOS::DumpCustomADIOS(LAMMPS *lmp, int narg, char **arg) : DumpCustom(lmp, narg, arg)
{
internal = new DumpCustomADIOSInternal();
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);
}
// 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);
// if (screen) fprintf(screen, "DumpCustomADIOS constructor: nvariable=%d
// id_variable=%p, variables=%p, nfield=%d, earg=%p\n", nvariable,
// id_variable, variable, nfield, earg);
internal->columnNames.reserve(nfield);
for (int i = 0; i < nfield; ++i) {
internal->columnNames.push_back(earg[i]);
// if (screen) fprintf(screen, "earg[%d] = '%s'\n", i, earg[i]);
}
internal = new DumpCustomADIOSInternal();
try {
internal->ad = new adios2::ADIOS("adios2_config.xml", world, adios2::DebugON);
} catch (std::ios_base::failure &e) {
error->all(FLERR, "ADIOS initialization failed with error: {}", e.what());
}
internal->columnNames.reserve(nfield);
for (int i = 0; i < nfield; ++i) { internal->columnNames.push_back(earg[i]); }
}
/* ---------------------------------------------------------------------- */
DumpCustomADIOS::~DumpCustomADIOS()
{
internal->columnNames.clear();
if (internal->fh) {
internal->fh.Close();
}
delete internal->ad;
delete internal;
internal->columnNames.clear();
if (internal->fh) { internal->fh.Close(); }
delete internal->ad;
delete internal;
}
/* ---------------------------------------------------------------------- */
@ -211,233 +135,214 @@ void DumpCustomADIOS::openfile()
void DumpCustomADIOS::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;
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
size_t nAtomsGlobal = static_cast<size_t>(ntotal);
size_t startRow = static_cast<size_t>(atomOffset);
size_t nAtomsLocal = static_cast<size_t>(nme);
size_t nColumns = static_cast<size_t>(size_one);
internal->varAtoms.SetShape({nAtomsGlobal, nColumns});
internal->varAtoms.SetSelection({{startRow, 0}, {nAtomsLocal, nColumns}});
// insure filewriter proc 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 (nme > maxbuf) {
if ((bigint) nme * size_one > MAXSMALLINT) error->all(FLERR, "Too much per-proc info for dump");
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<uint64_t>("ntimestep", update->ntimestep);
internal->fh.Put<int>("nprocs", nprocs);
internal->fh.Put<double>("boxxlo", boxxlo);
internal->fh.Put<double>("boxxhi", boxxhi);
internal->fh.Put<double>("boxylo", boxylo);
internal->fh.Put<double>("boxyhi", boxyhi);
internal->fh.Put<double>("boxzlo", boxzlo);
internal->fh.Put<double>("boxzhi", boxzhi);
if (domain->triclinic) {
internal->fh.Put<double>("boxxy", boxxy);
internal->fh.Put<double>("boxxz", boxxz);
internal->fh.Put<double>("boxyz", boxyz);
}
}
// Everyone needs to write scalar variables that are used as dimensions and
// offsets of arrays
internal->fh.Put<uint64_t>("natoms", ntotal);
internal->fh.Put<int>("ncolumns", size_one);
internal->fh.Put<uint64_t>("nme", bnme);
internal->fh.Put<uint64_t>("offset", atomOffset);
// now write the atoms
internal->fh.Put<double>("atoms", buf);
internal->fh.EndStep(); // I/O will happen now...
// 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
size_t nAtomsGlobal = static_cast<size_t>(ntotal);
size_t startRow = static_cast<size_t>(atomOffset);
size_t nAtomsLocal = static_cast<size_t>(nme);
size_t nColumns = static_cast<size_t>(size_one);
internal->varAtoms.SetShape({nAtomsGlobal, nColumns});
internal->varAtoms.SetSelection({{startRow, 0}, {nAtomsLocal, nColumns}});
// insure filewriter proc 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 (nme > maxbuf) {
if ((bigint)nme * size_one > MAXSMALLINT)
error->all(FLERR, "Too much per-proc info for dump");
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<uint64_t>("ntimestep", update->ntimestep);
internal->fh.Put<int>("nprocs", nprocs);
internal->fh.Put<double>("boxxlo", boxxlo);
internal->fh.Put<double>("boxxhi", boxxhi);
internal->fh.Put<double>("boxylo", boxylo);
internal->fh.Put<double>("boxyhi", boxyhi);
internal->fh.Put<double>("boxzlo", boxzlo);
internal->fh.Put<double>("boxzhi", boxzhi);
if (domain->triclinic) {
internal->fh.Put<double>("boxxy", boxxy);
internal->fh.Put<double>("boxxz", boxxz);
internal->fh.Put<double>("boxyz", boxyz);
}
}
// Everyone needs to write scalar variables that are used as dimensions and
// offsets of arrays
internal->fh.Put<uint64_t>("natoms", ntotal);
internal->fh.Put<int>("ncolumns", size_one);
internal->fh.Put<uint64_t>("nme", bnme);
internal->fh.Put<uint64_t>("offset", atomOffset);
// now write the atoms
internal->fh.Put<double>("atoms", buf);
internal->fh.EndStep(); // I/O will happen now...
if (multifile) {
internal->fh.Close();
}
if (multifile) { internal->fh.Close(); }
}
/* ---------------------------------------------------------------------- */
void DumpCustomADIOS::init_style()
{
// setup boundary string
// setup boundary string
domain->boundary_string(boundstr);
domain->boundary_string(boundstr);
// remove % from filename since ADIOS always writes a global file with
// data/metadata
int len = strlen(filename);
char *ptr = strchr(filename, '%');
if (ptr) {
*ptr = '\0';
char *s = new char[len - 1];
sprintf(s, "%s%s", filename, ptr + 1);
strncpy(filename, s, len);
// 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;
}
}
/* The next four loops are copied from dump_custom_mpiio, but nothing is
* done with them.
* It is unclear why we need them here.
* For metadata, variable[] will be written out as an ADIOS attribute if
* nvariable>0
*/
// find current ptr for each compute,fix,variable
// check that fix frequency is acceptable
int icompute;
for (int i = 0; i < ncompute; i++) {
icompute = modify->find_compute(id_compute[i]);
if (icompute < 0)
error->all(FLERR, "Could not find dump custom compute ID");
compute[i] = modify->compute[icompute];
}
/* The next four loops are copied from dump_custom_mpiio, but nothing is
* done with them.
* It is unclear why we need them here.
* For metadata, variable[] will be written out as an ADIOS attribute if
* nvariable>0
*/
// find current ptr for each compute,fix,variable
// check that fix frequency is acceptable
int icompute;
for (int i = 0; i < ncompute; i++) {
icompute = modify->find_compute(id_compute[i]);
if (icompute < 0) error->all(FLERR, "Could not find dump custom compute ID");
compute[i] = modify->compute[icompute];
}
int ifix;
for (int i = 0; i < nfix; i++) {
ifix = modify->find_fix(id_fix[i]);
if (ifix < 0)
error->all(FLERR, "Could not find dump custom fix ID");
fix[i] = modify->fix[ifix];
if (nevery % modify->fix[ifix]->peratom_freq)
error->all(FLERR,
"Dump custom and fix not computed at compatible times");
}
int ifix;
for (int i = 0; i < nfix; i++) {
ifix = modify->find_fix(id_fix[i]);
if (ifix < 0) error->all(FLERR, "Could not find dump custom fix ID");
fix[i] = modify->fix[ifix];
if (nevery % modify->fix[ifix]->peratom_freq)
error->all(FLERR, "Dump custom and fix not computed at compatible times");
}
int ivariable;
for (int i = 0; i < nvariable; i++) {
ivariable = input->variable->find(id_variable[i]);
if (ivariable < 0)
error->all(FLERR, "Could not find dump custom variable name");
variable[i] = ivariable;
}
int ivariable;
for (int i = 0; i < nvariable; i++) {
ivariable = input->variable->find(id_variable[i]);
if (ivariable < 0) error->all(FLERR, "Could not find dump custom variable name");
variable[i] = ivariable;
}
// set index and check validity of region
if (iregion >= 0) {
iregion = domain->find_region(idregion);
if (iregion == -1)
error->all(FLERR, "Region ID for dump custom does not exist");
}
// set index and check validity of region
if (iregion >= 0) {
iregion = domain->find_region(idregion);
if (iregion == -1) error->all(FLERR, "Region ID for dump custom does not exist");
}
/* Define the group of variables for the atom style here since it's a fixed
* set */
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;
char nstreams[128];
sprintf(nstreams, "%d", num_aggregators);
internal->io.SetParameters({{"substreams", nstreams}});
if (me == 0 && screen)
fprintf(
screen,
"ADIOS method for %s is n-to-m (aggregation with %s writers)\n",
filename, nstreams);
}
/* Define the group of variables for the atom style here since it's a fixed
* set */
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<uint64_t>("ntimestep");
internal->io.DefineVariable<uint64_t>("natoms");
internal->io.DefineVariable<uint64_t>("ntimestep");
internal->io.DefineVariable<uint64_t>("natoms");
internal->io.DefineVariable<int>("nprocs");
internal->io.DefineVariable<int>("ncolumns");
internal->io.DefineVariable<int>("nprocs");
internal->io.DefineVariable<int>("ncolumns");
internal->io.DefineVariable<double>("boxxlo");
internal->io.DefineVariable<double>("boxxhi");
internal->io.DefineVariable<double>("boxylo");
internal->io.DefineVariable<double>("boxyhi");
internal->io.DefineVariable<double>("boxzlo");
internal->io.DefineVariable<double>("boxzhi");
internal->io.DefineVariable<double>("boxxlo");
internal->io.DefineVariable<double>("boxxhi");
internal->io.DefineVariable<double>("boxylo");
internal->io.DefineVariable<double>("boxyhi");
internal->io.DefineVariable<double>("boxzlo");
internal->io.DefineVariable<double>("boxzhi");
internal->io.DefineVariable<double>("boxxy");
internal->io.DefineVariable<double>("boxxz");
internal->io.DefineVariable<double>("boxyz");
internal->io.DefineVariable<double>("boxxy");
internal->io.DefineVariable<double>("boxxz");
internal->io.DefineVariable<double>("boxyz");
internal->io.DefineAttribute<int>("triclinic", domain->triclinic);
internal->io.DefineAttribute<int>("triclinic", domain->triclinic);
int *boundaryptr = reinterpret_cast<int *>(domain->boundary);
internal->io.DefineAttribute<int>("boundary", boundaryptr, 6);
int *boundaryptr = reinterpret_cast<int *>(domain->boundary);
internal->io.DefineAttribute<int>("boundary", boundaryptr, 6);
size_t nColumns = static_cast<size_t>(size_one);
internal->io.DefineAttribute<std::string>(
"columns", internal->columnNames.data(), nColumns);
internal->io.DefineAttribute<std::string>("columnstr", columns);
internal->io.DefineAttribute<std::string>("boundarystr", boundstr);
internal->io.DefineAttribute<std::string>("LAMMPS/dump_style", "custom");
internal->io.DefineAttribute<std::string>("LAMMPS/version",
lmp->version);
internal->io.DefineAttribute<std::string>("LAMMPS/num_ver",
std::to_string(lmp->num_ver));
size_t nColumns = static_cast<size_t>(size_one);
internal->io.DefineAttribute<std::string>("columns", internal->columnNames.data(), nColumns);
internal->io.DefineAttribute<std::string>("columnstr", columns);
internal->io.DefineAttribute<std::string>("boundarystr", boundstr);
internal->io.DefineAttribute<std::string>("LAMMPS/dump_style", "custom");
internal->io.DefineAttribute<std::string>("LAMMPS/version", lmp->version);
internal->io.DefineAttribute<std::string>("LAMMPS/num_ver", std::to_string(lmp->num_ver));
internal->io.DefineVariable<uint64_t>(
"nme", {adios2::LocalValueDim}); // local dimension variable
internal->io.DefineVariable<uint64_t>(
"offset", {adios2::LocalValueDim}); // local dimension variable
internal->io.DefineVariable<uint64_t>("nme",
{adios2::LocalValueDim}); // local dimension variable
internal->io.DefineVariable<uint64_t>("offset",
{adios2::LocalValueDim}); // local dimension variable
// 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<double>(
"atoms", {UnknownSizeYet, nColumns}, {UnknownSizeYet, 0},
{UnknownSizeYet, nColumns});
// 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<double>(
"atoms", {UnknownSizeYet, nColumns}, {UnknownSizeYet, 0}, {UnknownSizeYet, nColumns});
}