2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
7 * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
38 /* This file is completely threadsafe - keep it that way! */
43 #include "gromacs/gmxlib/network.h"
44 #include "gromacs/math/functions.h"
45 #include "gromacs/math/invertmatrix.h"
46 #include "gromacs/math/vec.h"
47 #include "gromacs/math/vecdump.h"
48 #include "gromacs/mdlib/gmx_omp_nthreads.h"
49 #include "gromacs/mdtypes/inputrec.h"
50 #include "gromacs/mdtypes/md_enums.h"
51 #include "gromacs/pbcutil/pbc.h"
52 #include "gromacs/topology/topology.h"
53 #include "gromacs/utility/fatalerror.h"
54 #include "gromacs/utility/gmxassert.h"
55 #include "gromacs/utility/gmxomp.h"
56 #include "gromacs/utility/smalloc.h"
58 t_vcm::t_vcm(const SimulationGroups
& groups
, const t_inputrec
& ir
) :
59 integratorConservesMomentum(!EI_RANDOM(ir
.eI
))
61 mode
= (ir
.nstcomm
> 0) ? ir
.comm_mode
: ecmNO
;
63 timeStep
= ir
.nstcomm
* ir
.delta_t
;
65 if (mode
== ecmANGULAR
&& ndim
< 3)
67 gmx_fatal(FARGS
, "Can not have angular comm removal with pbc=%s", epbc_names
[ir
.ePBC
]);
72 nr
= groups
.groups
[SimulationAtomGroupType::MassCenterVelocityRemoval
].size();
73 /* Allocate one extra for a possible rest group */
75 /* We need vcm->nr+1 elements per thread, but to avoid cache
76 * invalidation we add 2 elements to get a 152 byte separation.
79 if (mode
== ecmANGULAR
)
88 group_name
.resize(size
);
91 group_mass
.resize(size
);
92 group_ndf
.resize(size
);
93 for (int g
= 0; (g
< nr
); g
++)
95 group_ndf
[g
] = ir
.opts
.nrdf
[g
];
97 *groups
.groupNames
[groups
.groups
[SimulationAtomGroupType::MassCenterVelocityRemoval
][g
]];
100 thread_vcm
.resize(gmx_omp_nthreads_get(emntDefault
) * stride
);
103 nFreeze
= ir
.opts
.nFreeze
;
108 if (mode
== ecmANGULAR
)
114 void reportComRemovalInfo(FILE* fp
, const t_vcm
& vcm
)
117 /* Copy pointer to group names and print it. */
118 if (fp
&& vcm
.mode
!= ecmNO
)
120 fprintf(fp
, "Center of mass motion removal mode is %s\n", ECOM(vcm
.mode
));
122 "We have the following groups for center of"
123 " mass motion removal:\n");
125 for (int g
= 0; (g
< vcm
.nr
); g
++)
128 fprintf(fp
, "%3d: %s\n", g
, vcm
.group_name
[g
]);
134 static void update_tensor(const rvec x
, real m0
, tensor I
)
138 /* Compute inertia tensor contribution due to this atom */
139 xy
= x
[XX
] * x
[YY
] * m0
;
140 xz
= x
[XX
] * x
[ZZ
] * m0
;
141 yz
= x
[YY
] * x
[ZZ
] * m0
;
142 I
[XX
][XX
] += x
[XX
] * x
[XX
] * m0
;
143 I
[YY
][YY
] += x
[YY
] * x
[YY
] * m0
;
144 I
[ZZ
][ZZ
] += x
[ZZ
] * x
[ZZ
] * m0
;
153 /* Center of mass code for groups */
154 void calc_vcm_grp(const t_mdatoms
& md
, const rvec x
[], const rvec v
[], t_vcm
* vcm
)
156 int nthreads
= gmx_omp_nthreads_get(emntDefault
);
157 if (vcm
->mode
!= ecmNO
)
159 #pragma omp parallel num_threads(nthreads)
161 int t
= gmx_omp_get_thread_num();
162 for (int g
= 0; g
< vcm
->size
; g
++)
164 /* Reset linear momentum */
165 t_vcm_thread
* vcm_t
= &vcm
->thread_vcm
[t
* vcm
->stride
+ g
];
167 clear_rvec(vcm_t
->p
);
168 if (vcm
->mode
== ecmANGULAR
)
170 /* Reset angular momentum */
171 clear_rvec(vcm_t
->j
);
172 clear_rvec(vcm_t
->x
);
177 #pragma omp for schedule(static)
178 for (int i
= 0; i
< md
.homenr
; i
++)
181 real m0
= md
.massT
[i
];
186 t_vcm_thread
* vcm_t
= &vcm
->thread_vcm
[t
* vcm
->stride
+ g
];
187 /* Calculate linear momentum */
190 for (m
= 0; (m
< DIM
); m
++)
192 vcm_t
->p
[m
] += m0
* v
[i
][m
];
195 if (vcm
->mode
== ecmANGULAR
)
197 /* Calculate angular momentum */
199 cprod(x
[i
], v
[i
], j0
);
201 for (m
= 0; (m
< DIM
); m
++)
203 vcm_t
->j
[m
] += m0
* j0
[m
];
204 vcm_t
->x
[m
] += m0
* x
[i
][m
];
206 /* Update inertia tensor */
207 update_tensor(x
[i
], m0
, vcm_t
->i
);
211 for (int g
= 0; g
< vcm
->size
; g
++)
213 /* Reset linear momentum */
214 vcm
->group_mass
[g
] = 0;
215 clear_rvec(vcm
->group_p
[g
]);
216 if (vcm
->mode
== ecmANGULAR
)
218 /* Reset angular momentum */
219 clear_rvec(vcm
->group_j
[g
]);
220 clear_rvec(vcm
->group_x
[g
]);
221 clear_rvec(vcm
->group_w
[g
]);
222 clear_mat(vcm
->group_i
[g
]);
225 for (int t
= 0; t
< nthreads
; t
++)
227 t_vcm_thread
* vcm_t
= &vcm
->thread_vcm
[t
* vcm
->stride
+ g
];
228 vcm
->group_mass
[g
] += vcm_t
->mass
;
229 rvec_inc(vcm
->group_p
[g
], vcm_t
->p
);
230 if (vcm
->mode
== ecmANGULAR
)
232 rvec_inc(vcm
->group_j
[g
], vcm_t
->j
);
233 rvec_inc(vcm
->group_x
[g
], vcm_t
->x
);
234 m_add(vcm_t
->i
, vcm
->group_i
[g
], vcm
->group_i
[g
]);
241 /*! \brief Remove the COM motion velocity from the velocities
243 * \note This routine should be called from within an OpenMP parallel region.
245 * \tparam numDimensions Correct dimensions 0 to \p numDimensions-1
246 * \param[in] mdatoms The atom property and group information
247 * \param[in,out] v The velocities to correct
248 * \param[in] vcm VCM data
250 template<int numDimensions
>
251 static void doStopComMotionLinear(const t_mdatoms
& mdatoms
, rvec
* v
, const t_vcm
& vcm
)
253 const int homenr
= mdatoms
.homenr
;
254 const unsigned short* group_id
= mdatoms
.cVCM
;
256 if (mdatoms
.cFREEZE
!= nullptr)
258 GMX_RELEASE_ASSERT(vcm
.nFreeze
!= nullptr, "Need freeze dimension info with freeze groups");
260 #pragma omp for schedule(static)
261 for (int i
= 0; i
< homenr
; i
++)
263 unsigned short vcmGroup
= (group_id
== nullptr ? 0 : group_id
[i
]);
264 unsigned short freezeGroup
= mdatoms
.cFREEZE
[i
];
265 for (int d
= 0; d
< numDimensions
; d
++)
267 if (vcm
.nFreeze
[freezeGroup
][d
] == 0)
269 v
[i
][d
] -= vcm
.group_v
[vcmGroup
][d
];
274 else if (group_id
== nullptr)
276 #pragma omp for schedule(static)
277 for (int i
= 0; i
< homenr
; i
++)
279 for (int d
= 0; d
< numDimensions
; d
++)
281 v
[i
][d
] -= vcm
.group_v
[0][d
];
287 #pragma omp for schedule(static)
288 for (int i
= 0; i
< homenr
; i
++)
290 const int g
= group_id
[i
];
291 for (int d
= 0; d
< numDimensions
; d
++)
293 v
[i
][d
] -= vcm
.group_v
[g
][d
];
299 /*! \brief Remove the COM motion velocity from the velocities, correct the coordinates assuming constant acceleration
301 * \note This routine should be called from within an OpenMP parallel region.
303 * \tparam numDimensions Correct dimensions 0 to \p numDimensions-1
304 * \param[in] homenr The number of atoms to correct
305 * \param[in] group_id List of VCM group ids, when nullptr is passed all atoms are assumed to be in group 0
306 * \param[in,out] x The coordinates to correct
307 * \param[in,out] v The velocities to correct
308 * \param[in] vcm VCM data
310 template<int numDimensions
>
311 static void doStopComMotionAccelerationCorrection(int homenr
,
312 const unsigned short* group_id
,
313 rvec
* gmx_restrict x
,
314 rvec
* gmx_restrict v
,
317 const real xCorrectionFactor
= 0.5 * vcm
.timeStep
;
319 if (group_id
== nullptr)
321 #pragma omp for schedule(static)
322 for (int i
= 0; i
< homenr
; i
++)
324 for (int d
= 0; d
< numDimensions
; d
++)
326 x
[i
][d
] -= vcm
.group_v
[0][d
] * xCorrectionFactor
;
327 v
[i
][d
] -= vcm
.group_v
[0][d
];
333 #pragma omp for schedule(static)
334 for (int i
= 0; i
< homenr
; i
++)
336 const int g
= group_id
[i
];
337 for (int d
= 0; d
< numDimensions
; d
++)
339 x
[i
][d
] -= vcm
.group_v
[g
][d
] * xCorrectionFactor
;
340 v
[i
][d
] -= vcm
.group_v
[g
][d
];
346 static void do_stopcm_grp(const t_mdatoms
& mdatoms
, rvec x
[], rvec v
[], const t_vcm
& vcm
)
348 if (vcm
.mode
!= ecmNO
)
350 const int homenr
= mdatoms
.homenr
;
351 const unsigned short* group_id
= mdatoms
.cVCM
;
353 int gmx_unused nth
= gmx_omp_nthreads_get(emntDefault
);
354 #pragma omp parallel num_threads(nth)
356 if (vcm
.mode
== ecmLINEAR
|| vcm
.mode
== ecmANGULAR
357 || (vcm
.mode
== ecmLINEAR_ACCELERATION_CORRECTION
&& x
== nullptr))
359 /* Subtract linear momentum for v */
362 case 1: doStopComMotionLinear
<1>(mdatoms
, v
, vcm
); break;
363 case 2: doStopComMotionLinear
<2>(mdatoms
, v
, vcm
); break;
364 case 3: doStopComMotionLinear
<3>(mdatoms
, v
, vcm
); break;
369 GMX_ASSERT(vcm
.mode
== ecmLINEAR_ACCELERATION_CORRECTION
,
370 "When the mode is not linear or angular, it should be acceleration "
372 /* Subtract linear momentum for v and x*/
376 doStopComMotionAccelerationCorrection
<1>(homenr
, group_id
, x
, v
, vcm
);
379 doStopComMotionAccelerationCorrection
<2>(homenr
, group_id
, x
, v
, vcm
);
382 doStopComMotionAccelerationCorrection
<3>(homenr
, group_id
, x
, v
, vcm
);
386 if (vcm
.mode
== ecmANGULAR
)
388 /* Subtract angular momentum */
389 GMX_ASSERT(x
, "Need x to compute angular momentum correction");
392 #pragma omp for schedule(static)
393 for (int i
= 0; i
< homenr
; i
++)
399 /* Compute the correction to the velocity for each atom */
401 rvec_sub(x
[i
], vcm
.group_x
[g
], dx
);
402 cprod(vcm
.group_w
[g
], dx
, dv
);
410 static void get_minv(tensor A
, tensor B
)
416 tmp
[XX
][XX
] = A
[YY
][YY
] + A
[ZZ
][ZZ
];
417 tmp
[YY
][XX
] = -A
[XX
][YY
];
418 tmp
[ZZ
][XX
] = -A
[XX
][ZZ
];
419 tmp
[XX
][YY
] = -A
[XX
][YY
];
420 tmp
[YY
][YY
] = A
[XX
][XX
] + A
[ZZ
][ZZ
];
421 tmp
[ZZ
][YY
] = -A
[YY
][ZZ
];
422 tmp
[XX
][ZZ
] = -A
[XX
][ZZ
];
423 tmp
[YY
][ZZ
] = -A
[YY
][ZZ
];
424 tmp
[ZZ
][ZZ
] = A
[XX
][XX
] + A
[YY
][YY
];
426 /* This is a hack to prevent very large determinants */
427 rfac
= (tmp
[XX
][XX
] + tmp
[YY
][YY
] + tmp
[ZZ
][ZZ
]) / 3;
430 gmx_fatal(FARGS
, "Can not stop center of mass: maybe 2dimensional system");
433 for (m
= 0; (m
< DIM
); m
++)
435 for (n
= 0; (n
< DIM
); n
++)
440 gmx::invertMatrix(tmp
, B
);
441 for (m
= 0; (m
< DIM
); m
++)
443 for (n
= 0; (n
< DIM
); n
++)
450 /* Processes VCM after reduction over ranks and prints warning with high VMC and fp != nullptr */
451 static void process_and_check_cm_grp(FILE* fp
, t_vcm
* vcm
, real Temp_Max
)
454 real ekcm
, ekrot
, tm
, tm_1
, Temp_cm
;
458 /* First analyse the total results */
459 if (vcm
->mode
!= ecmNO
)
461 for (g
= 0; (g
< vcm
->nr
); g
++)
463 tm
= vcm
->group_mass
[g
];
467 svmul(tm_1
, vcm
->group_p
[g
], vcm
->group_v
[g
]);
469 /* Else it's zero anyway! */
471 if (vcm
->mode
== ecmANGULAR
)
473 for (g
= 0; (g
< vcm
->nr
); g
++)
475 tm
= vcm
->group_mass
[g
];
480 /* Compute center of mass for this group */
481 for (m
= 0; (m
< DIM
); m
++)
483 vcm
->group_x
[g
][m
] *= tm_1
;
486 /* Subtract the center of mass contribution to the
489 cprod(vcm
->group_x
[g
], vcm
->group_v
[g
], jcm
);
490 for (m
= 0; (m
< DIM
); m
++)
492 vcm
->group_j
[g
][m
] -= tm
* jcm
[m
];
495 /* Subtract the center of mass contribution from the inertia
496 * tensor (this is not as trivial as it seems, but due to
497 * some cancellation we can still do it, even in parallel).
500 update_tensor(vcm
->group_x
[g
], tm
, Icm
);
501 m_sub(vcm
->group_i
[g
], Icm
, vcm
->group_i
[g
]);
503 /* Compute angular velocity, using matrix operation
508 get_minv(vcm
->group_i
[g
], Icm
);
509 mvmul(Icm
, vcm
->group_j
[g
], vcm
->group_w
[g
]);
511 /* Else it's zero anyway! */
515 for (g
= 0; (g
< vcm
->nr
); g
++)
518 if (vcm
->group_mass
[g
] != 0 && vcm
->group_ndf
[g
] > 0)
520 for (m
= 0; m
< vcm
->ndim
; m
++)
522 ekcm
+= gmx::square(vcm
->group_v
[g
][m
]);
524 ekcm
*= 0.5 * vcm
->group_mass
[g
];
525 Temp_cm
= 2 * ekcm
/ vcm
->group_ndf
[g
];
527 if ((Temp_cm
> Temp_Max
) && fp
)
529 fprintf(fp
, "Large VCM(group %s): %12.5f, %12.5f, %12.5f, Temp-cm: %12.5e\n",
530 vcm
->group_name
[g
], vcm
->group_v
[g
][XX
], vcm
->group_v
[g
][YY
],
531 vcm
->group_v
[g
][ZZ
], Temp_cm
);
534 if (vcm
->mode
== ecmANGULAR
)
536 ekrot
= 0.5 * iprod(vcm
->group_j
[g
], vcm
->group_w
[g
]);
537 // TODO: Change absolute energy comparison to relative
538 if ((ekrot
> 1) && fp
&& vcm
->integratorConservesMomentum
)
540 /* if we have an integrator that may not conserve momenta, skip */
541 tm
= vcm
->group_mass
[g
];
542 fprintf(fp
, "Group %s with mass %12.5e, Ekrot %12.5e Det(I) = %12.5e\n",
543 vcm
->group_name
[g
], tm
, ekrot
, det(vcm
->group_i
[g
]));
544 fprintf(fp
, " COM: %12.5f %12.5f %12.5f\n", vcm
->group_x
[g
][XX
],
545 vcm
->group_x
[g
][YY
], vcm
->group_x
[g
][ZZ
]);
546 fprintf(fp
, " P: %12.5f %12.5f %12.5f\n", vcm
->group_p
[g
][XX
],
547 vcm
->group_p
[g
][YY
], vcm
->group_p
[g
][ZZ
]);
548 fprintf(fp
, " V: %12.5f %12.5f %12.5f\n", vcm
->group_v
[g
][XX
],
549 vcm
->group_v
[g
][YY
], vcm
->group_v
[g
][ZZ
]);
550 fprintf(fp
, " J: %12.5f %12.5f %12.5f\n", vcm
->group_j
[g
][XX
],
551 vcm
->group_j
[g
][YY
], vcm
->group_j
[g
][ZZ
]);
552 fprintf(fp
, " w: %12.5f %12.5f %12.5f\n", vcm
->group_w
[g
][XX
],
553 vcm
->group_w
[g
][YY
], vcm
->group_w
[g
][ZZ
]);
554 pr_rvecs(fp
, 0, "Inertia tensor", vcm
->group_i
[g
], DIM
);
561 void process_and_stopcm_grp(FILE* fplog
, t_vcm
* vcm
, const t_mdatoms
& mdatoms
, rvec x
[], rvec v
[])
563 if (vcm
->mode
!= ecmNO
)
565 // TODO: Replace fixed temperature of 1 by a system value
566 process_and_check_cm_grp(fplog
, vcm
, 1);
568 do_stopcm_grp(mdatoms
, x
, v
, *vcm
);