2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
36 * \brief Defines the propagator element for the modular simulator
38 * \author Pascal Merz <pascal.merz@me.com>
39 * \ingroup module_modularsimulator
44 #include "propagator.h"
46 #include "gromacs/utility.h"
47 #include "gromacs/math/vec.h"
48 #include "gromacs/math/vectypes.h"
49 #include "gromacs/mdlib/gmx_omp_nthreads.h"
50 #include "gromacs/mdlib/mdatoms.h"
51 #include "gromacs/mdlib/update.h"
52 #include "gromacs/mdtypes/inputrec.h"
53 #include "gromacs/mdtypes/mdatom.h"
54 #include "gromacs/timing/wallcycle.h"
55 #include "gromacs/utility/fatalerror.h"
57 #include "modularsimulator.h"
58 #include "simulatoralgorithm.h"
59 #include "statepropagatordata.h"
64 template<NumVelocityScalingValues numStartVelocityScalingValues,
65 ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling,
66 NumVelocityScalingValues numEndVelocityScalingValues>
67 static void inline updateVelocities(int a,
71 const rvec* gmx_restrict invMassPerDim,
73 const rvec* gmx_restrict f,
75 const matrix matrixPR)
77 for (int d = 0; d < DIM; d++)
79 // TODO: Extract this into policy classes
80 if (numStartVelocityScalingValues != NumVelocityScalingValues::None
81 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
83 v[a][d] *= lambdaStart;
85 if (numStartVelocityScalingValues != NumVelocityScalingValues::None
86 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Diagonal)
88 v[a][d] *= (lambdaStart - diagPR[d]);
90 if (numStartVelocityScalingValues != NumVelocityScalingValues::None
91 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Full)
93 v[a][d] = lambdaStart * v[a][d] - iprod(matrixPR[d], v[a]);
95 if (numStartVelocityScalingValues == NumVelocityScalingValues::None
96 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Diagonal)
98 v[a][d] *= (1 - diagPR[d]);
100 if (numStartVelocityScalingValues == NumVelocityScalingValues::None
101 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Full)
103 v[a][d] -= iprod(matrixPR[d], v[a]);
105 v[a][d] += f[a][d] * invMassPerDim[a][d] * dt;
106 if (numEndVelocityScalingValues != NumVelocityScalingValues::None)
108 v[a][d] *= lambdaEnd;
114 static void inline updatePositions(int a,
116 const rvec* gmx_restrict x,
117 rvec* gmx_restrict xprime,
118 const rvec* gmx_restrict v)
120 for (int d = 0; d < DIM; d++)
122 xprime[a][d] = x[a][d] + v[a][d] * dt;
126 //! Helper function diagonalizing the PR matrix if possible
127 template<ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
128 static inline bool diagonalizePRMatrix(matrix matrixPR, rvec diagPR)
130 if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::Full)
136 if (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0)
138 diagPR[XX] = matrixPR[XX][XX];
139 diagPR[YY] = matrixPR[YY][YY];
140 diagPR[ZZ] = matrixPR[ZZ][ZZ];
150 //! Propagation (position only)
152 template<NumVelocityScalingValues numStartVelocityScalingValues,
153 ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling,
154 NumVelocityScalingValues numEndVelocityScalingValues>
155 void Propagator<IntegrationStep::PositionsOnly>::run()
157 wallcycle_start(wcycle_, ewcUPDATE);
159 auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
160 auto x = as_rvec_array(statePropagatorData_->constPositionsView().paddedArrayRef().data());
161 auto v = as_rvec_array(statePropagatorData_->constVelocitiesView().paddedArrayRef().data());
163 int nth = gmx_omp_nthreads_get(emntUpdate);
164 int homenr = mdAtoms_->mdatoms()->homenr;
166 #pragma omp parallel for num_threads(nth) schedule(static) default(none) shared(nth, homenr, x, xp, v)
167 for (int th = 0; th < nth; th++)
171 int start_th, end_th;
172 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
174 for (int a = start_th; a < end_th; a++)
176 updatePositions(a, timestep_, x, xp, v);
179 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
181 wallcycle_stop(wcycle_, ewcUPDATE);
184 //! Propagation (velocity only)
186 template<NumVelocityScalingValues numStartVelocityScalingValues,
187 ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling,
188 NumVelocityScalingValues numEndVelocityScalingValues>
189 void Propagator<IntegrationStep::VelocitiesOnly>::run()
191 wallcycle_start(wcycle_, ewcUPDATE);
193 auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
194 auto f = as_rvec_array(statePropagatorData_->constForcesView().force().data());
195 auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
197 const real lambdaStart = (numStartVelocityScalingValues == NumVelocityScalingValues::Single)
198 ? startVelocityScaling_[0]
200 const real lambdaEnd = (numEndVelocityScalingValues == NumVelocityScalingValues::Single)
201 ? endVelocityScaling_[0]
204 const bool isFullScalingMatrixDiagonal =
205 diagonalizePRMatrix<parrinelloRahmanVelocityScaling>(matrixPR_, diagPR_);
207 const int nth = gmx_omp_nthreads_get(emntUpdate);
208 const int homenr = mdAtoms_->mdatoms()->homenr;
210 // const variables could be shared, but gcc-8 & gcc-9 don't agree how to write that...
211 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
212 #pragma omp parallel for num_threads(nth) schedule(static) default(none) shared(v, f, invMassPerDim) \
213 firstprivate(nth, homenr, lambdaStart, lambdaEnd, isFullScalingMatrixDiagonal)
214 for (int th = 0; th < nth; th++)
218 int start_th, end_th;
219 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
221 for (int a = start_th; a < end_th; a++)
223 if (isFullScalingMatrixDiagonal)
225 updateVelocities<numStartVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal, numEndVelocityScalingValues>(
228 numStartVelocityScalingValues == NumVelocityScalingValues::Multiple
229 ? startVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
231 numEndVelocityScalingValues == NumVelocityScalingValues::Multiple
232 ? endVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
242 updateVelocities<numStartVelocityScalingValues, parrinelloRahmanVelocityScaling, numEndVelocityScalingValues>(
245 numStartVelocityScalingValues == NumVelocityScalingValues::Multiple
246 ? startVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
248 numEndVelocityScalingValues == NumVelocityScalingValues::Multiple
249 ? endVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
259 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
261 wallcycle_stop(wcycle_, ewcUPDATE);
264 //! Propagation (leapfrog case - position and velocity)
266 template<NumVelocityScalingValues numStartVelocityScalingValues,
267 ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling,
268 NumVelocityScalingValues numEndVelocityScalingValues>
269 void Propagator<IntegrationStep::LeapFrog>::run()
271 wallcycle_start(wcycle_, ewcUPDATE);
273 auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
274 auto x = as_rvec_array(statePropagatorData_->constPositionsView().paddedArrayRef().data());
275 auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
276 auto f = as_rvec_array(statePropagatorData_->constForcesView().force().data());
277 auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
279 const real lambdaStart = (numStartVelocityScalingValues == NumVelocityScalingValues::Single)
280 ? startVelocityScaling_[0]
282 const real lambdaEnd = (numEndVelocityScalingValues == NumVelocityScalingValues::Single)
283 ? endVelocityScaling_[0]
286 const bool isFullScalingMatrixDiagonal =
287 diagonalizePRMatrix<parrinelloRahmanVelocityScaling>(matrixPR_, diagPR_);
289 const int nth = gmx_omp_nthreads_get(emntUpdate);
290 const int homenr = mdAtoms_->mdatoms()->homenr;
292 // const variables could be shared, but gcc-8 & gcc-9 don't agree how to write that...
293 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
294 #pragma omp parallel for num_threads(nth) schedule(static) default(none) \
295 shared(x, xp, v, f, invMassPerDim) \
296 firstprivate(nth, homenr, lambdaStart, lambdaEnd, isFullScalingMatrixDiagonal)
297 for (int th = 0; th < nth; th++)
301 int start_th, end_th;
302 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
304 for (int a = start_th; a < end_th; a++)
306 if (isFullScalingMatrixDiagonal)
308 updateVelocities<numStartVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal, numEndVelocityScalingValues>(
311 numStartVelocityScalingValues == NumVelocityScalingValues::Multiple
312 ? startVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
314 numEndVelocityScalingValues == NumVelocityScalingValues::Multiple
315 ? endVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
325 updateVelocities<numStartVelocityScalingValues, parrinelloRahmanVelocityScaling, numEndVelocityScalingValues>(
328 numStartVelocityScalingValues == NumVelocityScalingValues::Multiple
329 ? startVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
331 numEndVelocityScalingValues == NumVelocityScalingValues::Multiple
332 ? endVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
340 updatePositions(a, timestep_, x, xp, v);
343 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
345 wallcycle_stop(wcycle_, ewcUPDATE);
348 //! Propagation (velocity verlet stage 2 - velocity and position)
350 template<NumVelocityScalingValues numStartVelocityScalingValues,
351 ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling,
352 NumVelocityScalingValues numEndVelocityScalingValues>
353 void Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>::run()
355 wallcycle_start(wcycle_, ewcUPDATE);
357 auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
358 auto x = as_rvec_array(statePropagatorData_->constPositionsView().paddedArrayRef().data());
359 auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
360 auto f = as_rvec_array(statePropagatorData_->constForcesView().force().data());
361 auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
363 const real lambdaStart = (numStartVelocityScalingValues == NumVelocityScalingValues::Single)
364 ? startVelocityScaling_[0]
366 const real lambdaEnd = (numEndVelocityScalingValues == NumVelocityScalingValues::Single)
367 ? endVelocityScaling_[0]
370 const bool isFullScalingMatrixDiagonal =
371 diagonalizePRMatrix<parrinelloRahmanVelocityScaling>(matrixPR_, diagPR_);
373 const int nth = gmx_omp_nthreads_get(emntUpdate);
374 const int homenr = mdAtoms_->mdatoms()->homenr;
376 // const variables could be shared, but gcc-8 & gcc-9 don't agree how to write that...
377 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
378 #pragma omp parallel for num_threads(nth) schedule(static) default(none) \
379 shared(x, xp, v, f, invMassPerDim) \
380 firstprivate(nth, homenr, lambdaStart, lambdaEnd, isFullScalingMatrixDiagonal)
381 for (int th = 0; th < nth; th++)
385 int start_th, end_th;
386 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
388 for (int a = start_th; a < end_th; a++)
390 if (isFullScalingMatrixDiagonal)
392 updateVelocities<numStartVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal, numEndVelocityScalingValues>(
395 numStartVelocityScalingValues == NumVelocityScalingValues::Multiple
396 ? startVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
398 numEndVelocityScalingValues == NumVelocityScalingValues::Multiple
399 ? endVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
409 updateVelocities<numStartVelocityScalingValues, parrinelloRahmanVelocityScaling, numEndVelocityScalingValues>(
412 numStartVelocityScalingValues == NumVelocityScalingValues::Multiple
413 ? startVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
415 numEndVelocityScalingValues == NumVelocityScalingValues::Multiple
416 ? endVelocityScaling_[mdAtoms_->mdatoms()->cTC[a]]
424 updatePositions(a, timestep_, x, xp, v);
427 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
429 wallcycle_stop(wcycle_, ewcUPDATE);
432 template<IntegrationStep algorithm>
433 Propagator<algorithm>::Propagator(double timestep,
434 StatePropagatorData* statePropagatorData,
435 const MDAtoms* mdAtoms,
436 gmx_wallcycle* wcycle) :
438 statePropagatorData_(statePropagatorData),
439 doSingleStartVelocityScaling_(false),
440 doGroupStartVelocityScaling_(false),
441 doSingleEndVelocityScaling_(false),
442 doGroupEndVelocityScaling_(false),
443 scalingStepVelocity_(-1),
452 template<IntegrationStep algorithm>
453 void Propagator<algorithm>::scheduleTask(Step gmx_unused step,
454 Time gmx_unused time,
455 const RegisterRunFunction& registerRunFunction)
457 const bool doSingleVScalingThisStep =
458 (doSingleStartVelocityScaling_ && (step == scalingStepVelocity_));
459 const bool doGroupVScalingThisStep = (doGroupStartVelocityScaling_ && (step == scalingStepVelocity_));
461 const bool doParrinelloRahmanThisStep = (step == scalingStepPR_);
463 if (doSingleVScalingThisStep)
465 if (doParrinelloRahmanThisStep)
467 if (doSingleEndVelocityScaling_)
469 registerRunFunction([this]() {
470 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::Full, NumVelocityScalingValues::Single>();
475 registerRunFunction([this]() {
476 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::Full, NumVelocityScalingValues::None>();
482 if (doSingleEndVelocityScaling_)
484 registerRunFunction([this]() {
485 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::No, NumVelocityScalingValues::Single>();
490 registerRunFunction([this]() {
491 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::No, NumVelocityScalingValues::None>();
496 else if (doGroupVScalingThisStep)
498 if (doParrinelloRahmanThisStep)
500 if (doGroupEndVelocityScaling_)
502 registerRunFunction([this]() {
503 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full, NumVelocityScalingValues::Multiple>();
508 registerRunFunction([this]() {
509 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full, NumVelocityScalingValues::None>();
515 if (doGroupEndVelocityScaling_)
517 registerRunFunction([this]() {
518 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No, NumVelocityScalingValues::Multiple>();
523 registerRunFunction([this]() {
524 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No, NumVelocityScalingValues::None>();
531 if (doParrinelloRahmanThisStep)
533 registerRunFunction([this]() {
534 run<NumVelocityScalingValues::None, ParrinelloRahmanVelocityScaling::Full, NumVelocityScalingValues::None>();
539 registerRunFunction([this]() {
540 run<NumVelocityScalingValues::None, ParrinelloRahmanVelocityScaling::No, NumVelocityScalingValues::None>();
546 template<IntegrationStep algorithm>
547 void Propagator<algorithm>::setNumVelocityScalingVariables(int numVelocityScalingVariables,
548 ScaleVelocities scaleVelocities)
550 if (algorithm == IntegrationStep::PositionsOnly)
552 gmx_fatal(FARGS, "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
554 GMX_ASSERT(startVelocityScaling_.empty(),
555 "Number of velocity scaling variables cannot be changed once set.");
557 const bool scaleEndVelocities = (scaleVelocities == ScaleVelocities::PreStepAndPostStep);
558 startVelocityScaling_.resize(numVelocityScalingVariables, 1.);
559 if (scaleEndVelocities)
561 endVelocityScaling_.resize(numVelocityScalingVariables, 1.);
563 doSingleStartVelocityScaling_ = numVelocityScalingVariables == 1;
564 doGroupStartVelocityScaling_ = numVelocityScalingVariables > 1;
565 doSingleEndVelocityScaling_ = doSingleStartVelocityScaling_ && scaleEndVelocities;
566 doGroupEndVelocityScaling_ = doGroupStartVelocityScaling_ && scaleEndVelocities;
569 template<IntegrationStep algorithm>
570 ArrayRef<real> Propagator<algorithm>::viewOnStartVelocityScaling()
572 GMX_RELEASE_ASSERT(algorithm != IntegrationStep::PositionsOnly,
573 "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
574 GMX_RELEASE_ASSERT(!startVelocityScaling_.empty(),
575 "Number of velocity scaling variables not set.");
577 return startVelocityScaling_;
580 template<IntegrationStep algorithm>
581 ArrayRef<real> Propagator<algorithm>::viewOnEndVelocityScaling()
583 GMX_RELEASE_ASSERT(algorithm != IntegrationStep::PositionsOnly,
584 "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
585 GMX_RELEASE_ASSERT(!endVelocityScaling_.empty(),
586 "Number of velocity scaling variables not set.");
588 return endVelocityScaling_;
591 template<IntegrationStep algorithm>
592 PropagatorCallback Propagator<algorithm>::velocityScalingCallback()
594 if (algorithm == IntegrationStep::PositionsOnly)
596 gmx_fatal(FARGS, "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
599 return [this](Step step) { scalingStepVelocity_ = step; };
602 template<IntegrationStep algorithm>
603 ArrayRef<rvec> Propagator<algorithm>::viewOnPRScalingMatrix()
606 algorithm != IntegrationStep::PositionsOnly,
607 "Parrinello-Rahman scaling not implemented for IntegrationStep::PositionsOnly.");
609 clear_mat(matrixPR_);
610 // gcc-5 needs this to be explicit (all other tested compilers would be ok
611 // with simply returning matrixPR)
612 return ArrayRef<rvec>(matrixPR_);
615 template<IntegrationStep algorithm>
616 PropagatorCallback Propagator<algorithm>::prScalingCallback()
619 algorithm != IntegrationStep::PositionsOnly,
620 "Parrinello-Rahman scaling not implemented for IntegrationStep::PositionsOnly.");
622 return [this](Step step) { scalingStepPR_ = step; };
625 template<IntegrationStep algorithm>
626 ISimulatorElement* Propagator<algorithm>::getElementPointerImpl(
627 LegacySimulatorData* legacySimulatorData,
628 ModularSimulatorAlgorithmBuilderHelper* builderHelper,
629 StatePropagatorData* statePropagatorData,
630 EnergyData gmx_unused* energyData,
631 FreeEnergyPerturbationData gmx_unused* freeEnergyPerturbationData,
632 GlobalCommunicationHelper gmx_unused* globalCommunicationHelper,
633 const PropagatorTag& propagatorTag,
636 auto* element = builderHelper->storeElement(std::make_unique<Propagator<algorithm>>(
637 timestep, statePropagatorData, legacySimulatorData->mdAtoms, legacySimulatorData->wcycle));
638 auto* propagator = static_cast<Propagator<algorithm>*>(element);
639 builderHelper->registerWithThermostat(
640 { [propagator](int num, ScaleVelocities scaleVelocities) {
641 propagator->setNumVelocityScalingVariables(num, scaleVelocities);
643 [propagator]() { return propagator->viewOnStartVelocityScaling(); },
644 [propagator]() { return propagator->viewOnEndVelocityScaling(); },
645 [propagator]() { return propagator->velocityScalingCallback(); },
647 builderHelper->registerWithBarostat(
648 { [propagator]() { return propagator->viewOnPRScalingMatrix(); },
649 [propagator]() { return propagator->prScalingCallback(); },
654 // Explicit template initializations
655 template class Propagator<IntegrationStep::PositionsOnly>;
656 template class Propagator<IntegrationStep::VelocitiesOnly>;
657 template class Propagator<IntegrationStep::LeapFrog>;
658 template class Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>;