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,2018,2019, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
37 /* This file is completely threadsafe - keep it that way! */
42 #include "gromacs/gmxlib/network.h"
43 #include "gromacs/math/functions.h"
44 #include "gromacs/math/invertmatrix.h"
45 #include "gromacs/math/vec.h"
46 #include "gromacs/math/vecdump.h"
47 #include "gromacs/mdlib/gmx_omp_nthreads.h"
48 #include "gromacs/mdtypes/inputrec.h"
49 #include "gromacs/mdtypes/md_enums.h"
50 #include "gromacs/pbcutil/pbc.h"
51 #include "gromacs/topology/topology.h"
52 #include "gromacs/utility/fatalerror.h"
53 #include "gromacs/utility/gmxassert.h"
54 #include "gromacs/utility/gmxomp.h"
55 #include "gromacs/utility/smalloc.h"
57 t_vcm::t_vcm(const SimulationGroups &groups, const t_inputrec &ir)
59 mode = (ir.nstcomm > 0) ? ir.comm_mode : ecmNO;
61 timeStep = ir.nstcomm*ir.delta_t;
63 if (mode == ecmANGULAR && ndim < 3)
65 gmx_fatal(FARGS, "Can not have angular comm removal with pbc=%s",
71 nr = groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval].nr;
72 /* Allocate one extra for a possible rest group */
74 /* We need vcm->nr+1 elements per thread, but to avoid cache
75 * invalidation we add 2 elements to get a 152 byte separation.
78 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];
96 group_name[g] = *groups.groupNames[groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval].nm_ind[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",
122 fprintf(fp, "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 */
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(int start, int homenr, t_mdatoms *md,
155 rvec x[], rvec v[], t_vcm *vcm)
157 int nthreads = gmx_omp_nthreads_get(emntDefault);
158 if (vcm->mode != ecmNO)
160 #pragma omp parallel num_threads(nthreads)
162 int t = gmx_omp_get_thread_num();
163 for (int g = 0; g < vcm->size; g++)
165 /* Reset linear momentum */
166 t_vcm_thread *vcm_t = &vcm->thread_vcm[t*vcm->stride + g];
168 clear_rvec(vcm_t->p);
169 if (vcm->mode == ecmANGULAR)
171 /* Reset angular momentum */
172 clear_rvec(vcm_t->j);
173 clear_rvec(vcm_t->x);
178 #pragma omp for schedule(static)
179 for (int i = start; i < start+homenr; i++)
182 real m0 = md->massT[i];
187 t_vcm_thread *vcm_t = &vcm->thread_vcm[t*vcm->stride + g];
188 /* Calculate linear momentum */
191 for (m = 0; (m < DIM); m++)
193 vcm_t->p[m] += m0*v[i][m];
196 if (vcm->mode == ecmANGULAR)
198 /* Calculate angular momentum */
200 cprod(x[i], v[i], j0);
202 for (m = 0; (m < DIM); m++)
204 vcm_t->j[m] += m0*j0[m];
205 vcm_t->x[m] += m0*x[i][m];
207 /* Update inertia tensor */
208 update_tensor(x[i], m0, vcm_t->i);
212 for (int g = 0; g < vcm->size; g++)
214 /* Reset linear momentum */
215 vcm->group_mass[g] = 0;
216 clear_rvec(vcm->group_p[g]);
217 if (vcm->mode == ecmANGULAR)
219 /* Reset angular momentum */
220 clear_rvec(vcm->group_j[g]);
221 clear_rvec(vcm->group_x[g]);
222 clear_rvec(vcm->group_w[g]);
223 clear_mat(vcm->group_i[g]);
226 for (int t = 0; t < nthreads; t++)
228 t_vcm_thread *vcm_t = &vcm->thread_vcm[t*vcm->stride + g];
229 vcm->group_mass[g] += vcm_t->mass;
230 rvec_inc(vcm->group_p[g], vcm_t->p);
231 if (vcm->mode == ecmANGULAR)
233 rvec_inc(vcm->group_j[g], vcm_t->j);
234 rvec_inc(vcm->group_x[g], vcm_t->x);
235 m_add(vcm_t->i, vcm->group_i[g], vcm->group_i[g]);
243 /*! \brief Remove the COM motion velocity from the velocities
245 * \note This routine should be called from within an OpenMP parallel region.
247 * \tparam numDimensions Correct dimensions 0 to \p numDimensions-1
248 * \param[in] mdatoms The atom property and group information
249 * \param[in,out] v The velocities to correct
250 * \param[in] vcm VCM data
252 template<int numDimensions>
254 doStopComMotionLinear(const t_mdatoms &mdatoms,
258 const int homenr = mdatoms.homenr;
259 const unsigned short *group_id = mdatoms.cVCM;
261 if (mdatoms.cFREEZE != nullptr)
263 GMX_RELEASE_ASSERT(vcm.nFreeze != nullptr, "Need freeze dimension info with freeze groups");
265 #pragma omp for schedule(static)
266 for (int i = 0; i < homenr; i++)
268 unsigned short vcmGroup = (group_id == nullptr ? 0 : group_id[i]);
269 unsigned short freezeGroup = mdatoms.cFREEZE[i];
270 for (int d = 0; d < numDimensions; d++)
272 if (vcm.nFreeze[freezeGroup][d] == 0)
274 v[i][d] -= vcm.group_v[vcmGroup][d];
279 else if (group_id == nullptr)
281 #pragma omp for schedule(static)
282 for (int i = 0; i < homenr; i++)
284 for (int d = 0; d < numDimensions; d++)
286 v[i][d] -= vcm.group_v[0][d];
292 #pragma omp for schedule(static)
293 for (int i = 0; i < homenr; i++)
295 const int g = group_id[i];
296 for (int d = 0; d < numDimensions; d++)
298 v[i][d] -= vcm.group_v[g][d];
304 /*! \brief Remove the COM motion velocity from the velocities, correct the coordinates assuming constant acceleration
306 * \note This routine should be called from within an OpenMP parallel region.
308 * \tparam numDimensions Correct dimensions 0 to \p numDimensions-1
309 * \param[in] homenr The number of atoms to correct
310 * \param[in] group_id List of VCM group ids, when nullptr is passed all atoms are assumed to be in group 0
311 * \param[in,out] x The coordinates to correct
312 * \param[in,out] v The velocities to correct
313 * \param[in] vcm VCM data
315 template<int numDimensions>
317 doStopComMotionAccelerationCorrection(int homenr,
318 const unsigned short *group_id,
319 rvec * gmx_restrict x,
320 rvec * gmx_restrict v,
323 const real xCorrectionFactor = 0.5*vcm.timeStep;
325 if (group_id == nullptr)
327 #pragma omp for schedule(static)
328 for (int i = 0; i < homenr; i++)
330 for (int d = 0; d < numDimensions; d++)
332 x[i][d] -= vcm.group_v[0][d]*xCorrectionFactor;
333 v[i][d] -= vcm.group_v[0][d];
339 #pragma omp for schedule(static)
340 for (int i = 0; i < homenr; i++)
342 const int g = group_id[i];
343 for (int d = 0; d < numDimensions; d++)
345 x[i][d] -= vcm.group_v[g][d]*xCorrectionFactor;
346 v[i][d] -= vcm.group_v[g][d];
352 void do_stopcm_grp(const t_mdatoms &mdatoms,
353 rvec x[], rvec v[], const t_vcm &vcm)
355 if (vcm.mode != ecmNO)
357 const int homenr = mdatoms.homenr;
358 const unsigned short *group_id = mdatoms.cVCM;
360 int gmx_unused nth = gmx_omp_nthreads_get(emntDefault);
361 #pragma omp parallel num_threads(nth)
363 if (vcm.mode == ecmLINEAR ||
364 vcm.mode == ecmANGULAR ||
365 (vcm.mode == ecmLINEAR_ACCELERATION_CORRECTION && x == nullptr))
367 /* Subtract linear momentum for v */
371 doStopComMotionLinear<1>(mdatoms, v, vcm);
374 doStopComMotionLinear<2>(mdatoms, v, vcm);
377 doStopComMotionLinear<3>(mdatoms, v, vcm);
383 GMX_ASSERT(vcm.mode == ecmLINEAR_ACCELERATION_CORRECTION, "When the mode is not linear or angular, it should be acceleration correction");
384 /* Subtract linear momentum for v and x*/
388 doStopComMotionAccelerationCorrection<1>(homenr, group_id, x, v, vcm);
391 doStopComMotionAccelerationCorrection<2>(homenr, group_id, x, v, vcm);
394 doStopComMotionAccelerationCorrection<3>(homenr, group_id, x, v, vcm);
399 if (vcm.mode == ecmANGULAR)
401 /* Subtract angular momentum */
402 GMX_ASSERT(x, "Need x to compute angular momentum correction");
405 #pragma omp for schedule(static)
406 for (int i = 0; i < homenr; i++)
412 /* Compute the correction to the velocity for each atom */
414 rvec_sub(x[i], vcm.group_x[g], dx);
415 cprod(vcm.group_w[g], dx, dv);
423 static void get_minv(tensor A, tensor B)
429 tmp[XX][XX] = A[YY][YY] + A[ZZ][ZZ];
430 tmp[YY][XX] = -A[XX][YY];
431 tmp[ZZ][XX] = -A[XX][ZZ];
432 tmp[XX][YY] = -A[XX][YY];
433 tmp[YY][YY] = A[XX][XX] + A[ZZ][ZZ];
434 tmp[ZZ][YY] = -A[YY][ZZ];
435 tmp[XX][ZZ] = -A[XX][ZZ];
436 tmp[YY][ZZ] = -A[YY][ZZ];
437 tmp[ZZ][ZZ] = A[XX][XX] + A[YY][YY];
439 /* This is a hack to prevent very large determinants */
440 rfac = (tmp[XX][XX]+tmp[YY][YY]+tmp[ZZ][ZZ])/3;
443 gmx_fatal(FARGS, "Can not stop center of mass: maybe 2dimensional system");
446 for (m = 0; (m < DIM); m++)
448 for (n = 0; (n < DIM); n++)
453 gmx::invertMatrix(tmp, B);
454 for (m = 0; (m < DIM); m++)
456 for (n = 0; (n < DIM); n++)
463 void check_cm_grp(FILE *fp, t_vcm *vcm, t_inputrec *ir, real Temp_Max)
466 real ekcm, ekrot, tm, tm_1, Temp_cm;
470 /* First analyse the total results */
471 if (vcm->mode != ecmNO)
473 for (g = 0; (g < vcm->nr); g++)
475 tm = vcm->group_mass[g];
479 svmul(tm_1, vcm->group_p[g], vcm->group_v[g]);
481 /* Else it's zero anyway! */
483 if (vcm->mode == ecmANGULAR)
485 for (g = 0; (g < vcm->nr); g++)
487 tm = vcm->group_mass[g];
492 /* Compute center of mass for this group */
493 for (m = 0; (m < DIM); m++)
495 vcm->group_x[g][m] *= tm_1;
498 /* Subtract the center of mass contribution to the
501 cprod(vcm->group_x[g], vcm->group_v[g], jcm);
502 for (m = 0; (m < DIM); m++)
504 vcm->group_j[g][m] -= tm*jcm[m];
507 /* Subtract the center of mass contribution from the inertia
508 * tensor (this is not as trivial as it seems, but due to
509 * some cancellation we can still do it, even in parallel).
512 update_tensor(vcm->group_x[g], tm, Icm);
513 m_sub(vcm->group_i[g], Icm, vcm->group_i[g]);
515 /* Compute angular velocity, using matrix operation
520 get_minv(vcm->group_i[g], Icm);
521 mvmul(Icm, vcm->group_j[g], vcm->group_w[g]);
523 /* Else it's zero anyway! */
527 for (g = 0; (g < vcm->nr); g++)
530 if (vcm->group_mass[g] != 0 && vcm->group_ndf[g] > 0)
532 for (m = 0; m < vcm->ndim; m++)
534 ekcm += gmx::square(vcm->group_v[g][m]);
536 ekcm *= 0.5*vcm->group_mass[g];
537 Temp_cm = 2*ekcm/vcm->group_ndf[g];
539 if ((Temp_cm > Temp_Max) && fp)
541 fprintf(fp, "Large VCM(group %s): %12.5f, %12.5f, %12.5f, Temp-cm: %12.5e\n",
542 vcm->group_name[g], vcm->group_v[g][XX],
543 vcm->group_v[g][YY], vcm->group_v[g][ZZ], Temp_cm);
546 if (vcm->mode == ecmANGULAR)
548 ekrot = 0.5*iprod(vcm->group_j[g], vcm->group_w[g]);
549 if ((ekrot > 1) && fp && !EI_RANDOM(ir->eI))
551 /* if we have an integrator that may not conserve momenta, skip */
552 tm = vcm->group_mass[g];
553 fprintf(fp, "Group %s with mass %12.5e, Ekrot %12.5e Det(I) = %12.5e\n",
554 vcm->group_name[g], tm, ekrot, det(vcm->group_i[g]));
555 fprintf(fp, " COM: %12.5f %12.5f %12.5f\n",
556 vcm->group_x[g][XX], vcm->group_x[g][YY], vcm->group_x[g][ZZ]);
557 fprintf(fp, " P: %12.5f %12.5f %12.5f\n",
558 vcm->group_p[g][XX], vcm->group_p[g][YY], vcm->group_p[g][ZZ]);
559 fprintf(fp, " V: %12.5f %12.5f %12.5f\n",
560 vcm->group_v[g][XX], vcm->group_v[g][YY], vcm->group_v[g][ZZ]);
561 fprintf(fp, " J: %12.5f %12.5f %12.5f\n",
562 vcm->group_j[g][XX], vcm->group_j[g][YY], vcm->group_j[g][ZZ]);
563 fprintf(fp, " w: %12.5f %12.5f %12.5f\n",
564 vcm->group_w[g][XX], vcm->group_w[g][YY], vcm->group_w[g][ZZ]);
565 pr_rvecs(fp, 0, "Inertia tensor", vcm->group_i[g], DIM);