diff --git a/.github/workflows/backend-template.yml b/.github/workflows/backend-template.yml index 37879bdcb..84e31e4f5 100644 --- a/.github/workflows/backend-template.yml +++ b/.github/workflows/backend-template.yml @@ -135,10 +135,7 @@ jobs: sudo apt install -y apptainer-suid - name: Get container images for build dependencies - shell: bash - working-directory: devel-tools - run: | - apptainer pull ${{ inputs.container_name }}.sif oras://ghcr.io/colvars/devel-containers:${{ inputs.container_name }} + run: devel-tools/get_container ${{ inputs.container_name }} - name: Update and build ${{ inputs.backend_name }} shell: bash diff --git a/.github/workflows/test-library.yml b/.github/workflows/test-library.yml index b3d8d00de..d034195f2 100644 --- a/.github/workflows/test-library.yml +++ b/.github/workflows/test-library.yml @@ -92,10 +92,7 @@ jobs: sudo apt install -y apptainer-suid - name: Get container image - shell: bash - working-directory: devel-tools - run: | - apptainer pull texlive.sif oras://ghcr.io/colvars/devel-containers:texlive + run: devel-tools/get_container texlive - name: Checkout website repository uses: actions/checkout@v4 @@ -191,10 +188,7 @@ jobs: sudo apt install -y apptainer-suid - name: Get container images for build dependencies - shell: bash - working-directory: devel-tools - run: | - apptainer pull CentOS9-devel.sif oras://ghcr.io/colvars/devel-containers:CentOS9-devel + run: devel-tools/get_container CentOS9-devel - name: Run build with Clang static analyser env: @@ -252,10 +246,7 @@ jobs: sudo apt install -y apptainer-suid - name: Get container images for build dependencies - shell: bash - working-directory: devel-tools - run: | - apptainer pull CentOS9-devel.sif oras://ghcr.io/colvars/devel-containers:CentOS9-devel + run: devel-tools/get_container CentOS9-devel - name: Build with Clang and address sanitizer env: @@ -307,11 +298,8 @@ jobs: sudo apt update sudo apt install -y apptainer-suid - - name: Get container images for build environment - shell: bash - working-directory: devel-tools - run: | - apptainer pull CentOS9-devel.sif oras://ghcr.io/colvars/devel-containers:CentOS9-devel + - name: Get container images for build dependencies + run: devel-tools/get_container CentOS9-devel - name: GCC 11, C++11 (CentOS 9) env: @@ -447,10 +435,7 @@ jobs: sudo apt install -y apptainer-suid - name: Get container images for build dependencies - shell: bash - working-directory: devel-tools - run: | - apptainer pull CentOS9-oneAPI.sif oras://ghcr.io/colvars/devel-containers:CentOS9-oneAPI + run: devel-tools/get_container CentOS9-oneAPI - name: Intel oneAPI 2024.2, C++11 shell: bash @@ -573,10 +558,7 @@ jobs: sudo apt install -y apptainer-suid - name: Get container images for build dependencies - shell: bash - working-directory: devel-tools - run: | - apptainer pull CentOS9-devel-arm64.sif oras://ghcr.io/colvars/devel-containers:CentOS9-devel-arm64 + run: devel-tools/get_container CentOS9-devel-arm64 - name: GCC 11, C++20 (CentOS 9) env: diff --git a/devel-tools/get_container b/devel-tools/get_container new file mode 100755 index 000000000..9c341208c --- /dev/null +++ b/devel-tools/get_container @@ -0,0 +1,20 @@ +#!/bin/bash + +DOWNLOAD_DIR=$(dirname $0) + +label=$1 +if [ -n "${label}" ] ; then + if [ ! -f "${DOWNLOAD_DIR}/${label}.sif" ] ; then + echo "Downloading container image \"${label}\"..." + for try in {1..3}; do + apptainer pull "${DOWNLOAD_DIR}/${label}.sif" oras://ghcr.io/colvars/devel-containers:"${label}" && break + retcode=$? + if [ ${retcode} != 0 ] ; then + echo "(download failed, retrying...)" + sleep 15s + fi + done + fi +fi + +exit ${retcode} diff --git a/gromacs/src/applied_forces/colvars/colvarproxygromacs.cpp.patch b/gromacs/src/applied_forces/colvars/colvarproxygromacs.cpp.patch new file mode 100644 index 000000000..00a64b7b8 --- /dev/null +++ b/gromacs/src/applied_forces/colvars/colvarproxygromacs.cpp.patch @@ -0,0 +1,14 @@ +diff --git a/src/gromacs/applied_forces/colvars/colvarproxygromacs.cpp b/src/gromacs/applied_forces/colvars/colvarproxygromacs.cpp +index 76b350c611..37e5b53647 100644 +--- a/src/gromacs/applied_forces/colvars/colvarproxygromacs.cpp ++++ b/src/gromacs/applied_forces/colvars/colvarproxygromacs.cpp +@@ -79,9 +79,6 @@ ColvarProxyGromacs::ColvarProxyGromacs(const std::string& colvarsConfigString, + // Retrieve masses and charges from input file + updated_masses_ = updated_charges_ = true; + +- // User-scripted forces are not available in GROMACS +- have_scripts = false; +- + angstrom_value_ = 0.1; + + // From Gnu units diff --git a/lammps/src/COLVARS/colvarproxy_lammps.h b/lammps/src/COLVARS/colvarproxy_lammps.h index e1bd552e5..d5a59896e 100644 --- a/lammps/src/COLVARS/colvarproxy_lammps.h +++ b/lammps/src/COLVARS/colvarproxy_lammps.h @@ -85,7 +85,18 @@ class colvarproxy_lammps : public colvarproxy { cvm::real rand_gaussian() override; int init_atom(int atom_number) override; + int init_atom(cvm::residue_id const &residue, std::string const &atom_name, + std::string const &segment_id) override + { + return colvarproxy::init_atom(residue, atom_name, segment_id); + } + int check_atom_id(int atom_number) override; + int check_atom_id(cvm::residue_id const &residue, std::string const &atom_name, + std::string const &segment_id) override + { + return colvarproxy::check_atom_id(residue, atom_name, segment_id); + } inline std::vector *modify_atom_types() { return &atoms_types; } }; diff --git a/lammps/src/COLVARS/fix_colvars.cpp b/lammps/src/COLVARS/fix_colvars.cpp index 468d8aa9e..ff32528d8 100644 --- a/lammps/src/COLVARS/fix_colvars.cpp +++ b/lammps/src/COLVARS/fix_colvars.cpp @@ -27,7 +27,6 @@ ------------------------------------------------------------------------- */ #include "fix_colvars.h" -#include "inthash.h" #include "atom.h" #include "citeme.h" @@ -61,7 +60,6 @@ struct LAMMPS_NS::commdata { using namespace LAMMPS_NS; using namespace FixConst; -using namespace IntHash_NS; // initialize static class members int FixColvars::instances = 0; @@ -122,7 +120,6 @@ FixColvars::FixColvars(LAMMPS *lmp, int narg, char **arg) : comm_buf = nullptr; taglist = nullptr; force_buf = nullptr; - idmap = nullptr; script_args[0] = reinterpret_cast(utils::strdup("fix_modify")); @@ -227,11 +224,6 @@ FixColvars::~FixColvars() if (proxy) delete proxy; - if (idmap) { - inthash_destroy(idmap); - delete idmap; - } - if (root2root != MPI_COMM_NULL) MPI_Comm_free(&root2root); @@ -349,16 +341,11 @@ void FixColvars::init_taglist() std::vector const &tl = *(proxy->get_atom_ids()); - if (idmap) { - delete idmap; - idmap = nullptr; - } - - idmap = new inthash_t; - inthash_init(idmap, num_coords); + idmap.clear(); + idmap.reserve(num_coords); for (int i = 0; i < num_coords; ++i) { taglist[i] = tl[i]; - inthash_insert(idmap, tl[i], i); + idmap[tl[i]] = i; } } @@ -497,7 +484,6 @@ void FixColvars::setup(int vflag) std::vector &tp = *(proxy->modify_atom_types()); std::vector &cd = *(proxy->modify_atom_positions()); - std::vector &of = *(proxy->modify_atom_total_forces()); std::vector &m = *(proxy->modify_atom_masses()); std::vector &q = *(proxy->modify_atom_charges()); @@ -508,8 +494,6 @@ void FixColvars::setup(int vflag) const tagint k = atom->map(taglist[i]); if ((k >= 0) && (k < nlocal)) { - of[i].x = of[i].y = of[i].z = 0.0; - if (unwrap_flag) { const int ix = (image[k] & IMGMASK) - IMGMAX; const int iy = (image[k] >> IMGBITS & IMGMASK) - IMGMAX; @@ -545,10 +529,9 @@ void FixColvars::setup(int vflag) ndata /= size_one; for (int k=0; ksecond; tp[j] = comm_buf[k].type; @@ -558,11 +541,14 @@ void FixColvars::setup(int vflag) m[j] = comm_buf[k].m; q[j] = comm_buf[k].q; - - of[j].x = of[j].y = of[j].z = 0.0; } } } + + if (proxy->total_forces_enabled()) { + proxy->set_total_forces_invalid(); + } + } else { // me != 0 // copy coordinate data into communication buffer @@ -701,8 +687,10 @@ void FixColvars::post_force(int /*vflag*/) ndata /= size_one; for (int k=0; ksecond; + cd[j].x = comm_buf[k].x; cd[j].y = comm_buf[k].y; cd[j].z = comm_buf[k].z; @@ -826,8 +814,10 @@ void FixColvars::end_of_step() const tagint k = atom->map(taglist[i]); if ((k >= 0) && (k < nlocal)) { - const int j = inthash_lookup(idmap, tag[k]); - if (j != HASH_FAIL) { + auto search = idmap.find(tag[k]); + if (search != idmap.end()) { + const int j = search->second; + of[j].x = f[k][0]; of[j].y = f[k][1]; of[j].z = f[k][2]; @@ -845,8 +835,10 @@ void FixColvars::end_of_step() ndata /= size_one; for (int k=0; ksecond; + of[j].x = comm_buf[k].x; of[j].y = comm_buf[k].y; of[j].z = comm_buf[k].z; @@ -854,6 +846,10 @@ void FixColvars::end_of_step() } } + if (proxy->total_forces_enabled()) { + proxy->set_total_forces_valid(); + } + } else { // me != 0 /* copy total force data into communication buffer */ nme = 0; @@ -871,6 +867,7 @@ void FixColvars::end_of_step() MPI_Recv(&tmp, 0, MPI_INT, 0, 0, world, MPI_STATUS_IGNORE); MPI_Rsend(comm_buf, nme*size_one, MPI_BYTE, 0, 0, world); } + } } diff --git a/lammps/src/COLVARS/fix_colvars.h b/lammps/src/COLVARS/fix_colvars.h index 01c5b0e48..40872d5bd 100644 --- a/lammps/src/COLVARS/fix_colvars.h +++ b/lammps/src/COLVARS/fix_colvars.h @@ -35,11 +35,9 @@ FixStyle(colvars,FixColvars); #define LMP_FIX_COLVARS_H #include "fix.h" +#include // Forward declarations -namespace IntHash_NS { -struct inthash_t; -} class colvarproxy_lammps; namespace LAMMPS_NS { @@ -87,7 +85,7 @@ class FixColvars : public Fix { /// Arguments passed from fix_modify to the Colvars script interface unsigned char *script_args[100]; - IntHash_NS::inthash_t *idmap; // hash for mapping atom indices to consistent order. + std::unordered_map idmap; // for mapping atom indices to consistent order. int nlevels_respa; // flag to determine respa levels. int store_forces; // flag to determine whether to store total forces diff --git a/namd/src/GlobalMasterColvars.h b/namd/src/GlobalMasterColvars.h index 5ceaaf13c..bbabd687b 100644 --- a/namd/src/GlobalMasterColvars.h +++ b/namd/src/GlobalMasterColvars.h @@ -39,6 +39,11 @@ class GlobalMasterColvars : public GlobalMaster { return modifyAppliedForces(); } + inline ResizeArray const &getRequestedGroups() const + { + return reqGroups; + } + inline ResizeArray &modifyRequestedGroupsPublic() { return modifyRequestedGroups(); @@ -49,6 +54,11 @@ class GlobalMasterColvars : public GlobalMaster { return modifyGroupForces(); } + inline IntList const &getRequestedGridObjects() const + { + return reqGridObjs; + } + inline IntList &modifyRequestedGridObjectsPublic() { return modifyRequestedGridObjects(); diff --git a/namd/src/colvarproxy_namd.C b/namd/src/colvarproxy_namd.C index f17215a7e..799834fe3 100644 --- a/namd/src/colvarproxy_namd.C +++ b/namd/src/colvarproxy_namd.C @@ -37,6 +37,8 @@ #include #endif +#include + #include "colvarmodule.h" #include "colvar.h" #include "colvarbias.h" @@ -134,12 +136,6 @@ colvarproxy_namd::colvarproxy_namd(GlobalMasterColvars *gm) // save to Node for Tcl script access Node::Object()->colvars = colvars; -#ifdef NAMD_TCL - have_scripts = true; -#else - have_scripts = false; -#endif - if (simparams->firstTimestep != 0) { colvars->set_initial_step(static_cast(simparams->firstTimestep)); } @@ -263,13 +259,12 @@ int colvarproxy_namd::setup() // zero out mutable arrays atoms_positions[i] = cvm::rvector(0.0, 0.0, 0.0); - atoms_total_forces[i] = cvm::rvector(0.0, 0.0, 0.0); atoms_new_colvar_forces[i] = cvm::rvector(0.0, 0.0, 0.0); } size_t n_group_atoms = 0; - for (int ig = 0; ig < globalmaster->modifyRequestedGroupsPublic().size(); ig++) { - n_group_atoms += globalmaster->modifyRequestedGroupsPublic()[ig].size(); + for (int ig = 0; ig < globalmaster->getRequestedGroups().size(); ig++) { + n_group_atoms += globalmaster->getRequestedGroups()[ig].size(); } log("updating group data ("+cvm::to_str(atom_groups_ids.size())+ @@ -277,24 +272,30 @@ int colvarproxy_namd::setup() cvm::to_str(n_group_atoms)+" atoms in total).\n"); // Note: groupMassBegin, groupMassEnd may be used here, but they won't work for charges - for (int ig = 0; ig < globalmaster->modifyRequestedGroupsPublic().size(); ig++) { + for (int ig = 0; ig < globalmaster->getRequestedGroups().size(); ig++) { // update mass and charge update_group_properties(ig); atom_groups_coms[ig] = cvm::rvector(0.0, 0.0, 0.0); - atom_groups_total_forces[ig] = cvm::rvector(0.0, 0.0, 0.0); atom_groups_new_colvar_forces[ig] = cvm::rvector(0.0, 0.0, 0.0); } #if NAMD_VERSION_NUMBER >= 34471681 log("updating grid object data ("+cvm::to_str(volmaps_ids.size())+ " grid objects in total).\n"); - for (int imap = 0; imap < globalmaster->modifyGridObjForcesPublic().size(); imap++) { + for (int imap = 0; imap < globalmaster->getRequestedGridObjects().size(); imap++) { volmaps_new_colvar_forces[imap] = 0.0; } #endif + if (total_force_requested && modified_atom_list()) { + if (cvm::debug()) { + log("zeroing out buffers total forces on atom and groups.\n"); + } + set_total_forces_invalid(); + } + size_t const new_features_hash = std::hash{}(colvars->feature_report(0)); if (new_features_hash != features_hash) { @@ -325,7 +326,13 @@ int colvarproxy_namd::reset() globalmaster->reset(); - atoms_map.clear(); + // atoms_map.clear(); + // TODO: There's no other way to re-initialize the atoms_map after + // clearing and then reloading a new configuration file, so we just + // assume that the number of atoms is unchanged, and reset the atoms_map + // to -1. However, this might be problematic if NAMD supports to + // reload a new system with different number of atoms in the future. + std::fill(atoms_map.begin(), atoms_map.end(), -1); // Clear internal atomic data error_code |= colvarproxy::reset(); @@ -420,11 +427,15 @@ void colvarproxy_namd::calculate() // If new atomic positions or forces have been requested by other // GlobalMaster objects, add these to the atom map as well size_t const n_all_atoms = Node::Object()->molecule->numAtoms; - if ( (atoms_map.size() != n_all_atoms) || - (int(atoms_ids.size()) < (globalmaster->getAtomIdEndPublic() - globalmaster->getAtomIdBeginPublic())) || - (int(atoms_ids.size()) < (globalmaster->getForceIdEndPublic() - globalmaster->getForceIdBeginPublic())) ) { + if (modified_atom_list() || /* Colvars just requested new atoms */ + (atoms_map.size() != n_all_atoms) || /* The system topology has changed */ + (int(atoms_ids.size()) < /* Another GlobalMaster requested new atoms */ + (globalmaster->getAtomIdEndPublic() - globalmaster->getAtomIdBeginPublic())) || + (int(atoms_ids.size()) < /* Another GlobalMaster requested new total forces */ + (globalmaster->getForceIdEndPublic() - globalmaster->getForceIdBeginPublic()))) { update_atoms_map(globalmaster->getAtomIdBeginPublic(), globalmaster->getAtomIdEndPublic()); update_atoms_map(globalmaster->getForceIdBeginPublic(), globalmaster->getForceIdEndPublic()); + reset_modified_atom_list(); // reset the flag as needed } // prepare local arrays @@ -480,6 +491,10 @@ void colvarproxy_namd::calculate() ForceList::const_iterator f_i = globalmaster->getTotalForcePublic(); for ( ; a_i != a_e; ++a_i, ++f_i ) { + if (atoms_map[*a_i] < 0) { + cvm::error("Bug: atoms_map at " + cvm::to_str(*a_i) + " is less than zero!\n", + COLVARS_BUG_ERROR); + } atoms_total_forces[atoms_map[*a_i]] = cvm::rvector((*f_i).x, (*f_i).y, (*f_i).z); n_total_forces++; } @@ -561,6 +576,11 @@ void colvarproxy_namd::calculate() cvm::error("Error in the collective variables module.\n", COLVARS_ERROR); } + if (total_force_requested) { + // Total forces will be valid at the next step (this function is only called once) + set_total_forces_valid(); + } + if (cvm::debug()) { print_output_atomic_data(); } @@ -836,6 +856,9 @@ int colvarproxy_namd::init_atom(cvm::residue_id const &residue, ") for collective variables calculation.\n"); int const index = add_atom_slot(aid); + if (atoms_map.empty()) { + cvm::error("Bug: atoms_map is empty in colvarproxy_namd::init_atom!", COLVARS_BUG_ERROR); + } atoms_map[aid] = index; globalmaster->modifyRequestedAtomsPublic().add(aid); update_atom_properties(index); diff --git a/namd/src/colvarproxy_namd.h b/namd/src/colvarproxy_namd.h index ef86f615d..57f114b58 100644 --- a/namd/src/colvarproxy_namd.h +++ b/namd/src/colvarproxy_namd.h @@ -19,6 +19,11 @@ #include "colvarproxy_namd_version.h" +// For NAMD_UNIFIED_REDUCTION and AtomIDList +#include "NamdTypes.h" +// For CMK_SMP && USE_CKLOOP +#include "Node.h" + #include "colvarmodule.h" #include "colvarproxy.h" #include "colvarvalue.h" diff --git a/namd/tests/interface/003_reinitatoms/AutoDiff/test.colvars.traj b/namd/tests/interface/003_reinitatoms/AutoDiff/test.colvars.traj index 181c17ed2..cc26f2d68 100644 --- a/namd/tests/interface/003_reinitatoms/AutoDiff/test.colvars.traj +++ b/namd/tests/interface/003_reinitatoms/AutoDiff/test.colvars.traj @@ -43,7 +43,7 @@ 39 6.88300617631749e+00 -3.72442767426713e+01 -8.83006176317490e-01 40 6.88218411453200e+00 -2.66371930519242e+01 -8.82184114532001e-01 # step rgyr ft_rgyr fa_rgyr - 40 6.79302548471894e+00 -nan -7.93025484718935e-01 + 40 6.79302548471894e+00 0.00000000000000e+00 -7.93025484718935e-01 41 6.79123478804224e+00 1.47884739101504e+01 -7.91234788042243e-01 42 6.78960135015216e+00 6.31388772773012e+01 -7.89601350152160e-01 43 6.78822556441034e+00 1.06473629117400e+02 -7.88225564410344e-01 diff --git a/namd/tests/interface/003_reinitatoms/skip_test.sh b/namd/tests/interface/003_reinitatoms/skip_test.sh deleted file mode 100644 index 80117a457..000000000 --- a/namd/tests/interface/003_reinitatoms/skip_test.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -ARCH=$(arch) -if [ "$ARCH" = "aarch64" ]; then - exit 0 -else - exit 1 -fi diff --git a/src/colvar.cpp b/src/colvar.cpp index 0686e1f5e..90f288112 100644 --- a/src/colvar.cpp +++ b/src/colvar.cpp @@ -1427,8 +1427,9 @@ int colvar::calc_cvcs(int first_cvc, size_t num_cvcs) return error_code; } - if ((cvm::step_relative() > 0) && (!is_enabled(f_cv_total_force_current_step))){ - // Use Jacobian derivative from previous timestep + if (cvm::main()->proxy->total_forces_valid() && (!is_enabled(f_cv_total_force_current_step))) { + // Use Jacobian derivative computed at previous timestep and the total forces from the same + // step, collected just now from the engine error_code |= calc_cvc_total_force(first_cvc, num_cvcs); } // atom coordinates are updated by the next line @@ -1454,7 +1455,7 @@ int colvar::collect_cvc_data() int error_code = COLVARS_OK; - if ((cvm::step_relative() > 0) && (!is_enabled(f_cv_total_force_current_step))){ + if (!is_enabled(f_cv_total_force_current_step)) { // Total force depends on Jacobian derivative from previous timestep // collect_cvc_total_forces() uses the previous value of jd error_code |= collect_cvc_total_forces(); @@ -1644,6 +1645,11 @@ int colvar::collect_cvc_gradients() int colvar::calc_cvc_total_force(int first_cvc, size_t num_cvcs) { + if (!cvm::main()->proxy->total_forces_valid()) { + // This is not a step when valid total forces are available + return COLVARS_OK; + } + size_t const cvc_max_count = num_cvcs ? num_cvcs : num_active_cvcs(); size_t i, cvc_count; @@ -1674,26 +1680,29 @@ int colvar::calc_cvc_total_force(int first_cvc, size_t num_cvcs) int colvar::collect_cvc_total_forces() { if (is_enabled(f_cv_total_force_calc)) { + ft.reset(); - for (size_t i = 0; i < cvcs.size(); i++) { - if (!cvcs[i]->is_enabled()) continue; - if (cvm::debug()) - cvm::log("Colvar component no. "+cvm::to_str(i+1)+ - " within colvar \""+this->name+"\" has total force "+ - cvm::to_str((cvcs[i])->total_force(), - cvm::cv_width, cvm::cv_prec)+".\n"); - // linear combination is assumed - ft += (cvcs[i])->total_force() * (cvcs[i])->sup_coeff / active_cvc_square_norm; - } + if (cvm::main()->proxy->total_forces_valid()) { + for (size_t i = 0; i < cvcs.size(); i++) { + if (!cvcs[i]->is_enabled()) continue; + if (cvm::debug()) + cvm::log("Colvar component no. "+cvm::to_str(i+1)+ + " within colvar \""+this->name+"\" has total force "+ + cvm::to_str((cvcs[i])->total_force(), + cvm::cv_width, cvm::cv_prec)+".\n"); + // linear combination is assumed + ft += (cvcs[i])->total_force() * (cvcs[i])->sup_coeff / active_cvc_square_norm; + } - if (!(is_enabled(f_cv_hide_Jacobian) && is_enabled(f_cv_subtract_applied_force))) { - // This is by far the most common case - // Add the Jacobian force to the total force, and don't apply any silent - // correction internally: biases such as colvarbias_abf will handle it - // If f_cv_hide_Jacobian is enabled, a force of -fj is present in ft due to the - // Jacobian-compensating force - ft += fj; + if (!(is_enabled(f_cv_hide_Jacobian) && is_enabled(f_cv_subtract_applied_force))) { + // This is by far the most common case + // Add the Jacobian force to the total force, and don't apply any silent + // correction internally: biases such as colvarbias_abf will handle it + // If f_cv_hide_Jacobian is enabled, a force of -fj is present in ft due to the + // Jacobian-compensating force + ft += fj; + } } } diff --git a/src/colvar_gpu_calc.cpp b/src/colvar_gpu_calc.cpp index 555e2f61f..c0a9bd82c 100644 --- a/src/colvar_gpu_calc.cpp +++ b/src/colvar_gpu_calc.cpp @@ -24,6 +24,10 @@ int colvarmodule_gpu_calc::cvc_calc_total_force( colvarmodule* colvar_module, bool use_current_step) { int error_code = COLVARS_OK; + const bool total_force_valid = colvar_module->proxy ? colvar_module->proxy->total_forces_valid() : false; + if (!total_force_valid) { + return error_code; + } colvarproxy* p = colvar_module->proxy; cudaStream_t stream = p->get_default_stream(); #if defined (COLVARS_NVTX_PROFILING) @@ -617,6 +621,8 @@ int colvarmodule_gpu_calc::cv_collect_cvc_data(const std::vector& colva cv_collect_cvc_data_prof.start(); #endif // defined (COLVARS_NVTX_PROFILING) for (auto cvi = colvars.begin(); cvi != colvars.end(); cvi++) { + // If the total forces are not available, it will be reset in + // collect_cvc_total_forces anyway (called by collect_cvc_data) error_code |= (*cvi)->collect_cvc_data(); if (colvar_module->get_error()) { return COLVARS_ERROR; diff --git a/src/colvarproxy.cpp b/src/colvarproxy.cpp index d1315c529..59ed83b6e 100644 --- a/src/colvarproxy.cpp +++ b/src/colvarproxy.cpp @@ -416,18 +416,14 @@ int colvarproxy_smp::smp_unlock() -colvarproxy_script::colvarproxy_script() -{ - script = NULL; - have_scripts = false; -} +colvarproxy_script::colvarproxy_script() {} colvarproxy_script::~colvarproxy_script() { - if (script != NULL) { + if (script) { delete script; - script = NULL; + script = nullptr; } } @@ -613,6 +609,21 @@ int colvarproxy::post_run() } +void colvarproxy::set_total_forces_invalid() +{ + std::fill(atoms_total_forces.begin(), atoms_total_forces.end(), cvm::rvector(0.0, 0.0, 0.0)); + std::fill(atom_groups_total_forces.begin(), atom_groups_total_forces.end(), + cvm::rvector(0.0, 0.0, 0.0)); + total_forces_valid_ = false; +} + + +void colvarproxy::set_total_forces_valid() +{ + total_forces_valid_ = true; +} + + void colvarproxy::print_input_atomic_data() { cvm::log(cvm::line_marker); diff --git a/src/colvarproxy.h b/src/colvarproxy.h index a1e99eff0..c05380003 100644 --- a/src/colvarproxy.h +++ b/src/colvarproxy.h @@ -529,10 +529,7 @@ class colvarproxy_script { /// Pointer to the scripting interface object /// (does not need to be allocated in a new interface) - colvarscript *script; - - /// Do we have a scripting interface? - bool have_scripts; + colvarscript *script = nullptr; /// Run a user-defined colvar forces script virtual int run_force_callback(); @@ -629,6 +626,18 @@ class colvarproxy return engine_ready_; } + /// Are total forces currently valid? (They would not be right after configuration change) + inline bool total_forces_valid() const + { + return total_forces_valid_; + } + + /// Mark the total forces as invalid (due to e.g. a configuration change) + void set_total_forces_invalid(); + + /// Mark the total forces as up to date + void set_total_forces_valid(); + /// Enqueue new configuration text, to be parsed as soon as possible void add_config(std::string const &cmd, std::string const &conf); @@ -695,6 +704,9 @@ class colvarproxy /// Whether the engine allows to fully initialize Colvars immediately bool engine_ready_; + /// Whether the total forces are currently valid + bool total_forces_valid_ = false; + /// Collected error messages std::string error_output; diff --git a/src/colvarproxy_system.cpp b/src/colvarproxy_system.cpp index 401bb4c59..6b082b8ed 100644 --- a/src/colvarproxy_system.cpp +++ b/src/colvarproxy_system.cpp @@ -7,6 +7,7 @@ // If you wish to distribute your changes, please submit them to the // Colvars repository at GitHub. +#include #include "colvarmodule.h" #include "colvartypes.h" diff --git a/vmd/src/colvarproxy_vmd.C b/vmd/src/colvarproxy_vmd.C index ff2644dae..f5f310807 100644 --- a/vmd/src/colvarproxy_vmd.C +++ b/vmd/src/colvarproxy_vmd.C @@ -85,15 +85,9 @@ colvarproxy_vmd::colvarproxy_vmd(Tcl_Interp *interp, VMDApp *v, int molid) // both fields are taken from data structures already available updated_masses_ = updated_charges_ = true; - // Do we have scripts? - // For now colvars depend on Tcl, but this may not always be the case - // in the future #if defined(VMDTCL) - have_scripts = true; // Need to set this before constructing colvarmodule, which creates colvarscript object set_tcl_interp(interp); -#else - have_scripts = false; #endif colvars = new colvarmodule(this);