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! */
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
)
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. */
103 fprintf(fp
, "Center of mass motion removal mode is %s\n",
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
]];
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
;
125 static void update_tensor(const rvec x
, real m0
, tensor I
)
129 /* Compute inertia tensor contribution due to this atom */
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
;
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
];
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
);
169 #pragma omp for schedule(static)
170 for (int i
= start
; i
< start
+homenr
; i
++)
173 real m0
= md
->massT
[i
];
178 t_vcm_thread
*vcm_t
= &vcm
->thread_vcm
[t
*vcm
->stride
+ g
];
179 /* Calculate linear momentum */
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 */
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
>
245 doStopComMotionLinear(const t_mdatoms
&mdatoms
,
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
];
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
>
308 doStopComMotionAccelerationCorrection(int homenr
,
309 const unsigned short *group_id
,
310 rvec
* gmx_restrict x
,
311 rvec
* gmx_restrict v
,
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
];
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 */
362 doStopComMotionLinear
<1>(mdatoms
, v
, vcm
);
365 doStopComMotionLinear
<2>(mdatoms
, v
, vcm
);
368 doStopComMotionLinear
<3>(mdatoms
, v
, vcm
);
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*/
379 doStopComMotionAccelerationCorrection
<1>(homenr
, group_id
, x
, v
, vcm
);
382 doStopComMotionAccelerationCorrection
<2>(homenr
, group_id
, x
, v
, vcm
);
385 doStopComMotionAccelerationCorrection
<3>(homenr
, group_id
, x
, v
, vcm
);
390 if (vcm
.mode
== ecmANGULAR
)
392 /* Subtract angular momentum */
393 GMX_ASSERT(x
, "Need x to compute angular momentum correction");
396 #pragma omp for schedule(static)
397 for (int i
= 0; i
< homenr
; i
++)
403 /* Compute the correction to the velocity for each atom */
405 rvec_sub(x
[i
], vcm
.group_x
[g
], dx
);
406 cprod(vcm
.group_w
[g
], dx
, dv
);
414 static void get_minv(tensor A
, tensor B
)
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;
434 gmx_fatal(FARGS
, "Can not stop center of mass: maybe 2dimensional system");
437 for (m
= 0; (m
< DIM
); m
++)
439 for (n
= 0; (n
< DIM
); n
++)
444 gmx::invertMatrix(tmp
, B
);
445 for (m
= 0; (m
< DIM
); m
++)
447 for (n
= 0; (n
< DIM
); n
++)
454 void check_cm_grp(FILE *fp
, t_vcm
*vcm
, t_inputrec
*ir
, real Temp_Max
)
457 real ekcm
, ekrot
, tm
, tm_1
, Temp_cm
;
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
];
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
];
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
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).
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
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
++)
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
);