Make PBC type enumeration into PbcType enum class
[alexxy/gromacs.git] / src / gromacs / pbcutil / pbcmethods.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2019,2020, 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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 #include "gmxpre.h"
36
37 #include "pbcmethods.h"
38
39 #include <algorithm>
40 #include <memory>
41
42 #include "gromacs/pbcutil/pbc.h"
43 #include "gromacs/topology/topology.h"
44 #include "gromacs/utility/fatalerror.h"
45 #include "gromacs/utility/smalloc.h"
46
47 void calc_pbc_cluster(int ecenter, int nrefat, t_topology* top, PbcType pbcType, rvec x[], const int index[], matrix box)
48 {
49     int       m, i, j, j0, j1, jj, ai, aj;
50     int       imin, jmin;
51     real      fac, min_dist2;
52     rvec      dx, xtest, box_center;
53     int       nmol, imol_center;
54     int*      molind;
55     gmx_bool *bMol, *bTmp;
56     rvec *    m_com, *m_shift;
57     t_pbc     pbc;
58     int*      cluster;
59     int*      added;
60     int       ncluster, nadded;
61     real      tmp_r2;
62
63     calc_box_center(ecenter, box, box_center);
64
65     /* Initiate the pbc structure */
66     std::memset(&pbc, 0, sizeof(pbc));
67     set_pbc(&pbc, pbcType, box);
68
69     /* Convert atom index to molecular */
70     nmol   = top->mols.nr;
71     molind = top->mols.index;
72     snew(bMol, nmol);
73     snew(m_com, nmol);
74     snew(m_shift, nmol);
75     snew(cluster, nmol);
76     snew(added, nmol);
77     snew(bTmp, top->atoms.nr);
78
79     for (i = 0; (i < nrefat); i++)
80     {
81         /* Mark all molecules in the index */
82         ai       = index[i];
83         bTmp[ai] = TRUE;
84         /* Binary search assuming the molecules are sorted */
85         j0 = 0;
86         j1 = nmol - 1;
87         while (j0 < j1)
88         {
89             if (ai < molind[j0 + 1])
90             {
91                 j1 = j0;
92             }
93             else if (ai >= molind[j1])
94             {
95                 j0 = j1;
96             }
97             else
98             {
99                 jj = (j0 + j1) / 2;
100                 if (ai < molind[jj + 1])
101                 {
102                     j1 = jj;
103                 }
104                 else
105                 {
106                     j0 = jj;
107                 }
108             }
109         }
110         bMol[j0] = TRUE;
111     }
112     /* Double check whether all atoms in all molecules that are marked are part
113      * of the cluster. Simultaneously compute the center of geometry.
114      */
115     min_dist2   = 10 * gmx::square(trace(box));
116     imol_center = -1;
117     ncluster    = 0;
118     for (i = 0; i < nmol; i++)
119     {
120         for (j = molind[i]; j < molind[i + 1]; j++)
121         {
122             if (bMol[i] && !bTmp[j])
123             {
124                 gmx_fatal(FARGS,
125                           "Molecule %d marked for clustering but not atom %d in it - check your "
126                           "index!",
127                           i + 1, j + 1);
128             }
129             else if (!bMol[i] && bTmp[j])
130             {
131                 gmx_fatal(FARGS,
132                           "Atom %d marked for clustering but not molecule %d - this is an internal "
133                           "error...",
134                           j + 1, i + 1);
135             }
136             else if (bMol[i])
137             {
138                 /* Make molecule whole, move 2nd and higher atom to same periodicity as 1st atom in molecule */
139                 if (j > molind[i])
140                 {
141                     pbc_dx(&pbc, x[j], x[j - 1], dx);
142                     rvec_add(x[j - 1], dx, x[j]);
143                 }
144                 /* Compute center of geometry of molecule - m_com[i] was zeroed when we did snew() on it! */
145                 rvec_inc(m_com[i], x[j]);
146             }
147         }
148         if (bMol[i])
149         {
150             /* Normalize center of geometry */
151             fac = 1.0 / (molind[i + 1] - molind[i]);
152             for (m = 0; (m < DIM); m++)
153             {
154                 m_com[i][m] *= fac;
155             }
156             /* Determine which molecule is closest to the center of the box */
157             pbc_dx(&pbc, box_center, m_com[i], dx);
158             tmp_r2 = iprod(dx, dx);
159
160             if (tmp_r2 < min_dist2)
161             {
162                 min_dist2   = tmp_r2;
163                 imol_center = i;
164             }
165             cluster[ncluster++] = i;
166         }
167     }
168     sfree(bTmp);
169
170     if (ncluster <= 0)
171     {
172         fprintf(stderr, "No molecules selected in the cluster\n");
173         return;
174     }
175     else if (imol_center == -1)
176     {
177         fprintf(stderr, "No central molecules could be found\n");
178         return;
179     }
180
181     nadded            = 0;
182     added[nadded++]   = imol_center;
183     bMol[imol_center] = FALSE;
184
185     while (nadded < ncluster)
186     {
187         /* Find min distance between cluster molecules and those remaining to be added */
188         min_dist2 = 10 * gmx::square(trace(box));
189         imin      = -1;
190         jmin      = -1;
191         /* Loop over added mols */
192         for (i = 0; i < nadded; i++)
193         {
194             ai = added[i];
195             /* Loop over all mols */
196             for (j = 0; j < ncluster; j++)
197             {
198                 aj = cluster[j];
199                 /* check those remaining to be added */
200                 if (bMol[aj])
201                 {
202                     pbc_dx(&pbc, m_com[aj], m_com[ai], dx);
203                     tmp_r2 = iprod(dx, dx);
204                     if (tmp_r2 < min_dist2)
205                     {
206                         min_dist2 = tmp_r2;
207                         imin      = ai;
208                         jmin      = aj;
209                     }
210                 }
211             }
212         }
213
214         /* Add the best molecule */
215         added[nadded++] = jmin;
216         bMol[jmin]      = FALSE;
217         /* Calculate the shift from the ai molecule */
218         pbc_dx(&pbc, m_com[jmin], m_com[imin], dx);
219         rvec_add(m_com[imin], dx, xtest);
220         rvec_sub(xtest, m_com[jmin], m_shift[jmin]);
221         rvec_inc(m_com[jmin], m_shift[jmin]);
222
223         for (j = molind[jmin]; j < molind[jmin + 1]; j++)
224         {
225             rvec_inc(x[j], m_shift[jmin]);
226         }
227         fprintf(stdout, "\rClustering iteration %d of %d...", nadded, ncluster);
228         fflush(stdout);
229     }
230
231     sfree(added);
232     sfree(cluster);
233     sfree(bMol);
234     sfree(m_com);
235     sfree(m_shift);
236
237     fprintf(stdout, "\n");
238 }
239
240 void put_molecule_com_in_box(int      unitcell_enum,
241                              int      ecenter,
242                              t_block* mols,
243                              int      natoms,
244                              t_atom   atom[],
245                              PbcType  pbcType,
246                              matrix   box,
247                              rvec     x[])
248 {
249     int    i, j;
250     int    d;
251     rvec   com, shift, box_center;
252     real   m;
253     double mtot;
254     t_pbc  pbc;
255
256     calc_box_center(ecenter, box, box_center);
257     set_pbc(&pbc, pbcType, box);
258     if (mols->nr <= 0)
259     {
260         gmx_fatal(FARGS,
261                   "There are no molecule descriptions. I need a .tpr file for this pbc option.");
262     }
263     for (i = 0; (i < mols->nr); i++)
264     {
265         /* calc COM */
266         clear_rvec(com);
267         mtot = 0;
268         for (j = mols->index[i]; (j < mols->index[i + 1] && j < natoms); j++)
269         {
270             m = atom[j].m;
271             for (d = 0; d < DIM; d++)
272             {
273                 com[d] += m * x[j][d];
274             }
275             mtot += m;
276         }
277         /* calculate final COM */
278         svmul(1.0 / mtot, com, com);
279
280         /* check if COM is outside box */
281         gmx::RVec newCom;
282         copy_rvec(com, newCom);
283         auto newComArrayRef = gmx::arrayRefFromArray(&newCom, 1);
284         switch (unitcell_enum)
285         {
286             case euRect: put_atoms_in_box(pbcType, box, newComArrayRef); break;
287             case euTric: put_atoms_in_triclinic_unitcell(ecenter, box, newComArrayRef); break;
288             case euCompact:
289                 put_atoms_in_compact_unitcell(pbcType, ecenter, box, newComArrayRef);
290                 break;
291         }
292         rvec_sub(newCom, com, shift);
293         if (norm2(shift) > 0)
294         {
295             if (debug)
296             {
297                 fprintf(debug,
298                         "\nShifting position of molecule %d "
299                         "by %8.3f  %8.3f  %8.3f\n",
300                         i + 1, shift[XX], shift[YY], shift[ZZ]);
301             }
302             for (j = mols->index[i]; (j < mols->index[i + 1] && j < natoms); j++)
303             {
304                 rvec_inc(x[j], shift);
305             }
306         }
307     }
308 }
309
310 void put_residue_com_in_box(int     unitcell_enum,
311                             int     ecenter,
312                             int     natoms,
313                             t_atom  atom[],
314                             PbcType pbcType,
315                             matrix  box,
316                             rvec    x[])
317 {
318     int              i, j, res_start, res_end;
319     int              d, presnr;
320     real             m;
321     double           mtot;
322     rvec             box_center, com, shift;
323     static const int NOTSET = -12347;
324     calc_box_center(ecenter, box, box_center);
325
326     presnr    = NOTSET;
327     res_start = 0;
328     clear_rvec(com);
329     mtot = 0;
330     for (i = 0; i < natoms + 1; i++)
331     {
332         if (i == natoms || (presnr != atom[i].resind && presnr != NOTSET))
333         {
334             /* calculate final COM */
335             res_end = i;
336             svmul(1.0 / mtot, com, com);
337
338             /* check if COM is outside box */
339             gmx::RVec newCom;
340             copy_rvec(com, newCom);
341             auto newComArrayRef = gmx::arrayRefFromArray(&newCom, 1);
342             switch (unitcell_enum)
343             {
344                 case euRect: put_atoms_in_box(pbcType, box, newComArrayRef); break;
345                 case euTric: put_atoms_in_triclinic_unitcell(ecenter, box, newComArrayRef); break;
346                 case euCompact:
347                     put_atoms_in_compact_unitcell(pbcType, ecenter, box, newComArrayRef);
348                     break;
349             }
350             rvec_sub(newCom, com, shift);
351             if (norm2(shift) != 0.0F)
352             {
353                 if (debug)
354                 {
355                     fprintf(debug,
356                             "\nShifting position of residue %d (atoms %d-%d) "
357                             "by %g,%g,%g\n",
358                             atom[res_start].resind + 1, res_start + 1, res_end + 1, shift[XX],
359                             shift[YY], shift[ZZ]);
360                 }
361                 for (j = res_start; j < res_end; j++)
362                 {
363                     rvec_inc(x[j], shift);
364                 }
365             }
366             clear_rvec(com);
367             mtot = 0;
368
369             /* remember start of new residue */
370             res_start = i;
371         }
372         if (i < natoms)
373         {
374             /* calc COM */
375             m = atom[i].m;
376             for (d = 0; d < DIM; d++)
377             {
378                 com[d] += m * x[i][d];
379             }
380             mtot += m;
381
382             presnr = atom[i].resind;
383         }
384     }
385 }
386
387 void center_x(int ecenter, rvec x[], matrix box, int n, int nc, const int ci[])
388 {
389     int  i, m, ai;
390     rvec cmin, cmax, box_center, dx;
391
392     if (nc > 0)
393     {
394         copy_rvec(x[ci[0]], cmin);
395         copy_rvec(x[ci[0]], cmax);
396         for (i = 0; i < nc; i++)
397         {
398             ai = ci[i];
399             for (m = 0; m < DIM; m++)
400             {
401                 if (x[ai][m] < cmin[m])
402                 {
403                     cmin[m] = x[ai][m];
404                 }
405                 else if (x[ai][m] > cmax[m])
406                 {
407                     cmax[m] = x[ai][m];
408                 }
409             }
410         }
411         calc_box_center(ecenter, box, box_center);
412         for (m = 0; m < DIM; m++)
413         {
414             dx[m] = box_center[m] - (cmin[m] + cmax[m]) * 0.5;
415         }
416
417         for (i = 0; i < n; i++)
418         {
419             rvec_inc(x[i], dx);
420         }
421     }
422 }