2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2019,2020, 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/mdatom.h"
53 #include "gromacs/timing/wallcycle.h"
54 #include "gromacs/utility/fatalerror.h"
56 #include "statepropagatordata.h"
61 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
62 static void inline updateVelocities(int a,
65 const rvec* gmx_restrict invMassPerDim,
67 const rvec* gmx_restrict f,
69 const matrix matrixPR)
71 for (int d = 0; d < DIM; d++)
73 // TODO: Extract this into policy classes
74 if (numVelocityScalingValues != NumVelocityScalingValues::None
75 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
79 if (numVelocityScalingValues != NumVelocityScalingValues::None
80 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Diagonal)
82 v[a][d] *= (lambda - diagPR[d]);
84 if (numVelocityScalingValues != NumVelocityScalingValues::None
85 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Full)
87 v[a][d] = lambda * v[a][d] - iprod(matrixPR[d], v[a]);
89 if (numVelocityScalingValues == NumVelocityScalingValues::None
90 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Diagonal)
92 v[a][d] *= (1 - diagPR[d]);
94 if (numVelocityScalingValues == NumVelocityScalingValues::None
95 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Full)
97 v[a][d] -= iprod(matrixPR[d], v[a]);
99 v[a][d] += f[a][d] * invMassPerDim[a][d] * dt;
104 static void inline updatePositions(int a,
106 const rvec* gmx_restrict x,
107 rvec* gmx_restrict xprime,
108 const rvec* gmx_restrict v)
110 for (int d = 0; d < DIM; d++)
112 xprime[a][d] = x[a][d] + v[a][d] * dt;
116 //! Propagation (position only)
118 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
119 void Propagator<IntegrationStep::PositionsOnly>::run()
121 wallcycle_start(wcycle_, ewcUPDATE);
123 auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
124 auto x = as_rvec_array(statePropagatorData_->constPreviousPositionsView().paddedArrayRef().data());
125 auto v = as_rvec_array(statePropagatorData_->constVelocitiesView().paddedArrayRef().data());
127 int nth = gmx_omp_nthreads_get(emntUpdate);
128 int homenr = mdAtoms_->mdatoms()->homenr;
130 #pragma omp parallel for num_threads(nth) schedule(static) default(none) shared(nth, homenr, x, xp, v)
131 for (int th = 0; th < nth; th++)
135 int start_th, end_th;
136 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
138 for (int a = start_th; a < end_th; a++)
140 updatePositions(a, timestep_, x, xp, v);
143 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
145 wallcycle_stop(wcycle_, ewcUPDATE);
148 //! Propagation (velocity only)
150 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
151 void Propagator<IntegrationStep::VelocitiesOnly>::run()
153 wallcycle_start(wcycle_, ewcUPDATE);
155 auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
156 auto f = as_rvec_array(statePropagatorData_->constForcesView().paddedArrayRef().data());
157 auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
160 (numVelocityScalingValues == NumVelocityScalingValues::Single) ? velocityScaling_[0] : 1.0;
162 bool doDiagonalScaling = false;
163 if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::No)
165 // TODO: Could we know in advance whether the matrix is diagonal?
166 doDiagonalScaling = (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0);
167 if (doDiagonalScaling)
169 diagPR[XX] = matrixPR[XX][XX];
170 diagPR[YY] = matrixPR[YY][YY];
171 diagPR[ZZ] = matrixPR[ZZ][ZZ];
175 int nth = gmx_omp_nthreads_get(emntUpdate);
176 int homenr = mdAtoms_->mdatoms()->homenr;
178 // lambda could be shared, but gcc-8 & gcc-9 don't agree how to write that...
179 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
180 #pragma omp parallel for num_threads(nth) schedule(static) default(none) \
181 shared(nth, homenr, v, f, invMassPerDim, doDiagonalScaling) firstprivate(lambda)
182 for (int th = 0; th < nth; th++)
186 int start_th, end_th;
187 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
189 for (int a = start_th; a < end_th; a++)
191 if (parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
193 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
195 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>(
196 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
197 invMassPerDim, v, f, diagPR, matrixPR);
201 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::No>(
202 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
207 if (doDiagonalScaling)
209 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
211 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Diagonal>(
212 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
213 invMassPerDim, v, f, diagPR, matrixPR);
217 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
218 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
223 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
225 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>(
226 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
227 invMassPerDim, v, f, diagPR, matrixPR);
231 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Full>(
232 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
238 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
240 wallcycle_stop(wcycle_, ewcUPDATE);
243 //! Propagation (leapfrog case - position and velocity)
245 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
246 void Propagator<IntegrationStep::LeapFrog>::run()
248 wallcycle_start(wcycle_, ewcUPDATE);
250 auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
251 auto x = as_rvec_array(statePropagatorData_->constPreviousPositionsView().paddedArrayRef().data());
252 auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
253 auto f = as_rvec_array(statePropagatorData_->constForcesView().paddedArrayRef().data());
254 auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
257 (numVelocityScalingValues == NumVelocityScalingValues::Single) ? velocityScaling_[0] : 1.0;
259 bool doDiagonalScaling = false;
260 if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::No)
262 // TODO: Could we know in advance whether the matrix is diagonal?
263 doDiagonalScaling = (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0);
264 if (doDiagonalScaling)
266 diagPR[XX] = matrixPR[XX][XX];
267 diagPR[YY] = matrixPR[YY][YY];
268 diagPR[ZZ] = matrixPR[ZZ][ZZ];
272 int nth = gmx_omp_nthreads_get(emntUpdate);
273 int homenr = mdAtoms_->mdatoms()->homenr;
275 // lambda could be shared, but gcc-8 & gcc-9 don't agree how to write that...
276 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
277 #pragma omp parallel for num_threads(nth) schedule(static) default(none) \
278 shared(nth, homenr, x, xp, v, f, invMassPerDim, doDiagonalScaling) firstprivate(lambda)
279 for (int th = 0; th < nth; th++)
283 int start_th, end_th;
284 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
286 for (int a = start_th; a < end_th; a++)
288 if (parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
290 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
292 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>(
293 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
294 invMassPerDim, v, f, diagPR, matrixPR);
298 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::No>(
299 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
304 if (doDiagonalScaling)
306 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
308 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Diagonal>(
309 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
310 invMassPerDim, v, f, diagPR, matrixPR);
314 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
315 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
320 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
322 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>(
323 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
324 invMassPerDim, v, f, diagPR, matrixPR);
328 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Full>(
329 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
333 updatePositions(a, timestep_, x, xp, v);
336 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
338 wallcycle_stop(wcycle_, ewcUPDATE);
341 //! Propagation (velocity verlet stage 2 - velocity and position)
343 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
344 void Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>::run()
346 wallcycle_start(wcycle_, ewcUPDATE);
348 auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
349 auto x = as_rvec_array(statePropagatorData_->constPreviousPositionsView().paddedArrayRef().data());
350 auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
351 auto f = as_rvec_array(statePropagatorData_->constForcesView().paddedArrayRef().data());
352 auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
354 int nth = gmx_omp_nthreads_get(emntUpdate);
355 int homenr = mdAtoms_->mdatoms()->homenr;
358 (numVelocityScalingValues == NumVelocityScalingValues::Single) ? velocityScaling_[0] : 1.0;
360 bool doDiagonalScaling = false;
361 if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::No)
363 // TODO: Could we know in advance whether the matrix is diagonal?
364 doDiagonalScaling = (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0);
365 if (doDiagonalScaling)
367 diagPR[XX] = matrixPR[XX][XX];
368 diagPR[YY] = matrixPR[YY][YY];
369 diagPR[ZZ] = matrixPR[ZZ][ZZ];
373 // lambda could be shared, but gcc-8 & gcc-9 don't agree how to write that...
374 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
375 #pragma omp parallel for num_threads(nth) schedule(static) default(none) \
376 shared(nth, homenr, x, xp, v, f, invMassPerDim, doDiagonalScaling) firstprivate(lambda)
377 for (int th = 0; th < nth; th++)
381 int start_th, end_th;
382 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
384 for (int a = start_th; a < end_th; a++)
386 if (parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
388 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
390 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>(
391 a, 0.5 * timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
392 invMassPerDim, v, f, diagPR, matrixPR);
396 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::No>(
397 a, 0.5 * timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
402 if (doDiagonalScaling)
404 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
406 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Diagonal>(
407 a, 0.5 * timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
408 invMassPerDim, v, f, diagPR, matrixPR);
412 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
413 a, 0.5 * timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
418 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
420 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>(
421 a, 0.5 * timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
422 invMassPerDim, v, f, diagPR, matrixPR);
426 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Full>(
427 a, 0.5 * timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
431 updatePositions(a, timestep_, x, xp, v);
434 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
436 wallcycle_stop(wcycle_, ewcUPDATE);
439 template<IntegrationStep algorithm>
440 Propagator<algorithm>::Propagator(double timestep,
441 StatePropagatorData* statePropagatorData,
442 const MDAtoms* mdAtoms,
443 gmx_wallcycle* wcycle) :
445 statePropagatorData_(statePropagatorData),
446 doSingleVelocityScaling(false),
447 doGroupVelocityScaling(false),
448 scalingStepVelocity_(-1),
457 template<IntegrationStep algorithm>
458 void Propagator<algorithm>::scheduleTask(Step gmx_unused step,
459 Time gmx_unused time,
460 const RegisterRunFunctionPtr& registerRunFunction)
462 const bool doSingleVScalingThisStep = (doSingleVelocityScaling && (step == scalingStepVelocity_));
463 const bool doGroupVScalingThisStep = (doGroupVelocityScaling && (step == scalingStepVelocity_));
465 const bool doParrinelloRahmanThisStep = (step == scalingStepPR_);
467 if (doSingleVScalingThisStep)
469 if (doParrinelloRahmanThisStep)
471 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
472 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::Full>();
477 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
478 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::No>();
482 else if (doGroupVScalingThisStep)
484 if (doParrinelloRahmanThisStep)
486 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
487 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>();
492 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
493 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>();
499 if (doParrinelloRahmanThisStep)
501 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
502 run<NumVelocityScalingValues::None, ParrinelloRahmanVelocityScaling::Full>();
507 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
508 run<NumVelocityScalingValues::None, ParrinelloRahmanVelocityScaling::No>();
514 template<IntegrationStep algorithm>
515 void Propagator<algorithm>::setNumVelocityScalingVariables(int numVelocityScalingVariables)
517 if (algorithm == IntegrationStep::PositionsOnly)
519 gmx_fatal(FARGS, "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
521 GMX_ASSERT(velocityScaling_.empty(),
522 "Number of velocity scaling variables cannot be changed once set.");
524 velocityScaling_.resize(numVelocityScalingVariables, 1.);
525 doSingleVelocityScaling = numVelocityScalingVariables == 1;
526 doGroupVelocityScaling = numVelocityScalingVariables > 1;
529 template<IntegrationStep algorithm>
530 ArrayRef<real> Propagator<algorithm>::viewOnVelocityScaling()
532 if (algorithm == IntegrationStep::PositionsOnly)
534 gmx_fatal(FARGS, "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
536 GMX_ASSERT(!velocityScaling_.empty(), "Number of velocity scaling variables not set.");
538 return velocityScaling_;
541 template<IntegrationStep algorithm>
542 std::unique_ptr<std::function<void(Step)>> Propagator<algorithm>::velocityScalingCallback()
544 if (algorithm == IntegrationStep::PositionsOnly)
546 gmx_fatal(FARGS, "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
549 return std::make_unique<PropagatorCallback>([this](Step step) { scalingStepVelocity_ = step; });
552 template<IntegrationStep algorithm>
553 ArrayRef<rvec> Propagator<algorithm>::viewOnPRScalingMatrix()
556 algorithm != IntegrationStep::PositionsOnly,
557 "Parrinello-Rahman scaling not implemented for IntegrationStep::PositionsOnly.");
560 // gcc-5 needs this to be explicit (all other tested compilers would be ok
561 // with simply returning matrixPR)
562 return ArrayRef<rvec>(matrixPR);
565 template<IntegrationStep algorithm>
566 PropagatorCallbackPtr Propagator<algorithm>::prScalingCallback()
569 algorithm != IntegrationStep::PositionsOnly,
570 "Parrinello-Rahman scaling not implemented for IntegrationStep::PositionsOnly.");
572 return std::make_unique<PropagatorCallback>([this](Step step) { scalingStepPR_ = step; });
575 //! Explicit template initialization
577 template class Propagator<IntegrationStep::PositionsOnly>;
578 template class Propagator<IntegrationStep::VelocitiesOnly>;
579 template class Propagator<IntegrationStep::LeapFrog>;
580 template class Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>;