Added trivial const qualifiers
[gromacs.git] / src / gromacs / mdlib / vcm.cpp
blobd7a56714be0dafc7c70958425f4381d73b4bf040
1 /*
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, 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! */
38 #include "gmxpre.h"
40 #include "vcm.h"
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 *init_vcm(FILE *fp, const gmx_groups_t *groups, const t_inputrec *ir)
59 t_vcm *vcm;
60 int g;
62 snew(vcm, 1);
64 vcm->mode = (ir->nstcomm > 0) ? ir->comm_mode : ecmNO;
65 vcm->ndim = ndof_com(ir);
66 vcm->timeStep = ir->nstcomm*ir->delta_t;
68 if (vcm->mode == ecmANGULAR && vcm->ndim < 3)
70 gmx_fatal(FARGS, "Can not have angular comm removal with pbc=%s",
71 epbc_names[ir->ePBC]);
74 if (vcm->mode != ecmNO)
76 vcm->nr = groups->grps[egcVCM].nr;
77 /* Allocate one extra for a possible rest group */
78 vcm->size = vcm->nr + 1;
79 /* We need vcm->nr+1 elements per thread, but to avoid cache
80 * invalidation we add 2 elements to get a 152 byte separation.
82 vcm->stride = vcm->nr + 3;
83 if (vcm->mode == ecmANGULAR)
85 snew(vcm->group_j, vcm->size);
86 snew(vcm->group_x, vcm->size);
87 snew(vcm->group_i, vcm->size);
88 snew(vcm->group_w, vcm->size);
90 snew(vcm->group_p, vcm->size);
91 snew(vcm->group_v, vcm->size);
92 snew(vcm->group_mass, vcm->size);
93 snew(vcm->group_name, vcm->size);
94 snew(vcm->group_ndf, vcm->size);
95 for (g = 0; (g < vcm->nr); g++)
97 vcm->group_ndf[g] = ir->opts.nrdf[g];
100 /* Copy pointer to group names and print it. */
101 if (fp)
103 fprintf(fp, "Center of mass motion removal mode is %s\n",
104 ECOM(vcm->mode));
105 fprintf(fp, "We have the following groups for center of"
106 " mass motion removal:\n");
108 for (g = 0; (g < vcm->nr); g++)
110 vcm->group_name[g] = *groups->grpname[groups->grps[egcVCM].nm_ind[g]];
111 if (fp)
113 fprintf(fp, "%3d: %s\n", g, vcm->group_name[g]);
117 snew(vcm->thread_vcm, gmx_omp_nthreads_get(emntDefault) * vcm->stride);
120 vcm->nFreeze = ir->opts.nFreeze;
122 return vcm;
125 static void update_tensor(const rvec x, real m0, tensor I)
127 real xy, xz, yz;
129 /* Compute inertia tensor contribution due to this atom */
130 xy = x[XX]*x[YY]*m0;
131 xz = x[XX]*x[ZZ]*m0;
132 yz = x[YY]*x[ZZ]*m0;
133 I[XX][XX] += x[XX]*x[XX]*m0;
134 I[YY][YY] += x[YY]*x[YY]*m0;
135 I[ZZ][ZZ] += x[ZZ]*x[ZZ]*m0;
136 I[XX][YY] += xy;
137 I[YY][XX] += xy;
138 I[XX][ZZ] += xz;
139 I[ZZ][XX] += xz;
140 I[YY][ZZ] += yz;
141 I[ZZ][YY] += yz;
144 /* Center of mass code for groups */
145 void calc_vcm_grp(int start, int homenr, t_mdatoms *md,
146 rvec x[], rvec v[], t_vcm *vcm)
148 int nthreads = gmx_omp_nthreads_get(emntDefault);
149 if (vcm->mode != ecmNO)
151 #pragma omp parallel num_threads(nthreads)
153 int t = gmx_omp_get_thread_num();
154 for (int g = 0; g < vcm->size; g++)
156 /* Reset linear momentum */
157 t_vcm_thread *vcm_t = &vcm->thread_vcm[t*vcm->stride + g];
158 vcm_t->mass = 0;
159 clear_rvec(vcm_t->p);
160 if (vcm->mode == ecmANGULAR)
162 /* Reset angular momentum */
163 clear_rvec(vcm_t->j);
164 clear_rvec(vcm_t->x);
165 clear_mat(vcm_t->i);
169 #pragma omp for schedule(static)
170 for (int i = start; i < start+homenr; i++)
172 int g = 0;
173 real m0 = md->massT[i];
174 if (md->cVCM)
176 g = md->cVCM[i];
178 t_vcm_thread *vcm_t = &vcm->thread_vcm[t*vcm->stride + g];
179 /* Calculate linear momentum */
180 vcm_t->mass += m0;
181 int m;
182 for (m = 0; (m < DIM); m++)
184 vcm_t->p[m] += m0*v[i][m];
187 if (vcm->mode == ecmANGULAR)
189 /* Calculate angular momentum */
190 rvec j0;
191 cprod(x[i], v[i], j0);
193 for (m = 0; (m < DIM); m++)
195 vcm_t->j[m] += m0*j0[m];
196 vcm_t->x[m] += m0*x[i][m];
198 /* Update inertia tensor */
199 update_tensor(x[i], m0, vcm_t->i);
203 for (int g = 0; g < vcm->size; g++)
205 /* Reset linear momentum */
206 vcm->group_mass[g] = 0;
207 clear_rvec(vcm->group_p[g]);
208 if (vcm->mode == ecmANGULAR)
210 /* Reset angular momentum */
211 clear_rvec(vcm->group_j[g]);
212 clear_rvec(vcm->group_x[g]);
213 clear_rvec(vcm->group_w[g]);
214 clear_mat(vcm->group_i[g]);
217 for (int t = 0; t < nthreads; t++)
219 t_vcm_thread *vcm_t = &vcm->thread_vcm[t*vcm->stride + g];
220 vcm->group_mass[g] += vcm_t->mass;
221 rvec_inc(vcm->group_p[g], vcm_t->p);
222 if (vcm->mode == ecmANGULAR)
224 rvec_inc(vcm->group_j[g], vcm_t->j);
225 rvec_inc(vcm->group_x[g], vcm_t->x);
226 m_add(vcm_t->i, vcm->group_i[g], vcm->group_i[g]);
234 /*! \brief Remove the COM motion velocity from the velocities
236 * \note This routine should be called from within an OpenMP parallel region.
238 * \tparam numDimensions Correct dimensions 0 to \p numDimensions-1
239 * \param[in] mdatoms The atom property and group information
240 * \param[in,out] v The velocities to correct
241 * \param[in] vcm VCM data
243 template<int numDimensions>
244 static void
245 doStopComMotionLinear(const t_mdatoms &mdatoms,
246 rvec *v,
247 const t_vcm &vcm)
249 const int homenr = mdatoms.homenr;
250 const unsigned short *group_id = mdatoms.cVCM;
252 if (mdatoms.cFREEZE != nullptr)
254 GMX_RELEASE_ASSERT(vcm.nFreeze != nullptr, "Need freeze dimension info with freeze groups");
256 #pragma omp for schedule(static)
257 for (int i = 0; i < homenr; i++)
259 unsigned short vcmGroup = (group_id == nullptr ? 0 : group_id[i]);
260 unsigned short freezeGroup = mdatoms.cFREEZE[i];
261 for (int d = 0; d < numDimensions; d++)
263 if (vcm.nFreeze[freezeGroup][d] == 0)
265 v[i][d] -= vcm.group_v[vcmGroup][d];
270 else if (group_id == nullptr)
272 #pragma omp for schedule(static)
273 for (int i = 0; i < homenr; i++)
275 for (int d = 0; d < numDimensions; d++)
277 v[i][d] -= vcm.group_v[0][d];
281 else
283 #pragma omp for schedule(static)
284 for (int i = 0; i < homenr; i++)
286 const int g = group_id[i];
287 for (int d = 0; d < numDimensions; d++)
289 v[i][d] -= vcm.group_v[g][d];
295 /*! \brief Remove the COM motion velocity from the velocities, correct the coordinates assuming constant acceleration
297 * \note This routine should be called from within an OpenMP parallel region.
299 * \tparam numDimensions Correct dimensions 0 to \p numDimensions-1
300 * \param[in] homenr The number of atoms to correct
301 * \param[in] group_id List of VCM group ids, when nullptr is passed all atoms are assumed to be in group 0
302 * \param[in,out] x The coordinates to correct
303 * \param[in,out] v The velocities to correct
304 * \param[in] vcm VCM data
306 template<int numDimensions>
307 static void
308 doStopComMotionAccelerationCorrection(int homenr,
309 const unsigned short *group_id,
310 rvec * gmx_restrict x,
311 rvec * gmx_restrict v,
312 const t_vcm &vcm)
314 const real xCorrectionFactor = 0.5*vcm.timeStep;
316 if (group_id == nullptr)
318 #pragma omp for schedule(static)
319 for (int i = 0; i < homenr; i++)
321 for (int d = 0; d < numDimensions; d++)
323 x[i][d] -= vcm.group_v[0][d]*xCorrectionFactor;
324 v[i][d] -= vcm.group_v[0][d];
328 else
330 #pragma omp for schedule(static)
331 for (int i = 0; i < homenr; i++)
333 const int g = group_id[i];
334 for (int d = 0; d < numDimensions; d++)
336 x[i][d] -= vcm.group_v[g][d]*xCorrectionFactor;
337 v[i][d] -= vcm.group_v[g][d];
343 void do_stopcm_grp(const t_mdatoms &mdatoms,
344 rvec x[], rvec v[], const t_vcm &vcm)
346 if (vcm.mode != ecmNO)
348 const int homenr = mdatoms.homenr;
349 const unsigned short *group_id = mdatoms.cVCM;
351 int gmx_unused nth = gmx_omp_nthreads_get(emntDefault);
352 #pragma omp parallel num_threads(nth)
354 if (vcm.mode == ecmLINEAR ||
355 vcm.mode == ecmANGULAR ||
356 (vcm.mode == ecmLINEAR_ACCELERATION_CORRECTION && x == nullptr))
358 /* Subtract linear momentum for v */
359 switch (vcm.ndim)
361 case 1:
362 doStopComMotionLinear<1>(mdatoms, v, vcm);
363 break;
364 case 2:
365 doStopComMotionLinear<2>(mdatoms, v, vcm);
366 break;
367 case 3:
368 doStopComMotionLinear<3>(mdatoms, v, vcm);
369 break;
372 else
374 GMX_ASSERT(vcm.mode == ecmLINEAR_ACCELERATION_CORRECTION, "When the mode is not linear or angular, it should be acceleration correction");
375 /* Subtract linear momentum for v and x*/
376 switch (vcm.ndim)
378 case 1:
379 doStopComMotionAccelerationCorrection<1>(homenr, group_id, x, v, vcm);
380 break;
381 case 2:
382 doStopComMotionAccelerationCorrection<2>(homenr, group_id, x, v, vcm);
383 break;
384 case 3:
385 doStopComMotionAccelerationCorrection<3>(homenr, group_id, x, v, vcm);
386 break;
390 if (vcm.mode == ecmANGULAR)
392 /* Subtract angular momentum */
393 GMX_ASSERT(x, "Need x to compute angular momentum correction");
395 int g = 0;
396 #pragma omp for schedule(static)
397 for (int i = 0; i < homenr; i++)
399 if (group_id)
401 g = group_id[i];
403 /* Compute the correction to the velocity for each atom */
404 rvec dv, dx;
405 rvec_sub(x[i], vcm.group_x[g], dx);
406 cprod(vcm.group_w[g], dx, dv);
407 rvec_dec(v[i], dv);
414 static void get_minv(tensor A, tensor B)
416 int m, n;
417 double fac, rfac;
418 tensor tmp;
420 tmp[XX][XX] = A[YY][YY] + A[ZZ][ZZ];
421 tmp[YY][XX] = -A[XX][YY];
422 tmp[ZZ][XX] = -A[XX][ZZ];
423 tmp[XX][YY] = -A[XX][YY];
424 tmp[YY][YY] = A[XX][XX] + A[ZZ][ZZ];
425 tmp[ZZ][YY] = -A[YY][ZZ];
426 tmp[XX][ZZ] = -A[XX][ZZ];
427 tmp[YY][ZZ] = -A[YY][ZZ];
428 tmp[ZZ][ZZ] = A[XX][XX] + A[YY][YY];
430 /* This is a hack to prevent very large determinants */
431 rfac = (tmp[XX][XX]+tmp[YY][YY]+tmp[ZZ][ZZ])/3;
432 if (rfac == 0.0)
434 gmx_fatal(FARGS, "Can not stop center of mass: maybe 2dimensional system");
436 fac = 1.0/rfac;
437 for (m = 0; (m < DIM); m++)
439 for (n = 0; (n < DIM); n++)
441 tmp[m][n] *= fac;
444 gmx::invertMatrix(tmp, B);
445 for (m = 0; (m < DIM); m++)
447 for (n = 0; (n < DIM); n++)
449 B[m][n] *= fac;
454 void check_cm_grp(FILE *fp, t_vcm *vcm, t_inputrec *ir, real Temp_Max)
456 int m, g;
457 real ekcm, ekrot, tm, tm_1, Temp_cm;
458 rvec jcm;
459 tensor Icm;
461 /* First analyse the total results */
462 if (vcm->mode != ecmNO)
464 for (g = 0; (g < vcm->nr); g++)
466 tm = vcm->group_mass[g];
467 if (tm != 0)
469 tm_1 = 1.0/tm;
470 svmul(tm_1, vcm->group_p[g], vcm->group_v[g]);
472 /* Else it's zero anyway! */
474 if (vcm->mode == ecmANGULAR)
476 for (g = 0; (g < vcm->nr); g++)
478 tm = vcm->group_mass[g];
479 if (tm != 0)
481 tm_1 = 1.0/tm;
483 /* Compute center of mass for this group */
484 for (m = 0; (m < DIM); m++)
486 vcm->group_x[g][m] *= tm_1;
489 /* Subtract the center of mass contribution to the
490 * angular momentum
492 cprod(vcm->group_x[g], vcm->group_v[g], jcm);
493 for (m = 0; (m < DIM); m++)
495 vcm->group_j[g][m] -= tm*jcm[m];
498 /* Subtract the center of mass contribution from the inertia
499 * tensor (this is not as trivial as it seems, but due to
500 * some cancellation we can still do it, even in parallel).
502 clear_mat(Icm);
503 update_tensor(vcm->group_x[g], tm, Icm);
504 m_sub(vcm->group_i[g], Icm, vcm->group_i[g]);
506 /* Compute angular velocity, using matrix operation
507 * Since J = I w
508 * we have
509 * w = I^-1 J
511 get_minv(vcm->group_i[g], Icm);
512 mvmul(Icm, vcm->group_j[g], vcm->group_w[g]);
514 /* Else it's zero anyway! */
518 for (g = 0; (g < vcm->nr); g++)
520 ekcm = 0;
521 if (vcm->group_mass[g] != 0 && vcm->group_ndf[g] > 0)
523 for (m = 0; m < vcm->ndim; m++)
525 ekcm += gmx::square(vcm->group_v[g][m]);
527 ekcm *= 0.5*vcm->group_mass[g];
528 Temp_cm = 2*ekcm/vcm->group_ndf[g];
530 if ((Temp_cm > Temp_Max) && fp)
532 fprintf(fp, "Large VCM(group %s): %12.5f, %12.5f, %12.5f, Temp-cm: %12.5e\n",
533 vcm->group_name[g], vcm->group_v[g][XX],
534 vcm->group_v[g][YY], vcm->group_v[g][ZZ], Temp_cm);
537 if (vcm->mode == ecmANGULAR)
539 ekrot = 0.5*iprod(vcm->group_j[g], vcm->group_w[g]);
540 if ((ekrot > 1) && fp && !EI_RANDOM(ir->eI))
542 /* if we have an integrator that may not conserve momenta, skip */
543 tm = vcm->group_mass[g];
544 fprintf(fp, "Group %s with mass %12.5e, Ekrot %12.5e Det(I) = %12.5e\n",
545 vcm->group_name[g], tm, ekrot, det(vcm->group_i[g]));
546 fprintf(fp, " COM: %12.5f %12.5f %12.5f\n",
547 vcm->group_x[g][XX], vcm->group_x[g][YY], vcm->group_x[g][ZZ]);
548 fprintf(fp, " P: %12.5f %12.5f %12.5f\n",
549 vcm->group_p[g][XX], vcm->group_p[g][YY], vcm->group_p[g][ZZ]);
550 fprintf(fp, " V: %12.5f %12.5f %12.5f\n",
551 vcm->group_v[g][XX], vcm->group_v[g][YY], vcm->group_v[g][ZZ]);
552 fprintf(fp, " J: %12.5f %12.5f %12.5f\n",
553 vcm->group_j[g][XX], vcm->group_j[g][YY], vcm->group_j[g][ZZ]);
554 fprintf(fp, " w: %12.5f %12.5f %12.5f\n",
555 vcm->group_w[g][XX], vcm->group_w[g][YY], vcm->group_w[g][ZZ]);
556 pr_rvecs(fp, 0, "Inertia tensor", vcm->group_i[g], DIM);