28#if !defined(EONMPI) && !defined(IS_WINDOWS)
54 lammpsThr{p.potential_options().LAMMPSThreads},
58 mpiComm{
eonc::getMpiClientComm(p)}
64 static std::atomic<int> ids{0};
69#if !defined(EONMPI) && !defined(IS_WINDOWS)
70 if (!isolate_worker) {
88 std::unique_lock<std::mutex> lock(
maskMutex_, std::defer_lock);
92 if (nAtoms <= 0 || isFixed ==
nullptr) {
97 fixedMask_.assign(isFixed, isFixed + 3 * nAtoms);
102 std::vector<double> mask;
105 std::unique_lock<std::mutex> lock(
maskMutex_, std::defer_lock);
114 static constexpr const char *kUnfix[] = {
"unfix eon_fx",
"unfix eon_fy",
115 "unfix eon_fz",
"unfix eon_freeze"};
116 static constexpr const char *kUngroup[] = {
117 "group eon_fx delete",
"group eon_fy delete",
"group eon_fz delete",
118 "group eon_frozen delete"};
119 for (
const char *cmd : kUnfix) {
125 for (
const char *cmd : kUngroup) {
134 for (
long i = 0; i < N; ++i) {
135 for (
int ax = 0; ax < 3; ++ax) {
136 if (mask[
static_cast<size_t>(3 * i + ax)] >= 0.5) {
137 ids[ax] += std::format(
"{} ", i + 1);
141 static constexpr const char *kGroup[] = {
"eon_fx",
"eon_fy",
"eon_fz"};
142 static constexpr const char *kFix[] = {
143 "fix eon_fx eon_fx setforce 0.0 NULL NULL",
144 "fix eon_fy eon_fy setforce NULL 0.0 NULL",
145 "fix eon_fz eon_fz setforce NULL NULL 0.0"};
146 for (
int ax = 0; ax < 3; ++ax) {
147 if (ids[ax].empty()) {
151 (
"group " + std::string(kGroup[ax]) +
" id " + ids[ax]).c_str());
157#if !defined(EONMPI) && !defined(IS_WINDOWS)
167#if !defined(EONMPI) && !defined(IS_WINDOWS)
184void rejectGeometry(
double *U,
double *F,
long N) {
194 for (
long i = 0; i < 3 * N; ++i) {
197 for (
long i = 0; i < N; ++i) {
198 F[3 * i] = (i % 2 == 0) ? 1.0 : -1.0;
204bool readExact(
int fd,
void *buf,
size_t n) {
205 auto *p =
static_cast<char *
>(buf);
207 ssize_t r = read(fd, p, n);
209 if (r < 0 && errno == EINTR)
214 n -=
static_cast<size_t>(r);
218bool writeExact(
int fd,
const void *buf,
size_t n) {
219 const auto *p =
static_cast<const char *
>(buf);
221 ssize_t w = write(fd, p, n);
228 n -=
static_cast<size_t>(w);
240 if (pipe(reqPipe) != 0) {
241 throw std::runtime_error(
"LAMMPSPot: failed to create worker pipes");
243 if (pipe(resPipe) != 0) {
246 throw std::runtime_error(
"LAMMPSPot: failed to create worker pipes");
258 throw std::runtime_error(
"LAMMPSPot: fork for worker failed");
286 std::signal(SIGPIPE, SIG_IGN);
301 if (!readExact(
reqFd, &N,
sizeof(N))) {
307 std::vector<int> atomicNrs(
static_cast<size_t>(N));
308 std::vector<double> R(
static_cast<size_t>(3 * N));
310 if (!readExact(
reqFd, atomicNrs.data(),
311 sizeof(
int) *
static_cast<size_t>(N)) ||
312 !readExact(
reqFd, box,
sizeof(box)) ||
313 !readExact(
reqFd, R.data(),
314 sizeof(
double) *
static_cast<size_t>(3 * N))) {
317 std::vector<double> mask(
static_cast<size_t>(3 * N), 0.0);
318 if (!readExact(
reqFd, mask.data(),
319 sizeof(
double) *
static_cast<size_t>(3 * N))) {
324 std::vector<double> F(
static_cast<size_t>(3 * N), 0.0);
328 forceLocal(N, R.data(), atomicNrs.data(), F.data(), &U, box);
333 double stressRaw[9] = {};
335 for (
int i = 0; i < 9; ++i) {
336 stressRaw[i] =
stress_(i / 3, i % 3);
340 if (!writeExact(
resFd, &status,
sizeof(status)) ||
341 !writeExact(
resFd, &U,
sizeof(U)) ||
342 !writeExact(
resFd, F.data(),
343 sizeof(
double) *
static_cast<size_t>(3 * N)) ||
344 !writeExact(
resFd, stressRaw,
sizeof(stressRaw))) {
358 writeExact(
reqFd, &sentinel,
sizeof(sentinel));
373 for (
int i = 0; i < 100; ++i) {
374 pid_t r = waitpid(
workerPid, &st, WNOHANG);
385 if (r ==
workerPid || (r < 0 && errno != EINTR)) {
398 for (
int i = 0; i < 9; ++i) {
413 double *U,
double *variance,
const double *box) {
417 std::lock_guard<std::mutex> stateLock(
workerMutex);
419#elif defined(IS_WINDOWS)
421 std::lock_guard<std::mutex> stateLock(
workerMutex);
427 std::lock_guard<std::mutex> workerLock(
workerMutex);
429 rejectGeometry(U, F, N);
436 std::vector<double> mask(
static_cast<size_t>(3 * N), 0.0);
443 if (!writeExact(
reqFd, &N,
sizeof(N)) ||
444 !writeExact(
reqFd, atomicNrs,
sizeof(
int) *
static_cast<size_t>(N)) ||
445 !writeExact(
reqFd, box,
sizeof(
double) * 9) ||
446 !writeExact(
reqFd, R,
sizeof(
double) *
static_cast<size_t>(3 * N)) ||
447 !writeExact(
reqFd, mask.data(),
448 sizeof(
double) *
static_cast<size_t>(3 * N))) {
457 rejectGeometry(U, F, N);
472 int pr = poll(&pfd, 1, 90000);
479 "[LAMMPSPot] worker force eval timed out; {} respawns left "
483 rejectGeometry(U, F, N);
488 double stressRaw[9] = {};
489 if (!readExact(
resFd, &status,
sizeof(status)) ||
490 !readExact(
resFd, U,
sizeof(
double)) ||
491 !readExact(
resFd, F,
sizeof(
double) *
static_cast<size_t>(3 * N)) ||
492 !readExact(
resFd, stressRaw,
sizeof(stressRaw))) {
495 "[LAMMPSPot] worker died during force eval; {} respawns left "
499 rejectGeometry(U, F, N);
506 for (
int row = 0; row < 3; ++row) {
507 for (
int col = 0; col < 3; ++col) {
508 stress_(row, col) = stressRaw[row * 3 + col];
516 "[LAMMPSPot] worker reported an evaluation error; {} respawns left "
520 rejectGeometry(U, F, N);
542 bool nonfinite = !std::isfinite(*U);
543 for (
long i = 0; i < 3 * N && !nonfinite; ++i) {
544 nonfinite = !std::isfinite(F[i]);
548 "geometry (eon-7416)");
549 rejectGeometry(U, F, N);
555 double *F,
double *U,
const double *box) {
565 bool newLammps =
false;
566 for (
int i = 0; i < 9; i++) {
576 throw std::runtime_error(
"Should have a LAMMPS instance by now");
579 lmp.scatter_atoms(
LAMMPSObj,
"x", 1, 3,
const_cast<double *
>(R));
590 static_cast<double *
>(lmp.extract_variable(
LAMMPSObj,
"pe",
nullptr));
592 static_cast<double *
>(lmp.extract_variable(
LAMMPSObj,
"fx",
"all"));
594 static_cast<double *
>(lmp.extract_variable(
LAMMPSObj,
"fy",
"all"));
596 static_cast<double *
>(lmp.extract_variable(
LAMMPSObj,
"fz",
"all"));
597 if (!pe || !fx || !fy || !fz) {
606 throw std::runtime_error(
607 "LAMMPS: extract_variable returned null (pe/fx/fy/fz)");
612 for (
long i = 0; i < N; i++) {
613 F[3 * i + 0] = fx[i];
614 F[3 * i + 1] = fy[i];
615 F[3 * i + 2] = fz[i];
620 constexpr double kcalPerEv = 23.0609;
622 for (
long i = 0; i < 3 * N; i++) {
634 auto *sxx =
static_cast<double *
>(
635 lmp.extract_variable(
LAMMPSObj,
"eon_sxx",
nullptr));
636 auto *syy =
static_cast<double *
>(
637 lmp.extract_variable(
LAMMPSObj,
"eon_syy",
nullptr));
638 auto *szz =
static_cast<double *
>(
639 lmp.extract_variable(
LAMMPSObj,
"eon_szz",
nullptr));
640 auto *sxy =
static_cast<double *
>(
641 lmp.extract_variable(
LAMMPSObj,
"eon_sxy",
nullptr));
642 auto *sxz =
static_cast<double *
>(
643 lmp.extract_variable(
LAMMPSObj,
"eon_sxz",
nullptr));
644 auto *syz =
static_cast<double *
>(
645 lmp.extract_variable(
LAMMPSObj,
"eon_syz",
nullptr));
646 const bool got = sxx && syy && szz && sxy && sxz && syz;
647 auto release = [&]() {
666 constexpr double barPerEvA3 = 1.6021766208e6;
667 constexpr double barPerAtm = 1.01325;
669 realunits ? -(barPerAtm / barPerEvA3) : -(1.0 / barPerEvA3);
698 const auto fileSize =
static_cast<std::int64_t
>(sz);
708 while (std::getline(in, line)) {
709 if (!line.empty() && line.back() ==
'\r') {
717 in.seekg(0, std::ios::end);
718 const auto end = in.tellg();
738 std::memcpy(
oldBox, box, 9 *
sizeof(
double));
747 std::map<int, int> type_map;
749 for (
long i = 0; i < N; i++) {
750 if (type_map.count(atomicNrs[i]) == 0) {
751 type_map.insert({atomicNrs[i], ++ntypes});
756 const std::vector<std::string> argStore =
758 std::vector<char *> argPtrs;
759 argPtrs.reserve(argStore.size());
760 for (
const std::string &arg : argStore) {
761 argPtrs.push_back(
const_cast<char *
>(arg.c_str()));
763 int lmpargc =
static_cast<int>(argPtrs.size());
764 char **lmpargv = argPtrs.data();
766 throw std::runtime_error(
767 "LAMMPS library found but lacks MPI support (lammps_open not found).\n"
768 "Install an MPI-enabled LAMMPS build.");
770 MPI_Comm inst_comm = MPI_COMM_NULL;
771 MPI_Comm_dup(mpiComm, &inst_comm);
772 LAMMPSObj = lmp.open_mpi(lmpargc, lmpargv, inst_comm,
nullptr);
774 const std::vector<std::string> argStore =
776 std::vector<char *> argPtrs;
777 argPtrs.reserve(argStore.size());
778 for (
const std::string &arg : argStore) {
779 argPtrs.push_back(
const_cast<char *
>(arg.c_str()));
781 int lmpargc =
static_cast<int>(argPtrs.size());
782 LAMMPSObj = lmp.open_no_mpi(lmpargc, argPtrs.data(),
nullptr);
788 std::string cmd = std::format(
"package omp {} force/neigh",
lammpsThr);
794 if (std::filesystem::exists(
"in.lammps")) {
795 std::ifstream infile(
"in.lammps");
797 while (std::getline(infile, line)) {
798 if (line ==
"#!units real") {
809 throw std::runtime_error(
810 "LAMMPS: in.lammps not found in working directory");
825 constexpr double kTiltCut = 1.0e-8;
826 if (std::abs(box[1]) > kTiltCut || std::abs(box[2]) > kTiltCut ||
827 std::abs(box[5]) > kTiltCut) {
828 throw std::runtime_error(
829 "LAMMPS: cell is not restricted triclinic (ay/az/bz must be ~0)");
831 std::string region_cmd =
832 std::format(
"region cell prism 0 {} 0 {} 0 {} {} {} {} units box", box[0],
833 box[4], box[8], box[3], box[6], box[7]);
836 std::string create_box_cmd = std::format(
"create_box {} cell", ntypes);
840 for (
long i = 0; i < N; i++) {
841 std::string atom_cmd =
842 std::format(
"create_atoms {} single {} {} {} units box",
843 type_map[atomicNrs[i]], 0.0, 0.0, 0.0);
#define EONC_LOG_WARNING(...)
#define EONC_LOG_INFO(...)
bool lammpsScreenRestart_
std::string lammpsScreenPath_
void setFixedMask(long nAtoms, const double *isFixed) override
Optional frozen-atom mask (nAtoms*3, 1.0 = fixed).
std::vector< double > fixedMask_
void makeNewLAMMPS(long N, const double *R, const int *atomicNrs, const double *box)
LAMMPSPot(const eonc::Parameters &p)
Production: process-default LammpsLoader and POSIX worker isolation.
void lammpsCommand(const char *cmd)
std::int64_t lammpsScreenPos_
void force(long N, const double *R, const int *atomicNrs, double *F, double *U, double *variance, const double *box) override
bool noteScreenGeometry(long N, const double *box)
void forceLocal(long N, const double *R, const int *atomicNrs, double *F, double *U, const double *box)
void applySetforce(long N)
eonc::ILammpsLoader & loader_
Potential(PotType a_ptype)
Production default: construction-scope registry, else PotRegistry::get().
RAII resource manager for the ARTn C library with global synchronization.
std::int64_t lammpsScreenCursor(std::int64_t pos, std::int64_t fileSize, bool restarted)
Byte offset at which to keep copying a LAMMPS screen file.
bool lammpsWorkerReaped(long got, long child, int err)
True when waitpid has collected the child. EINTR is not a collection.
std::vector< std::string > lammpsOpenArgs(bool logging, bool with_omp, const std::string &screen)
LAMMPS argv.