2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
5 * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
38 * \brief This file defines high-level functions for mdrun to compute
39 * energies and forces for listed interactions.
41 * \author Mark Abraham <mark.j.abraham@gmail.com>
43 * \ingroup module_listed_forces
47 #include "gromacs/utility/arrayref.h"
48 #include "gromacs/utility/enumerationhelpers.h"
49 #include "listed_forces.h"
57 #include "gromacs/gmxlib/network.h"
58 #include "gromacs/gmxlib/nrnb.h"
59 #include "gromacs/listed_forces/bonded.h"
60 #include "gromacs/listed_forces/disre.h"
61 #include "gromacs/listed_forces/orires.h"
62 #include "gromacs/listed_forces/pairs.h"
63 #include "gromacs/listed_forces/position_restraints.h"
64 #include "gromacs/math/vec.h"
65 #include "gromacs/mdlib/enerdata_utils.h"
66 #include "gromacs/mdlib/force.h"
67 #include "gromacs/mdlib/gmx_omp_nthreads.h"
68 #include "gromacs/mdtypes/commrec.h"
69 #include "gromacs/mdtypes/mdatom.h"
70 #include "gromacs/mdtypes/fcdata.h"
71 #include "gromacs/mdtypes/forceoutput.h"
72 #include "gromacs/mdtypes/forcerec.h"
73 #include "gromacs/mdtypes/inputrec.h"
74 #include "gromacs/mdtypes/md_enums.h"
75 #include "gromacs/mdtypes/simulation_workload.h"
76 #include "gromacs/pbcutil/ishift.h"
77 #include "gromacs/pbcutil/pbc.h"
78 #include "gromacs/timing/wallcycle.h"
79 #include "gromacs/topology/topology.h"
80 #include "gromacs/utility/exceptions.h"
81 #include "gromacs/utility/fatalerror.h"
83 #include "listed_internal.h"
84 #include "manage_threading.h"
85 #include "utilities.h"
87 ListedForces::ListedForces(const gmx_ffparams_t& ffparams,
88 const int numEnergyGroups,
90 const InteractionSelection interactionSelection,
92 idefSelection_(ffparams),
93 threading_(std::make_unique<bonded_threading_t>(numThreads, numEnergyGroups, fplog)),
94 interactionSelection_(interactionSelection),
95 foreignEnergyGroups_(std::make_unique<gmx_grppairener_t>(numEnergyGroups))
99 ListedForces::ListedForces(ListedForces&& o) noexcept = default;
101 ListedForces::~ListedForces() = default;
103 //! Copies the selection interactions from \p idefSrc to \p idef
104 static void selectInteractions(InteractionDefinitions* idef,
105 const InteractionDefinitions& idefSrc,
106 const ListedForces::InteractionSelection interactionSelection)
108 const bool selectPairs =
109 interactionSelection.test(static_cast<int>(ListedForces::InteractionGroup::Pairs));
110 const bool selectDihedrals =
111 interactionSelection.test(static_cast<int>(ListedForces::InteractionGroup::Dihedrals));
112 const bool selectAngles =
113 interactionSelection.test(static_cast<int>(ListedForces::InteractionGroup::Angles));
114 const bool selectRest =
115 interactionSelection.test(static_cast<int>(ListedForces::InteractionGroup::Rest));
117 for (int ftype = 0; ftype < F_NRE; ftype++)
119 const t_interaction_function& ifunc = interaction_function[ftype];
120 if (ifunc.flags & IF_BOND)
123 if (ifunc.flags & IF_PAIR)
125 assign = selectPairs;
127 else if (ifunc.flags & IF_DIHEDRAL)
129 assign = selectDihedrals;
131 else if (ifunc.flags & IF_ATYPE)
133 assign = selectAngles;
141 idef->il[ftype] = idefSrc.il[ftype];
145 idef->il[ftype].clear();
151 void ListedForces::setup(const InteractionDefinitions& domainIdef, const int numAtomsForce, const bool useGpu)
153 if (interactionSelection_.all())
155 // Avoid the overhead of copying all interaction lists by simply setting the reference to the domain idef
160 idef_ = &idefSelection_;
162 selectInteractions(&idefSelection_, domainIdef, interactionSelection_);
164 idefSelection_.ilsort = domainIdef.ilsort;
166 if (interactionSelection_.test(static_cast<int>(ListedForces::InteractionGroup::Rest)))
168 idefSelection_.iparams_posres = domainIdef.iparams_posres;
169 idefSelection_.iparams_fbposres = domainIdef.iparams_fbposres;
173 idefSelection_.iparams_posres.clear();
174 idefSelection_.iparams_fbposres.clear();
178 setup_bonded_threading(threading_.get(), numAtomsForce, useGpu, *idef_);
180 if (idef_->ilsort == ilsortFE_SORTED)
182 forceBufferLambda_.resize(numAtomsForce * sizeof(rvec4) / sizeof(real));
183 shiftForceBufferLambda_.resize(gmx::c_numShiftVectors);
192 /*! \brief Return true if ftype is an explicit pair-listed LJ or
193 * COULOMB interaction type: bonded LJ (usually 1-4), or special
194 * listed non-bonded for FEP. */
195 bool isPairInteraction(int ftype)
197 return ((ftype) >= F_LJ14 && (ftype) <= F_LJC_PAIRS_NB);
200 /*! \brief Returns the bonded kernel flavor
202 * Note that energies are always requested when the virial
203 * is requested (performance gain would be small).
204 * Note that currently we do not have bonded kernels that
205 * do not compute forces.
207 BondedKernelFlavor selectBondedKernelFlavor(const gmx::StepWorkload& stepWork,
208 const bool useSimdKernels,
209 const bool havePerturbedInteractions)
211 BondedKernelFlavor flavor;
212 if (stepWork.computeEnergy || stepWork.computeVirial)
214 if (stepWork.computeVirial)
216 flavor = BondedKernelFlavor::ForcesAndVirialAndEnergy;
220 flavor = BondedKernelFlavor::ForcesAndEnergy;
225 if (useSimdKernels && !havePerturbedInteractions)
227 flavor = BondedKernelFlavor::ForcesSimdWhenAvailable;
231 flavor = BondedKernelFlavor::ForcesNoSimd;
238 /*! \brief Calculate one element of the list of bonded interactions
240 real calc_one_bond(int thread,
242 const InteractionDefinitions& idef,
243 ArrayRef<const int> iatoms,
244 const int numNonperturbedInteractions,
245 const WorkDivision& workDivision,
249 const t_forcerec* fr,
251 gmx_grppairener_t* grpp,
253 gmx::ArrayRef<const real> lambda,
254 gmx::ArrayRef<real> dvdl,
257 const gmx::StepWorkload& stepWork,
258 int* global_atom_index)
260 GMX_ASSERT(idef.ilsort == ilsortNO_FE || idef.ilsort == ilsortFE_SORTED,
261 "The topology should be marked either as no FE or sorted on FE");
263 const bool havePerturbedInteractions =
264 (idef.ilsort == ilsortFE_SORTED && numNonperturbedInteractions < iatoms.ssize());
265 BondedKernelFlavor flavor =
266 selectBondedKernelFlavor(stepWork, fr->use_simd_kernels, havePerturbedInteractions);
267 FreeEnergyPerturbationCouplingType efptFTYPE;
268 if (IS_RESTRAINT_TYPE(ftype))
270 efptFTYPE = FreeEnergyPerturbationCouplingType::Restraint;
274 efptFTYPE = FreeEnergyPerturbationCouplingType::Bonded;
277 const int nat1 = interaction_function[ftype].nratoms + 1;
278 const int nbonds = iatoms.ssize() / nat1;
280 GMX_ASSERT(fr->listedForcesGpu != nullptr || workDivision.end(ftype) == iatoms.ssize(),
281 "The thread division should match the topology");
283 const int nb0 = workDivision.bound(ftype, thread);
284 const int nbn = workDivision.bound(ftype, thread + 1) - nb0;
286 ArrayRef<const t_iparams> iparams = idef.iparams;
289 if (!isPairInteraction(ftype))
293 /* TODO The execution time for CMAP dihedrals might be
294 nice to account to its own subtimer, but first
295 wallcycle needs to be extended to support calling from
305 lambda[static_cast<int>(efptFTYPE)],
306 &(dvdl[static_cast<int>(efptFTYPE)]),
307 gmx::arrayRefFromArray(md->chargeA, md->nr),
315 v = calculateSimpleBond(ftype,
323 lambda[static_cast<int>(efptFTYPE)],
324 &(dvdl[static_cast<int>(efptFTYPE)]),
325 gmx::arrayRefFromArray(md->chargeA, md->nr),
335 /* TODO The execution time for pairs might be nice to account
336 to its own subtimer, but first wallcycle needs to be
337 extended to support calling from multiple threads. */
348 md->chargeA ? gmx::arrayRefFromArray(md->chargeA, md->nr) : gmx::ArrayRef<real>{},
349 md->chargeB ? gmx::arrayRefFromArray(md->chargeB, md->nr) : gmx::ArrayRef<real>{},
350 md->bPerturbed ? gmx::arrayRefFromArray(md->bPerturbed, md->nr) : gmx::ArrayRef<bool>(),
351 gmx::arrayRefFromArray(md->cENER, md->nr),
354 havePerturbedInteractions,
362 inc_nrnb(nrnb, nrnbIndex(ftype), nbonds);
370 /*! \brief Compute the bonded part of the listed forces, parallelized over threads
372 static void calcBondedForces(const InteractionDefinitions& idef,
373 bonded_threading_t* bt,
375 const t_forcerec* fr,
376 const t_pbc* pbc_null,
377 rvec* fshiftMasterBuffer,
378 gmx_enerdata_t* enerd,
380 gmx::ArrayRef<const real> lambda,
381 gmx::ArrayRef<real> dvdl,
384 const gmx::StepWorkload& stepWork,
385 int* global_atom_index)
387 #pragma omp parallel for num_threads(bt->nthreads) schedule(static)
388 for (int thread = 0; thread < bt->nthreads; thread++)
392 auto& threadBuffer = bt->threadedForceBuffer.threadForceBuffer(thread);
395 gmx::ArrayRef<real> dvdlt;
396 gmx::ArrayRef<real> epot;
397 gmx_grppairener_t* grpp;
399 threadBuffer.clearForcesAndEnergies();
401 rvec4* ft = threadBuffer.forceBuffer();
403 /* Thread 0 writes directly to the main output buffers.
404 * We might want to reconsider this.
408 fshift = fshiftMasterBuffer;
415 fshift = as_rvec_array(threadBuffer.shiftForces().data());
416 epot = threadBuffer.energyTerms();
417 grpp = &threadBuffer.groupPairEnergies();
418 dvdlt = threadBuffer.dvdl();
420 /* Loop over all bonded force types to calculate the bonded forces */
421 for (int ftype = 0; (ftype < F_NRE); ftype++)
423 const InteractionList& ilist = idef.il[ftype];
424 if (!ilist.empty() && ftype_is_bonded_potential(ftype))
426 ArrayRef<const int> iatoms = gmx::makeConstArrayRef(ilist.iatoms);
428 real v = calc_one_bond(thread,
432 idef.numNonperturbedInteractions[ftype],
451 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
455 bool ListedForces::haveRestraints(const t_fcdata& fcdata) const
457 GMX_ASSERT(fcdata.disres, "NMR distance restraint object should be set up");
459 return (!idef_->il[F_POSRES].empty() || !idef_->il[F_FBPOSRES].empty() || fcdata.orires
460 || fcdata.disres->nres > 0);
463 bool ListedForces::haveCpuBondeds() const
465 return threading_->haveBondeds;
468 bool ListedForces::haveCpuListedForces(const t_fcdata& fcdata) const
470 return haveCpuBondeds() || haveRestraints(fcdata);
476 /*! \brief Calculates all listed force interactions. */
477 void calc_listed(struct gmx_wallcycle* wcycle,
478 const InteractionDefinitions& idef,
479 bonded_threading_t* bt,
481 gmx::ForceOutputs* forceOutputs,
482 const t_forcerec* fr,
484 gmx_enerdata_t* enerd,
486 gmx::ArrayRef<const real> lambda,
489 int* global_atom_index,
490 const gmx::StepWorkload& stepWork)
494 gmx::ForceWithShiftForces& forceWithShiftForces = forceOutputs->forceWithShiftForces();
496 wallcycle_sub_start(wcycle, WallCycleSubCounter::Listed);
497 /* The dummy array is to have a place to store the dhdl at other values
498 of lambda, which will be thrown away in the end */
499 gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, real> dvdl = { 0 };
500 calcBondedForces(idef,
504 fr->bMolPBC ? pbc : nullptr,
505 as_rvec_array(forceWithShiftForces.shiftForces().data()),
514 wallcycle_sub_stop(wcycle, WallCycleSubCounter::Listed);
516 wallcycle_sub_start(wcycle, WallCycleSubCounter::ListedBufOps);
517 bt->threadedForceBuffer.reduce(
518 &forceWithShiftForces, enerd->term.data(), &enerd->grpp, dvdl, stepWork, 1);
520 if (stepWork.computeDhdl)
522 for (auto i : keysOf(enerd->dvdl_lin))
524 enerd->dvdl_nonlin[i] += dvdl[i];
527 wallcycle_sub_stop(wcycle, WallCycleSubCounter::ListedBufOps);
530 /* Copy the sum of violations for the distance restraints from fcd */
533 enerd->term[F_DISRESVIOL] = fcd->disres->sumviol;
537 /*! \brief As calc_listed(), but only determines the potential energy
538 * for the perturbed interactions.
540 * The shift forces in fr are not affected.
542 void calc_listed_lambda(const InteractionDefinitions& idef,
543 bonded_threading_t* bt,
545 const t_forcerec* fr,
546 const struct t_pbc* pbc,
547 gmx::ArrayRef<real> forceBufferLambda,
548 gmx::ArrayRef<gmx::RVec> shiftForceBufferLambda,
549 gmx_grppairener_t* grpp,
550 gmx::ArrayRef<real> epot,
551 gmx::ArrayRef<real> dvdl,
553 gmx::ArrayRef<const real> lambda,
556 int* global_atom_index)
558 WorkDivision& workDivision = bt->foreignLambdaWorkDivision;
560 const t_pbc* pbc_null;
570 /* We already have the forces, so we use temp buffers here */
571 std::fill(forceBufferLambda.begin(), forceBufferLambda.end(), 0.0_real);
572 std::fill(shiftForceBufferLambda.begin(),
573 shiftForceBufferLambda.end(),
574 gmx::RVec{ 0.0_real, 0.0_real, 0.0_real });
575 rvec4* f = reinterpret_cast<rvec4*>(forceBufferLambda.data());
576 rvec* fshift = as_rvec_array(shiftForceBufferLambda.data());
578 /* Loop over all bonded force types to calculate the bonded energies */
579 for (int ftype = 0; (ftype < F_NRE); ftype++)
581 if (ftype_is_bonded_potential(ftype))
583 const InteractionList& ilist = idef.il[ftype];
584 /* Create a temporary iatom list with only perturbed interactions */
585 const int numNonperturbed = idef.numNonperturbedInteractions[ftype];
586 ArrayRef<const int> iatomsPerturbed = gmx::constArrayRefFromArray(
587 ilist.iatoms.data() + numNonperturbed, ilist.size() - numNonperturbed);
588 if (!iatomsPerturbed.empty())
590 /* Set the work range of thread 0 to the perturbed bondeds */
591 workDivision.setBound(ftype, 0, 0);
592 workDivision.setBound(ftype, 1, iatomsPerturbed.ssize());
594 gmx::StepWorkload tempFlags;
595 tempFlags.computeEnergy = true;
596 real v = calc_one_bond(0,
600 iatomsPerturbed.ssize(),
623 void ListedForces::calculate(struct gmx_wallcycle* wcycle,
625 const t_lambda* fepvals,
627 const gmx_multisim_t* ms,
628 gmx::ArrayRefWithPadding<const gmx::RVec> coordinates,
629 gmx::ArrayRef<const gmx::RVec> xWholeMolecules,
631 const history_t* hist,
632 gmx::ForceOutputs* forceOutputs,
633 const t_forcerec* fr,
634 const struct t_pbc* pbc,
635 gmx_enerdata_t* enerd,
637 gmx::ArrayRef<const real> lambda,
639 int* global_atom_index,
640 const gmx::StepWorkload& stepWork)
642 if (interactionSelection_.none() || !stepWork.computeListedForces)
647 const InteractionDefinitions& idef = *idef_;
649 // Todo: replace all rvec use here with ArrayRefWithPadding
650 const rvec* x = as_rvec_array(coordinates.paddedArrayRef().data());
652 const bool calculateRestInteractions =
653 interactionSelection_.test(static_cast<int>(ListedForces::InteractionGroup::Rest));
655 t_pbc pbc_full; /* Full PBC is needed for position restraints */
656 if (calculateRestInteractions && haveRestraints(*fcdata))
658 if (!idef.il[F_POSRES].empty() || !idef.il[F_FBPOSRES].empty())
660 /* Not enough flops to bother counting */
661 set_pbc(&pbc_full, fr->pbcType, box);
664 /* TODO Use of restraints triggers further function calls
665 inside the loop over calc_one_bond(), but those are too
666 awkward to account to this subtimer properly in the present
667 code. We don't test / care much about performance with
668 restraints, anyway. */
669 wallcycle_sub_start(wcycle, WallCycleSubCounter::Restraints);
671 if (!idef.il[F_POSRES].empty())
673 posres_wrapper(nrnb, idef, &pbc_full, x, enerd, lambda, fr, &forceOutputs->forceWithVirial());
676 if (!idef.il[F_FBPOSRES].empty())
678 fbposres_wrapper(nrnb, idef, &pbc_full, x, enerd, fr, &forceOutputs->forceWithVirial());
681 /* Do pre force calculation stuff which might require communication */
684 GMX_ASSERT(!xWholeMolecules.empty(), "Need whole molecules for orienation restraints");
685 enerd->term[F_ORIRESDEV] = calc_orires_dev(ms,
686 idef.il[F_ORIRES].size(),
687 idef.il[F_ORIRES].iatoms.data(),
691 fr->bMolPBC ? pbc : nullptr,
692 fcdata->orires.get());
694 if (fcdata->disres->nres > 0)
698 idef.il[F_DISRES].size(),
699 idef.il[F_DISRES].iatoms.data(),
701 fr->bMolPBC ? pbc : nullptr,
706 wallcycle_sub_stop(wcycle, WallCycleSubCounter::Restraints);
709 calc_listed(wcycle, idef, threading_.get(), x, forceOutputs, fr, pbc, enerd, nrnb, lambda, md, fcdata, global_atom_index, stepWork);
711 /* Check if we have to determine energy differences
712 * at foreign lambda's.
714 if (fepvals->n_lambda > 0 && stepWork.computeDhdl)
716 gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, real> dvdl = { 0 };
717 if (!idef.il[F_POSRES].empty())
719 posres_wrapper_lambda(wcycle, fepvals, idef, &pbc_full, x, enerd, lambda, fr);
721 if (idef.ilsort != ilsortNO_FE)
723 wallcycle_sub_start(wcycle, WallCycleSubCounter::ListedFep);
724 if (idef.ilsort != ilsortFE_SORTED)
726 gmx_incons("The bonded interactions are not sorted for free energy");
728 for (int i = 0; i < 1 + enerd->foreignLambdaTerms.numLambdas(); i++)
730 gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, real> lam_i;
731 foreignEnergyGroups_->clear();
732 std::array<real, F_NRE> foreign_term = { 0 };
733 for (auto j : keysOf(lam_i))
735 lam_i[j] = (i == 0 ? lambda[static_cast<int>(j)] : fepvals->all_lambda[j][i - 1]);
737 calc_listed_lambda(idef,
743 shiftForceBufferLambda_,
744 foreignEnergyGroups_.get(),
752 sum_epot(*foreignEnergyGroups_, foreign_term.data());
753 const double dvdlSum = std::accumulate(std::begin(dvdl), std::end(dvdl), 0.);
754 std::fill(std::begin(dvdl), std::end(dvdl), 0.0);
755 enerd->foreignLambdaTerms.accumulate(i, foreign_term[F_EPOT], dvdlSum);
757 wallcycle_sub_stop(wcycle, WallCycleSubCounter::ListedFep);