2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2019, 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/timing/wallcycle.h"
53 #include "gromacs/utility/fatalerror.h"
55 #include "statepropagatordata.h"
60 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
61 static void inline updateVelocities(int a,
64 const rvec* gmx_restrict invMassPerDim,
66 const rvec* gmx_restrict f,
68 const matrix matrixPR)
70 for (int d = 0; d < DIM; d++)
72 // TODO: Extract this into policy classes
73 if (numVelocityScalingValues != NumVelocityScalingValues::None
74 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
78 if (numVelocityScalingValues != NumVelocityScalingValues::None
79 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Diagonal)
81 v[a][d] *= (lambda - diagPR[d]);
83 if (numVelocityScalingValues != NumVelocityScalingValues::None
84 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Full)
86 v[a][d] = lambda * v[a][d] - iprod(matrixPR[d], v[a]);
88 if (numVelocityScalingValues == NumVelocityScalingValues::None
89 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Diagonal)
91 v[a][d] *= (1 - diagPR[d]);
93 if (numVelocityScalingValues == NumVelocityScalingValues::None
94 && parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::Full)
96 v[a][d] -= iprod(matrixPR[d], v[a]);
98 v[a][d] += f[a][d] * invMassPerDim[a][d] * dt;
103 static void inline updatePositions(int a,
105 const rvec* gmx_restrict x,
106 rvec* gmx_restrict xprime,
107 const rvec* gmx_restrict v)
109 for (int d = 0; d < DIM; d++)
111 xprime[a][d] = x[a][d] + v[a][d] * dt;
115 //! Propagation (position only)
117 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
118 void Propagator<IntegrationStep::PositionsOnly>::run()
120 wallcycle_start(wcycle_, ewcUPDATE);
122 auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
123 auto x = as_rvec_array(statePropagatorData_->constPreviousPositionsView().paddedArrayRef().data());
124 auto v = as_rvec_array(statePropagatorData_->constVelocitiesView().paddedArrayRef().data());
126 int nth = gmx_omp_nthreads_get(emntUpdate);
127 int homenr = mdAtoms_->mdatoms()->homenr;
129 #pragma omp parallel for num_threads(nth) schedule(static) default(none) shared(nth, homenr, x, xp, v)
130 for (int th = 0; th < nth; th++)
134 int start_th, end_th;
135 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
137 for (int a = start_th; a < end_th; a++)
139 updatePositions(a, timestep_, x, xp, v);
142 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
144 wallcycle_stop(wcycle_, ewcUPDATE);
147 //! Propagation (velocity only)
149 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
150 void Propagator<IntegrationStep::VelocitiesOnly>::run()
152 wallcycle_start(wcycle_, ewcUPDATE);
154 auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
155 auto f = as_rvec_array(statePropagatorData_->constForcesView().paddedArrayRef().data());
156 auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
159 (numVelocityScalingValues == NumVelocityScalingValues::Single) ? velocityScaling_[0] : 1.0;
161 bool doDiagonalScaling = false;
162 if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::No)
164 // TODO: Could we know in advance whether the matrix is diagonal?
165 doDiagonalScaling = (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0);
166 if (doDiagonalScaling)
168 diagPR[XX] = matrixPR[XX][XX];
169 diagPR[YY] = matrixPR[YY][YY];
170 diagPR[ZZ] = matrixPR[ZZ][ZZ];
174 int nth = gmx_omp_nthreads_get(emntUpdate);
175 int homenr = mdAtoms_->mdatoms()->homenr;
177 // lambda could be shared, but gcc-8 & gcc-9 don't agree how to write that...
178 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
179 #pragma omp parallel for num_threads(nth) schedule(static) default(none) \
180 shared(nth, homenr, v, f, invMassPerDim, doDiagonalScaling) firstprivate(lambda)
181 for (int th = 0; th < nth; th++)
185 int start_th, end_th;
186 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
188 for (int a = start_th; a < end_th; a++)
190 if (parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
192 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
194 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>(
195 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
196 invMassPerDim, v, f, diagPR, matrixPR);
200 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::No>(
201 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
206 if (doDiagonalScaling)
208 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
210 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Diagonal>(
211 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
212 invMassPerDim, v, f, diagPR, matrixPR);
216 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
217 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
222 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
224 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>(
225 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
226 invMassPerDim, v, f, diagPR, matrixPR);
230 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Full>(
231 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
237 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
239 wallcycle_stop(wcycle_, ewcUPDATE);
242 //! Propagation (leapfrog case - position and velocity)
244 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
245 void Propagator<IntegrationStep::LeapFrog>::run()
247 wallcycle_start(wcycle_, ewcUPDATE);
249 auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
250 auto x = as_rvec_array(statePropagatorData_->constPreviousPositionsView().paddedArrayRef().data());
251 auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
252 auto f = as_rvec_array(statePropagatorData_->constForcesView().paddedArrayRef().data());
253 auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
256 (numVelocityScalingValues == NumVelocityScalingValues::Single) ? velocityScaling_[0] : 1.0;
258 bool doDiagonalScaling = false;
259 if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::No)
261 // TODO: Could we know in advance whether the matrix is diagonal?
262 doDiagonalScaling = (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0);
263 if (doDiagonalScaling)
265 diagPR[XX] = matrixPR[XX][XX];
266 diagPR[YY] = matrixPR[YY][YY];
267 diagPR[ZZ] = matrixPR[ZZ][ZZ];
271 int nth = gmx_omp_nthreads_get(emntUpdate);
272 int homenr = mdAtoms_->mdatoms()->homenr;
274 // lambda could be shared, but gcc-8 & gcc-9 don't agree how to write that...
275 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
276 #pragma omp parallel for num_threads(nth) schedule(static) default(none) \
277 shared(nth, homenr, x, xp, v, f, invMassPerDim, doDiagonalScaling) firstprivate(lambda)
278 for (int th = 0; th < nth; th++)
282 int start_th, end_th;
283 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
285 for (int a = start_th; a < end_th; a++)
287 if (parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
289 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
291 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>(
292 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
293 invMassPerDim, v, f, diagPR, matrixPR);
297 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::No>(
298 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
303 if (doDiagonalScaling)
305 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
307 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Diagonal>(
308 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
309 invMassPerDim, v, f, diagPR, matrixPR);
313 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
314 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
319 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
321 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>(
322 a, timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
323 invMassPerDim, v, f, diagPR, matrixPR);
327 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Full>(
328 a, timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
332 updatePositions(a, timestep_, x, xp, v);
335 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
337 wallcycle_stop(wcycle_, ewcUPDATE);
340 //! Propagation (velocity verlet stage 2 - velocity and position)
342 template<NumVelocityScalingValues numVelocityScalingValues, ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling>
343 void Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>::run()
345 wallcycle_start(wcycle_, ewcUPDATE);
347 auto xp = as_rvec_array(statePropagatorData_->positionsView().paddedArrayRef().data());
348 auto x = as_rvec_array(statePropagatorData_->constPreviousPositionsView().paddedArrayRef().data());
349 auto v = as_rvec_array(statePropagatorData_->velocitiesView().paddedArrayRef().data());
350 auto f = as_rvec_array(statePropagatorData_->constForcesView().paddedArrayRef().data());
351 auto invMassPerDim = mdAtoms_->mdatoms()->invMassPerDim;
353 int nth = gmx_omp_nthreads_get(emntUpdate);
354 int homenr = mdAtoms_->mdatoms()->homenr;
357 (numVelocityScalingValues == NumVelocityScalingValues::Single) ? velocityScaling_[0] : 1.0;
359 bool doDiagonalScaling = false;
360 if (parrinelloRahmanVelocityScaling != ParrinelloRahmanVelocityScaling::No)
362 // TODO: Could we know in advance whether the matrix is diagonal?
363 doDiagonalScaling = (matrixPR[YY][XX] == 0 && matrixPR[ZZ][XX] == 0 && matrixPR[ZZ][YY] == 0);
364 if (doDiagonalScaling)
366 diagPR[XX] = matrixPR[XX][XX];
367 diagPR[YY] = matrixPR[YY][YY];
368 diagPR[ZZ] = matrixPR[ZZ][ZZ];
372 // lambda could be shared, but gcc-8 & gcc-9 don't agree how to write that...
373 // https://www.gnu.org/software/gcc/gcc-9/porting_to.html -> OpenMP data sharing
374 #pragma omp parallel for num_threads(nth) schedule(static) default(none) \
375 shared(nth, homenr, x, xp, v, f, invMassPerDim, doDiagonalScaling) firstprivate(lambda)
376 for (int th = 0; th < nth; th++)
380 int start_th, end_th;
381 getThreadAtomRange(nth, th, homenr, &start_th, &end_th);
383 for (int a = start_th; a < end_th; a++)
385 if (parrinelloRahmanVelocityScaling == ParrinelloRahmanVelocityScaling::No)
387 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
389 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>(
390 a, 0.5 * timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
391 invMassPerDim, v, f, diagPR, matrixPR);
395 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::No>(
396 a, 0.5 * timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
401 if (doDiagonalScaling)
403 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
405 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Diagonal>(
406 a, 0.5 * timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
407 invMassPerDim, v, f, diagPR, matrixPR);
411 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Diagonal>(
412 a, 0.5 * timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
417 if (numVelocityScalingValues == NumVelocityScalingValues::Multiple)
419 updateVelocities<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>(
420 a, 0.5 * timestep_, velocityScaling_[mdAtoms_->mdatoms()->cTC[a]],
421 invMassPerDim, v, f, diagPR, matrixPR);
425 updateVelocities<numVelocityScalingValues, ParrinelloRahmanVelocityScaling::Full>(
426 a, 0.5 * timestep_, lambda, invMassPerDim, v, f, diagPR, matrixPR);
430 updatePositions(a, timestep_, x, xp, v);
433 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
435 wallcycle_stop(wcycle_, ewcUPDATE);
438 template<IntegrationStep algorithm>
439 Propagator<algorithm>::Propagator(double timestep,
440 StatePropagatorData* statePropagatorData,
441 const MDAtoms* mdAtoms,
442 gmx_wallcycle* wcycle) :
444 statePropagatorData_(statePropagatorData),
445 doSingleVelocityScaling(false),
446 doGroupVelocityScaling(false),
447 scalingStepVelocity_(-1),
456 template<IntegrationStep algorithm>
457 void Propagator<algorithm>::scheduleTask(Step gmx_unused step,
458 Time gmx_unused time,
459 const RegisterRunFunctionPtr& registerRunFunction)
461 const bool doSingleVScalingThisStep = (doSingleVelocityScaling && (step == scalingStepVelocity_));
462 const bool doGroupVScalingThisStep = (doGroupVelocityScaling && (step == scalingStepVelocity_));
464 const bool doParrinelloRahmanThisStep = (step == scalingStepPR_);
466 if (doSingleVScalingThisStep)
468 if (doParrinelloRahmanThisStep)
470 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
471 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::Full>();
476 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
477 run<NumVelocityScalingValues::Single, ParrinelloRahmanVelocityScaling::No>();
481 else if (doGroupVScalingThisStep)
483 if (doParrinelloRahmanThisStep)
485 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
486 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::Full>();
491 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
492 run<NumVelocityScalingValues::Multiple, ParrinelloRahmanVelocityScaling::No>();
498 if (doParrinelloRahmanThisStep)
500 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
501 run<NumVelocityScalingValues::None, ParrinelloRahmanVelocityScaling::Full>();
506 (*registerRunFunction)(std::make_unique<SimulatorRunFunction>([this]() {
507 run<NumVelocityScalingValues::None, ParrinelloRahmanVelocityScaling::No>();
513 template<IntegrationStep algorithm>
514 void Propagator<algorithm>::setNumVelocityScalingVariables(int numVelocityScalingVariables)
516 if (algorithm == IntegrationStep::PositionsOnly)
518 gmx_fatal(FARGS, "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
520 GMX_ASSERT(velocityScaling_.empty(),
521 "Number of velocity scaling variables cannot be changed once set.");
523 velocityScaling_.resize(numVelocityScalingVariables, 1.);
524 doSingleVelocityScaling = numVelocityScalingVariables == 1;
525 doGroupVelocityScaling = numVelocityScalingVariables > 1;
528 template<IntegrationStep algorithm>
529 ArrayRef<real> Propagator<algorithm>::viewOnVelocityScaling()
531 if (algorithm == IntegrationStep::PositionsOnly)
533 gmx_fatal(FARGS, "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
535 GMX_ASSERT(!velocityScaling_.empty(), "Number of velocity scaling variables not set.");
537 return velocityScaling_;
540 template<IntegrationStep algorithm>
541 std::unique_ptr<std::function<void(Step)>> Propagator<algorithm>::velocityScalingCallback()
543 if (algorithm == IntegrationStep::PositionsOnly)
545 gmx_fatal(FARGS, "Velocity scaling not implemented for IntegrationStep::PositionsOnly.");
548 return std::make_unique<PropagatorCallback>([this](Step step) { scalingStepVelocity_ = step; });
551 template<IntegrationStep algorithm>
552 ArrayRef<rvec> Propagator<algorithm>::viewOnPRScalingMatrix()
555 algorithm != IntegrationStep::PositionsOnly,
556 "Parrinello-Rahman scaling not implemented for IntegrationStep::PositionsOnly.");
559 // gcc-5 needs this to be explicit (all other tested compilers would be ok
560 // with simply returning matrixPR)
561 return ArrayRef<rvec>(matrixPR);
564 template<IntegrationStep algorithm>
565 PropagatorCallbackPtr Propagator<algorithm>::prScalingCallback()
568 algorithm != IntegrationStep::PositionsOnly,
569 "Parrinello-Rahman scaling not implemented for IntegrationStep::PositionsOnly.");
571 return std::make_unique<PropagatorCallback>([this](Step step) { scalingStepPR_ = step; });
574 //! Explicit template initialization
576 template class Propagator<IntegrationStep::PositionsOnly>;
577 template class Propagator<IntegrationStep::VelocitiesOnly>;
578 template class Propagator<IntegrationStep::LeapFrog>;
579 template class Propagator<IntegrationStep::VelocityVerletPositionsAndVelocities>;