static constexpr int s_maxNumThreadsForReduction = 256;
template<typename ForceBufferElementType>
-ThreadForceBuffer<ForceBufferElementType>::ThreadForceBuffer(const int threadIndex,
- const int numEnergyGroups) :
+ThreadForceBuffer<ForceBufferElementType>::ThreadForceBuffer(const int threadIndex,
+ const bool useEnergyTerms,
+ const int numEnergyGroups) :
threadIndex_(threadIndex), shiftForces_(c_numShiftVectors), groupPairEnergies_(numEnergyGroups)
{
+ if (useEnergyTerms)
+ {
+ energyTerms_.resize(F_NRE);
+ }
}
template<typename ForceBufferElementType>
} // namespace
template<typename ForceBufferElementType>
-ThreadedForceBuffer<ForceBufferElementType>::ThreadedForceBuffer(const int numThreads, const int numEnergyGroups)
+ThreadedForceBuffer<ForceBufferElementType>::ThreadedForceBuffer(const int numThreads,
+ const bool useEnergyTerms,
+ const int numEnergyGroups) :
+ useEnergyTerms_(useEnergyTerms)
{
threadForceBuffers_.resize(numThreads);
#pragma omp parallel for num_threads(numThreads) schedule(static)
/* Note that thread 0 uses the global fshift and energy arrays,
* but to keep the code simple, we initialize all data here.
*/
- threadForceBuffers_[t] =
- std::make_unique<ThreadForceBuffer<ForceBufferElementType>>(t, numEnergyGroups);
+ threadForceBuffers_[t] = std::make_unique<ThreadForceBuffer<ForceBufferElementType>>(
+ t, useEnergyTerms_, numEnergyGroups);
}
GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
}
const gmx::StepWorkload& stepWork,
const int reductionBeginIndex)
{
- if (!usedBlockIndices_.empty())
+ if (stepWork.computeForces && !usedBlockIndices_.empty())
{
- /* Reduce the bonded force buffer */
+ /* Reduce the force buffer */
+ GMX_ASSERT(forceWithShiftForces, "Need a valid force buffer for reduction");
+
reduceThreadForceBuffers<ForceBufferElementType>(
forceWithShiftForces->force(), threadForceBuffers_, reductionMask_, usedBlockIndices_);
}
- rvec* gmx_restrict fshift = as_rvec_array(forceWithShiftForces->shiftForces().data());
-
const int numBuffers = numThreadBuffers();
/* When necessary, reduce energy and virial using one thread only */
if (stepWork.computeVirial)
{
+ GMX_ASSERT(forceWithShiftForces, "Need a valid force buffer for reduction");
+
+ rvec* gmx_restrict fshift = as_rvec_array(forceWithShiftForces->shiftForces().data());
+
for (int i = 0; i < gmx::c_numShiftVectors; i++)
{
for (int t = reductionBeginIndex; t < numBuffers; t++)
}
}
}
- if (stepWork.computeEnergy)
+ if (stepWork.computeEnergy && useEnergyTerms_)
{
+ GMX_ASSERT(ener, "Need a valid energy buffer for reduction");
+
for (int i = 0; i < F_NRE; i++)
{
for (int t = reductionBeginIndex; t < numBuffers; t++)
ener[i] += f_t[t]->energyTerms()[i];
}
}
+ }
+
+ if (stepWork.computeEnergy)
+ {
+ GMX_ASSERT(grpp, "Need a valid group pair energy buffer for reduction");
+
for (int i = 0; i < static_cast<int>(NonBondedEnergyTerms::Count); i++)
{
for (int j = 0; j < f_t[0]->groupPairEnergies().nener; j++)
}
if (stepWork.computeDhdl)
{
+ GMX_ASSERT(!dvdl.empty(), "Need a valid dV/dl buffer for reduction");
+
for (auto i : keysOf(f_t[0]->dvdl()))
{
}
}
+template class ThreadForceBuffer<RVec>;
+template class ThreadedForceBuffer<RVec>;
+
template class ThreadForceBuffer<rvec4>;
template class ThreadedForceBuffer<rvec4>;
#include "gromacs/math/vectypes.h"
#include "gromacs/mdtypes/enerdata.h"
#include "gromacs/mdtypes/simulation_workload.h"
-#include "gromacs/topology/idef.h"
#include "gromacs/topology/ifunc.h"
#include "gromacs/utility/alignedallocator.h"
#include "gromacs/utility/arrayref.h"
//! Force buffer block size in atoms
static constexpr int s_reductionBlockSize = (1 << s_numReductionBlockBits);
- //! Constructor
- ThreadForceBuffer(int threadIndex, int numEnergyGroups);
+ /*! \brief Constructor
+ * \param[in] threadIndex The index of the thread that will fill the buffers in this object
+ * \param[in] useEnergyTerms Whether the list of energy terms will be used
+ * \param[in] numEnergyGroups The number of non-bonded energy groups
+ */
+ ThreadForceBuffer(int threadIndex, bool useEnergyTerms, int numEnergyGroups);
//! Resizes the buffer to \p numAtoms and clears the mask
void resizeBufferAndClearMask(int numAtoms);
//! Shift force array, size c_numShiftVectors
std::vector<RVec> shiftForces_;
- //! Energy array
- std::array<real, F_NRE> energyTerms_;
+ //! Energy array, can be empty
+ std::vector<real> energyTerms_;
//! Group pair energy data for pairs
gmx_grppairener_t groupPairEnergies_;
//! Free-energy dV/dl output
class ThreadedForceBuffer
{
public:
- //! Constructor
- ThreadedForceBuffer(int numThreads, int numEnergyGroups);
+ /*! \brief Constructor
+ * \param[in] numThreads The number of threads that will use the buffers and reduce
+ * \param[in] useEnergyTerms Whether the list of energy terms will be used
+ * \param[in] numEnergyGroups The number of non-bonded energy groups
+ */
+ ThreadedForceBuffer(int numThreads, bool useEnergyTerms, int numEnergyGroups);
//! Returns the number of thread buffers
int numThreadBuffers() const { return threadForceBuffers_.size(); }
*
* The reduction of all output starts at the output from thread \p reductionBeginIndex,
* except for the normal force buffer, which always starts at 0.
+ *
+ * Buffers that will not be used as indicated by the flags in \p stepWork
+ * are allowed to be nullptr or empty.
*/
void reduce(gmx::ForceWithShiftForces* forceWithShiftForces,
real* ener,
int reductionBeginIndex);
private:
+ //! Whether the energy buffer is used
+ bool useEnergyTerms_;
//! Force/energy data per thread, size nthreads, stored in unique_ptr to allow thread local allocation
std::vector<std::unique_ptr<ThreadForceBuffer<ForceBufferElementType>>> threadForceBuffers_;
//! Indices of blocks that are used, i.e. have force contributions.
GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(ThreadedForceBuffer);
};
-// Instantiate for rvec4. Can also be instantiated for rvec.
+// Instantiate for RVec
+extern template class ThreadForceBuffer<RVec>;
+extern template class ThreadedForceBuffer<RVec>;
+
+// Instantiate for rvec4
extern template class ThreadForceBuffer<rvec4>;
extern template class ThreadedForceBuffer<rvec4>;