c6536b8aa56fcd2bb25e217c429336563ab0c464
[alexxy/gromacs.git] / src / gromacs / gmxana / dlist.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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,2018 by the GROMACS development team.
7  * Copyright (c) 2019,2020,2021, 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.
11  *
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.
16  *
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.
21  *
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.
26  *
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.
34  *
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.
37  */
38 #include "gmxpre.h"
39
40 #include <cstdlib>
41 #include <cstring>
42
43 #include <vector>
44
45 #include "gromacs/gmxana/gstat.h"
46 #include "gromacs/topology/topology.h"
47 #include "gromacs/utility/fatalerror.h"
48
49 std::vector<t_dlist> mk_dlist(FILE*                 log,
50                               const t_atoms*        atoms,
51                               gmx_bool              bPhi,
52                               gmx_bool              bPsi,
53                               gmx_bool              bChi,
54                               gmx_bool              bHChi,
55                               int                   maxchi,
56                               int                   r0,
57                               const ResidueTypeMap& residueTypeMap)
58 {
59     int       i, j, ii;
60     t_dihatms atm, prev;
61     int       nl = 0, nc[edMax];
62     char*     thisres;
63     // Initially, size this to all possible residues. Later it might
64     // be reduced to only handle those residues identified to contain
65     // dihedrals.
66     std::vector<t_dlist> dl(atoms->nres + 1);
67
68     prev.C = prev.Cn[1] = -1; /* Keep the compiler quiet */
69     for (i = 0; (i < edMax); i++)
70     {
71         nc[i] = 0;
72     }
73     i = 0;
74     while (i < atoms->nr)
75     {
76         int ires = atoms->atom[i].resind;
77
78         /* Initiate all atom numbers to -1 */
79         atm.minC = atm.H = atm.N = atm.C = atm.O = atm.minCalpha = -1;
80         for (j = 0; (j < MAXCHI + 3); j++)
81         {
82             atm.Cn[j] = -1;
83         }
84
85         /* Look for atoms in this residue */
86         /* maybe should allow for chis to hydrogens? */
87         while ((i < atoms->nr) && (atoms->atom[i].resind == ires))
88         {
89             if ((std::strcmp(*(atoms->atomname[i]), "H") == 0)
90                 || (std::strcmp(*(atoms->atomname[i]), "H1") == 0)
91                 || (std::strcmp(*(atoms->atomname[i]), "HN") == 0))
92             {
93                 atm.H = i;
94             }
95             else if (std::strcmp(*(atoms->atomname[i]), "N") == 0)
96             {
97                 atm.N = i;
98             }
99             else if (std::strcmp(*(atoms->atomname[i]), "C") == 0)
100             {
101                 atm.C = i;
102             }
103             else if ((std::strcmp(*(atoms->atomname[i]), "O") == 0)
104                      || (std::strcmp(*(atoms->atomname[i]), "O1") == 0)
105                      || (std::strcmp(*(atoms->atomname[i]), "OC1") == 0)
106                      || (std::strcmp(*(atoms->atomname[i]), "OT1") == 0))
107             {
108                 atm.O = i;
109             }
110             else if (std::strcmp(*(atoms->atomname[i]), "CA") == 0)
111             {
112                 atm.Cn[1] = i;
113             }
114             else if (std::strcmp(*(atoms->atomname[i]), "CB") == 0)
115             {
116                 atm.Cn[2] = i;
117             }
118             else if ((std::strcmp(*(atoms->atomname[i]), "CG") == 0)
119                      || (std::strcmp(*(atoms->atomname[i]), "CG1") == 0)
120                      || (std::strcmp(*(atoms->atomname[i]), "OG") == 0)
121                      || (std::strcmp(*(atoms->atomname[i]), "OG1") == 0)
122                      || (std::strcmp(*(atoms->atomname[i]), "SG") == 0))
123             {
124                 atm.Cn[3] = i;
125             }
126             else if ((std::strcmp(*(atoms->atomname[i]), "CD") == 0)
127                      || (std::strcmp(*(atoms->atomname[i]), "CD1") == 0)
128                      || (std::strcmp(*(atoms->atomname[i]), "SD") == 0)
129                      || (std::strcmp(*(atoms->atomname[i]), "OD1") == 0)
130                      || (std::strcmp(*(atoms->atomname[i]), "ND1") == 0))
131             { // NOLINT bugprone-branch-clone
132                 atm.Cn[4] = i;
133             }
134             /* by grs - split the Cn[4] into 2 bits to check allowing dih to H */
135             else if (bHChi
136                      && ((std::strcmp(*(atoms->atomname[i]), "HG") == 0)
137                          || (std::strcmp(*(atoms->atomname[i]), "HG1") == 0)))
138             {
139                 atm.Cn[4] = i;
140             }
141             else if ((std::strcmp(*(atoms->atomname[i]), "CE") == 0)
142                      || (std::strcmp(*(atoms->atomname[i]), "CE1") == 0)
143                      || (std::strcmp(*(atoms->atomname[i]), "OE1") == 0)
144                      || (std::strcmp(*(atoms->atomname[i]), "NE") == 0))
145             {
146                 atm.Cn[5] = i;
147             }
148             else if ((std::strcmp(*(atoms->atomname[i]), "CZ") == 0)
149                      || (std::strcmp(*(atoms->atomname[i]), "NZ") == 0))
150             {
151                 atm.Cn[6] = i;
152             }
153             /* HChi flag here too */
154             else if (bHChi && (std::strcmp(*(atoms->atomname[i]), "NH1") == 0))
155             {
156                 atm.Cn[7] = i;
157             }
158             i++;
159         }
160
161         thisres = *(atoms->resinfo[ires].name);
162
163         /* added by grs - special case for aromatics, whose chis above 2 are
164            not real and produce rubbish output - so set back to -1 */
165         if (std::strcmp(thisres, "PHE") == 0 || std::strcmp(thisres, "TYR") == 0
166             || std::strcmp(thisres, "PTR") == 0 || std::strcmp(thisres, "TRP") == 0
167             || std::strcmp(thisres, "HIS") == 0 || std::strcmp(thisres, "HISA") == 0
168             || std::strcmp(thisres, "HISB") == 0)
169         {
170             for (ii = 5; ii <= 7; ii++)
171             {
172                 atm.Cn[ii] = -1;
173             }
174         }
175         /* end fixing aromatics */
176
177         /* Special case for Pro, has no H */
178         if (std::strcmp(thisres, "PRO") == 0)
179         {
180             atm.H = atm.Cn[4];
181         }
182         /* Carbon from previous residue */
183         if (prev.C != -1)
184         {
185             atm.minC = prev.C;
186         }
187         /* Alpha-carbon from previous residue */
188         if (prev.Cn[1] != -1)
189         {
190             atm.minCalpha = prev.Cn[1];
191         }
192         prev = atm;
193
194         /* Check how many dihedrals we have */
195         if ((atm.N != -1) && (atm.Cn[1] != -1) && (atm.C != -1) && (atm.O != -1)
196             && ((atm.H != -1) || (atm.minC != -1)))
197         {
198             dl[nl].resnr     = ires + 1;
199             dl[nl].atm       = atm;
200             dl[nl].atm.Cn[0] = atm.N;
201             if ((atm.Cn[3] != -1) && (atm.Cn[2] != -1) && (atm.Cn[1] != -1))
202             {
203                 nc[0]++;
204                 if (atm.Cn[4] != -1)
205                 {
206                     nc[1]++;
207                     if (atm.Cn[5] != -1)
208                     {
209                         nc[2]++;
210                         if (atm.Cn[6] != -1)
211                         {
212                             nc[3]++;
213                             if (atm.Cn[7] != -1)
214                             {
215                                 nc[4]++;
216                                 if (atm.Cn[8] != -1)
217                                 {
218                                     nc[5]++;
219                                 }
220                             }
221                         }
222                     }
223                 }
224             }
225             if ((atm.minC != -1) && (atm.minCalpha != -1))
226             {
227                 nc[6]++;
228             }
229
230             /* Prevent use of unknown residues. If one adds a custom residue to
231              * residuetypes.dat but somehow loses it, changes it, or does analysis on
232              * another machine, the residue type will be unknown. */
233             if (residueTypeMap.find(thisres) == residueTypeMap.end())
234             {
235                 gmx_fatal(FARGS,
236                           "Unknown residue %s when searching for residue type.\n"
237                           "Maybe you need to add a custom residue in residuetypes.dat.",
238                           thisres);
239             }
240             dl[nl].residueName = thisres;
241
242             sprintf(dl[nl].name, "%s%d", thisres, ires + r0);
243             nl++;
244         }
245         else if (debug)
246         {
247             fprintf(debug,
248                     "Could not find N atom but could find other atoms"
249                     " in residue %s%d\n",
250                     thisres,
251                     ires + r0);
252         }
253     }
254     // Leave only the residues that were recognized to contain dihedrals
255     dl.resize(nl);
256
257     fprintf(stderr, "\n");
258     fprintf(log, "\n");
259     fprintf(log, "There are %d residues with dihedrals\n", nl);
260     j = 0;
261     if (bPhi)
262     {
263         j += nl;
264     }
265     if (bPsi)
266     {
267         j += nl;
268     }
269     if (bChi)
270     {
271         for (i = 0; (i < maxchi); i++)
272         {
273             j += nc[i];
274         }
275     }
276     fprintf(log, "There are %d dihedrals\n", j);
277     fprintf(log, "Dihedral: ");
278     if (bPhi)
279     {
280         fprintf(log, " Phi  ");
281     }
282     if (bPsi)
283     {
284         fprintf(log, " Psi  ");
285     }
286     if (bChi)
287     {
288         for (i = 0; (i < maxchi); i++)
289         {
290             fprintf(log, "Chi%d  ", i + 1);
291         }
292     }
293     fprintf(log, "\nNumber:   ");
294     if (bPhi)
295     {
296         fprintf(log, "%4d  ", nl);
297     }
298     if (bPsi)
299     {
300         fprintf(log, "%4d  ", nl);
301     }
302     if (bChi)
303     {
304         for (i = 0; (i < maxchi); i++)
305         {
306             fprintf(log, "%4d  ", nc[i]);
307         }
308     }
309     fprintf(log, "\n");
310
311     return dl;
312 }
313
314 gmx_bool has_dihedral(int Dih, const t_dlist& dl)
315 {
316     gmx_bool b = FALSE;
317     int      ddd;
318
319     switch (Dih)
320     {
321         case edPhi:
322             b = ((dl.atm.H != -1) && (dl.atm.N != -1) && (dl.atm.Cn[1] != -1) && (dl.atm.C != -1));
323             break;
324         case edPsi:
325             b = ((dl.atm.N != -1) && (dl.atm.Cn[1] != -1) && (dl.atm.C != -1) && (dl.atm.O != -1));
326             break;
327         case edOmega:
328             b = ((dl.atm.minCalpha != -1) && (dl.atm.minC != -1) && (dl.atm.N != -1)
329                  && (dl.atm.Cn[1] != -1));
330             break;
331         case edChi1:
332         case edChi2:
333         case edChi3:
334         case edChi4:
335         case edChi5:
336         case edChi6:
337             ddd = Dih - edChi1;
338             b = ((dl.atm.Cn[ddd] != -1) && (dl.atm.Cn[ddd + 1] != -1) && (dl.atm.Cn[ddd + 2] != -1)
339                  && (dl.atm.Cn[ddd + 3] != -1));
340             break;
341         default:
342             pr_dlist(stdout, gmx::constArrayRefFromArray(&dl, 1), 1, 0, TRUE, TRUE, TRUE, TRUE, MAXCHI);
343             gmx_fatal(FARGS, "Non existent dihedral %d in file %s, line %d", Dih, __FILE__, __LINE__);
344     }
345     return b;
346 }
347
348 static void pr_one_ro(FILE* fp, const t_dlist& dl, int nDih, real gmx_unused dt)
349 {
350     int k;
351     for (k = 0; k < NROT; k++)
352     {
353         fprintf(fp, "  %6.2f", dl.rot_occ[nDih][k]);
354     }
355     fprintf(fp, "\n");
356 }
357
358 static void pr_ntr_s2(FILE* fp, const t_dlist& dl, int nDih, real dt)
359 {
360     fprintf(fp, "  %6.2f  %6.2f\n", (dt == 0) ? 0 : dl.ntr[nDih] / dt, dl.S2[nDih]);
361 }
362
363 void pr_dlist(FILE*                        fp,
364               gmx::ArrayRef<const t_dlist> dlist,
365               real                         dt,
366               int                          printtype,
367               gmx_bool                     bPhi,
368               gmx_bool                     bPsi,
369               gmx_bool                     bChi,
370               gmx_bool                     bOmega,
371               int                          maxchi)
372 {
373     void (*pr_props)(FILE*, const t_dlist&, int, real);
374
375     /* Analysis of dihedral transitions etc */
376
377     if (printtype == edPrintST)
378     {
379         pr_props = pr_ntr_s2;
380         fprintf(stderr, "Now printing out transitions and OPs...\n");
381     }
382     else
383     {
384         pr_props = pr_one_ro;
385         fprintf(stderr, "Now printing out rotamer occupancies...\n");
386         fprintf(fp, "\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n");
387     }
388
389     /* change atom numbers from 0 based to 1 based */
390     for (const auto& dihedral : dlist)
391     {
392         fprintf(fp, "Residue %s\n", dihedral.name);
393         if (printtype == edPrintST)
394         {
395             fprintf(fp,
396                     " Angle [   AI,   AJ,   AK,   AL]  #tr/ns  S^2D  \n"
397                     "--------------------------------------------\n");
398         }
399         else
400         {
401             fprintf(fp,
402                     " Angle [   AI,   AJ,   AK,   AL]  rotamers  0  g(-)  t  g(+)\n"
403                     "--------------------------------------------\n");
404         }
405         if (bPhi)
406         {
407             fprintf(fp,
408                     "   Phi [%5d,%5d,%5d,%5d]",
409                     (dihedral.atm.H == -1) ? 1 + dihedral.atm.minC : 1 + dihedral.atm.H,
410                     1 + dihedral.atm.N,
411                     1 + dihedral.atm.Cn[1],
412                     1 + dihedral.atm.C);
413             pr_props(fp, dihedral, edPhi, dt);
414         }
415         if (bPsi)
416         {
417             fprintf(fp,
418                     "   Psi [%5d,%5d,%5d,%5d]",
419                     1 + dihedral.atm.N,
420                     1 + dihedral.atm.Cn[1],
421                     1 + dihedral.atm.C,
422                     1 + dihedral.atm.O);
423             pr_props(fp, dihedral, edPsi, dt);
424         }
425         if (bOmega && has_dihedral(edOmega, dihedral))
426         {
427             fprintf(fp,
428                     " Omega [%5d,%5d,%5d,%5d]",
429                     1 + dihedral.atm.minCalpha,
430                     1 + dihedral.atm.minC,
431                     1 + dihedral.atm.N,
432                     1 + dihedral.atm.Cn[1]);
433             pr_props(fp, dihedral, edOmega, dt);
434         }
435         for (int Xi = 0; Xi < MAXCHI; Xi++)
436         {
437             if (bChi && (Xi < maxchi) && (dihedral.atm.Cn[Xi + 3] != -1))
438             {
439                 fprintf(fp,
440                         "   Chi%d[%5d,%5d,%5d,%5d]",
441                         Xi + 1,
442                         1 + dihedral.atm.Cn[Xi],
443                         1 + dihedral.atm.Cn[Xi + 1],
444                         1 + dihedral.atm.Cn[Xi + 2],
445                         1 + dihedral.atm.Cn[Xi + 3]);
446                 pr_props(fp, dihedral, Xi + edChi1, dt); /* Xi+2 was wrong here */
447             }
448         }
449         fprintf(fp, "\n");
450     }
451 }