diff --git a/src/finish.cpp b/src/finish.cpp index c774f51e58..3b824f2ac4 100644 --- a/src/finish.cpp +++ b/src/finish.cpp @@ -121,9 +121,9 @@ void Finish::end(int flag) MPI_Allreduce(&cpu_loop,&tmp,1,MPI_DOUBLE,MPI_SUM,world); cpu_loop = tmp/nprocs; if (time_loop > 0.0) cpu_loop = cpu_loop/time_loop*100.0; + output->thermo->footer(); if (me == 0) { - output->thermo->footer(); int ntasks = nprocs * nthreads; utils::logmesg(lmp,"Loop time of {:.6g} on {} procs for {} steps with {} atoms\n\n", time_loop,ntasks,update->nsteps,atom->natoms); diff --git a/src/library.cpp b/src/library.cpp index b53257fc45..aa835d0906 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -853,7 +853,11 @@ This differs from :cpp:func:`lammps_get_thermo` in that it does **not** trigger an evaluation. Instead it provides direct access to a read-only location of the last thermo output data and the corresponding keyword strings. How to handle the return value depends on the value of the *what* -argument string. +argument string. When accessing the data from a concurrent thread while +LAMMPS is running, the cache needs to be locked first and then unlocked +after the data is obtained, so that the data is not corrupted while +reading in case LAMMPS wants to update it at the same time. Outside +of a run, the lock/unlock calls have no effect. .. note:: @@ -902,6 +906,14 @@ argument string. - actual field data for column - pointer to int, int64_t or double - yes + * - lock + - acquires lock to thermo data cache + - NULL pointer + - no + * - unlock + - releases lock to thermo data cache + - NULL pointer + - no \endverbatim * @@ -957,8 +969,14 @@ void *lammps_last_thermo(void *handle, const char *what, int index) } else if (field.type == multitype::LAMMPS_DOUBLE) { val = (void *) &field.data.d; } - + } else if (strcmp(what, "lock") == 0) { + th->lock_cache(); + val = nullptr; + } else if (strcmp(what, "unlock") == 0) { + th->unlock_cache(); + val = nullptr; } else val = nullptr; + } END_CAPTURE return val; diff --git a/src/thermo.cpp b/src/thermo.cpp index b2e4f7f934..d7a00fd14c 100644 --- a/src/thermo.cpp +++ b/src/thermo.cpp @@ -48,6 +48,7 @@ #include #include +#include #include using namespace LAMMPS_NS; @@ -100,8 +101,8 @@ static char fmtbuf[512]; /* ---------------------------------------------------------------------- */ Thermo::Thermo(LAMMPS *_lmp, int narg, char **arg) : - Pointers(_lmp), style(nullptr), vtype(nullptr), field2index(nullptr), argindex1(nullptr), - argindex2(nullptr), temperature(nullptr), pressure(nullptr), pe(nullptr) + Pointers(_lmp), style(nullptr), vtype(nullptr), cache_mutex(nullptr), field2index(nullptr), + argindex1(nullptr), argindex2(nullptr), temperature(nullptr), pressure(nullptr), pe(nullptr) { style = utils::strdup(arg[0]); @@ -208,6 +209,7 @@ void Thermo::init() ValueTokenizer *format_line = nullptr; if (format_line_user.size()) format_line = new ValueTokenizer(format_line_user); + lock_cache(); field_data.clear(); field_data.resize(nfield); std::string format_this, format_line_user_def; @@ -277,6 +279,7 @@ void Thermo::init() format[i] += fmt::format("{:<8} = {} ", keyword[i], format_this); } } + unlock_cache(); // chop off trailing blank or add closing bracket if needed and then add newline if (lineflag == ONELINE) @@ -320,6 +323,9 @@ void Thermo::init() if (index_press_scalar >= 0) pressure = computes[index_press_scalar]; if (index_press_vector >= 0) pressure = computes[index_press_vector]; if (index_pe >= 0) pe = computes[index_pe]; + + // create mutex to protect access to cached thermo data + cache_mutex = new std::mutex; } /* ---------------------------------------------------------------------- @@ -366,9 +372,17 @@ void Thermo::header() /* ---------------------------------------------------------------------- */ +// called at the end of a run from Finish class + void Thermo::footer() { - if (lineflag == YAMLLINE) utils::logmesg(lmp, "...\n"); + if (comm->me == 0) { + if (lineflag == YAMLLINE) utils::logmesg(lmp, "...\n"); + } + + // no more locking for cached thermo data access needed + delete cache_mutex; + cache_mutex = nullptr; } /* ---------------------------------------------------------------------- */ @@ -422,6 +436,7 @@ void Thermo::compute(int flag) } // add each thermo value to line with its specific format + lock_cache(); field_data.clear(); field_data.resize(nfield); @@ -441,6 +456,7 @@ void Thermo::compute(int flag) field_data[ifield] = bivalue; } } + unlock_cache(); // print line to screen and logfile @@ -579,7 +595,8 @@ void Thermo::modify_params(int narg, char **arg) if (iarg + 2 > narg) error->all(FLERR, "Illegal thermo_modify command"); triclinic_general = utils::logical(FLERR, arg[iarg + 1], false, lmp); if (triclinic_general && !domain->triclinic_general) - error->all(FLERR,"Thermo_modify triclinic/general cannot be used " + error->all(FLERR, + "Thermo_modify triclinic/general cannot be used " "if simulation box is not general triclinic"); iarg += 2; @@ -1566,6 +1583,26 @@ int Thermo::evaluate_keyword(const std::string &word, double *answer) return 0; } +/* ---------------------------------------------------------------------- */ + +// lock cache for current thermo data + +void Thermo::lock_cache() +{ + // no locking outside of a run + if (!cache_mutex) return; + cache_mutex->lock(); +} + +// unlock cache for current thermo data + +void Thermo::unlock_cache() +{ + // no locking outside of a run + if (!cache_mutex) return; + cache_mutex->unlock(); +} + /* ---------------------------------------------------------------------- extraction of Compute, Fix, Variable results compute/fix are normalized by atoms if returning extensive value diff --git a/src/thermo.h b/src/thermo.h index 52f69e7609..b5934f48b1 100644 --- a/src/thermo.h +++ b/src/thermo.h @@ -17,6 +17,10 @@ #include "pointers.h" #include +namespace std { +class mutex; +} + namespace LAMMPS_NS { class Thermo : protected Pointers { @@ -43,6 +47,8 @@ class Thermo : protected Pointers { int evaluate_keyword(const std::string &, double *); // for accessing cached thermo and related data + void lock_cache(); + void unlock_cache(); const int *get_line() const { return &nline; } const char *get_image_fname() const { return image_fname.c_str(); } @@ -82,6 +88,9 @@ class Thermo : protected Pointers { int nline; std::string image_fname; + // mutex for locking the cache + std::mutex *cache_mutex; + // data used by routines that compute single values int ivalue; // integer value to print diff --git a/tools/lammps-gui/lammps-gui.appdata.xml b/tools/lammps-gui/lammps-gui.appdata.xml index abf0a4bf45..c019ab1ce3 100644 --- a/tools/lammps-gui/lammps-gui.appdata.xml +++ b/tools/lammps-gui/lammps-gui.appdata.xml @@ -61,6 +61,8 @@ Highlight warnings and error messages in Output window Make Tutorial wizards more compact Include download and compilation of WHAM software from Alan Grossfield + Add dialog to run WHAM directly from LAMMPS-GUI + Use mutex to avoid corruption of thermo data diff --git a/tools/lammps-gui/lammpsgui.cpp b/tools/lammps-gui/lammpsgui.cpp index 8749e24821..59e8017fc0 100644 --- a/tools/lammps-gui/lammpsgui.cpp +++ b/tools/lammps-gui/lammpsgui.cpp @@ -1015,6 +1015,7 @@ void LammpsGui::logupdate() void *ptr = lammps.last_thermo("setup", 0); if (ptr && *(int *)ptr) return; + lammps.last_thermo("lock", 0); ptr = lammps.last_thermo("num", 0); if (ptr) { int ncols = *(int *)ptr; @@ -1066,6 +1067,7 @@ void LammpsGui::logupdate() chartwindow->add_data(step, data, i); } } + lammps.last_thermo("unlock", 0); } // update list of available image file names