Apply clang-format to source tree
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / genhydro.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-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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 #include "gmxpre.h"
38
39 #include "genhydro.h"
40
41 #include <cstring>
42 #include <ctime>
43
44 #include "gromacs/fileio/confio.h"
45 #include "gromacs/gmxlib/network.h"
46 #include "gromacs/gmxpreprocess/calch.h"
47 #include "gromacs/gmxpreprocess/h_db.h"
48 #include "gromacs/gmxpreprocess/notset.h"
49 #include "gromacs/gmxpreprocess/pgutil.h"
50 #include "gromacs/gmxpreprocess/ter_db.h"
51 #include "gromacs/math/vec.h"
52 #include "gromacs/topology/atoms.h"
53 #include "gromacs/topology/symtab.h"
54 #include "gromacs/utility/cstringutil.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/fatalerror.h"
57 #include "gromacs/utility/futil.h"
58 #include "gromacs/utility/smalloc.h"
59
60 #include "hackblock.h"
61 #include "resall.h"
62
63 static void copy_atom(const t_atoms* atoms1, int a1, t_atoms* atoms2, int a2, t_symtab* symtab)
64 {
65     atoms2->atom[a2]     = atoms1->atom[a1];
66     atoms2->atomname[a2] = put_symtab(symtab, *atoms1->atomname[a1]);
67 }
68
69 static int pdbasearch_atom(const char* name, int resind, const t_atoms* pdba, const char* searchtype, bool bAllowMissing)
70 {
71     int i;
72
73     for (i = 0; (i < pdba->nr) && (pdba->atom[i].resind != resind); i++) {}
74
75     return search_atom(name, i, pdba, searchtype, bAllowMissing);
76 }
77
78 static void hacksearch_atom(int*                                            ii,
79                             int*                                            jj,
80                             const char*                                     name,
81                             gmx::ArrayRef<const std::vector<MoleculePatch>> patches,
82                             int                                             resind,
83                             const t_atoms*                                  pdba)
84 {
85     int i;
86
87     *ii = -1;
88     if (name[0] == '-')
89     {
90         name++;
91         resind--;
92     }
93     for (i = 0; (i < pdba->nr) && (pdba->atom[i].resind != resind); i++) {}
94     for (; (i < pdba->nr) && (pdba->atom[i].resind == resind) && (*ii < 0); i++)
95     {
96         int j = 0;
97         for (const auto& patch : patches[i])
98         {
99             if (patch.nname == name)
100             {
101                 *ii = i;
102                 *jj = j;
103             }
104             j++;
105         }
106     }
107 }
108
109 static std::vector<MoleculePatchDatabase>
110 getMoleculePatchDatabases(const t_atoms*                              pdba,
111                           gmx::ArrayRef<const MoleculePatchDatabase>  globalPatches,
112                           int                                         nterpairs,
113                           gmx::ArrayRef<MoleculePatchDatabase* const> ntdb,
114                           gmx::ArrayRef<MoleculePatchDatabase* const> ctdb,
115                           gmx::ArrayRef<const int>                    rN,
116                           gmx::ArrayRef<const int>                    rC)
117 {
118     std::vector<MoleculePatchDatabase> modBlock(pdba->nres);
119     /* make space */
120     /* first the termini */
121     for (int i = 0; i < nterpairs; i++)
122     {
123         if (ntdb[i] != nullptr)
124         {
125             copyModificationBlocks(*ntdb[i], &modBlock[rN[i]]);
126         }
127         if (ctdb[i] != nullptr)
128         {
129             mergeAtomAndBondModifications(*ctdb[i], &modBlock[rC[i]]);
130         }
131     }
132     /* then the whole hdb */
133     for (int rnr = 0; rnr < pdba->nres; rnr++)
134     {
135         auto ahptr = search_h_db(globalPatches, *pdba->resinfo[rnr].rtp);
136         if (ahptr != globalPatches.end())
137         {
138             if (modBlock[rnr].name.empty())
139             {
140                 modBlock[rnr].name = ahptr->name;
141             }
142             mergeAtomModifications(*ahptr, &modBlock[rnr]);
143         }
144     }
145     return modBlock;
146 }
147
148 static void expand_hackblocks_one(const MoleculePatchDatabase& newPatch,
149                                   const std::string localAtomName, //NOLINT(performance-unnecessary-value-param)
150                                   std::vector<MoleculePatch>* globalPatches,
151                                   bool                        bN,
152                                   bool                        bC)
153 {
154     /* we'll recursively add atoms to atoms */
155     int pos = 0;
156     for (auto& singlePatch : newPatch.hack)
157     {
158         /* first check if we're in the N- or C-terminus, then we should ignore
159            all hacks involving atoms from resp. previous or next residue
160            (i.e. which name begins with '-' (N) or '+' (C) */
161         bool bIgnore = false;
162         if (bN) /* N-terminus: ignore '-' */
163         {
164             for (int k = 0; k < 4 && !singlePatch.a[k].empty() && !bIgnore; k++)
165             {
166                 bIgnore = singlePatch.a[k][0] == '-';
167             }
168         }
169         if (bC) /* C-terminus: ignore '+' */
170         {
171             for (int k = 0; k < 4 && !singlePatch.a[k].empty() && !bIgnore; k++)
172             {
173                 bIgnore = singlePatch.a[k][0] == '+';
174             }
175         }
176         /* must be either hdb entry (tp>0) or add from tdb (oname==NULL)
177            and first control aton (AI) matches this atom or
178            delete/replace from tdb (oname!=NULL) and oname matches this atom */
179
180         if (!bIgnore
181             && (((singlePatch.tp > 0 || singlePatch.oname.empty()) && singlePatch.a[0] == localAtomName)
182                 || (singlePatch.oname == localAtomName)))
183         {
184             /* now expand all hacks for this atom */
185             for (int k = 0; k < singlePatch.nr; k++)
186             {
187                 globalPatches->push_back(singlePatch);
188                 MoleculePatch* patch = &globalPatches->back();
189                 patch->bXSet         = false;
190                 /* if we're adding (oname==NULL) and don't have a new name (nname)
191                    yet, build it from localAtomName */
192                 if (patch->nname.empty())
193                 {
194                     if (patch->oname.empty())
195                     {
196                         patch->nname    = localAtomName;
197                         patch->nname[0] = 'H';
198                     }
199                 }
200                 else
201                 {
202                     if (gmx_debug_at)
203                     {
204                         fprintf(debug,
205                                 "Hack '%s' %d, replacing nname '%s' with '%s' (old name '%s')\n",
206                                 localAtomName.c_str(), pos, patch->nname.c_str(),
207                                 singlePatch.nname.c_str(),
208                                 patch->oname.empty() ? "" : patch->oname.c_str());
209                     }
210                     patch->nname = singlePatch.nname;
211                 }
212
213                 if (singlePatch.tp == 10 && k == 2)
214                 {
215                     /* This is a water virtual site, not a hydrogen */
216                     /* Ugly hardcoded name hack */
217                     patch->nname.assign("M");
218                 }
219                 else if (singlePatch.tp == 11 && k >= 2)
220                 {
221                     /* This is a water lone pair, not a hydrogen */
222                     /* Ugly hardcoded name hack */
223                     patch->nname.assign(gmx::formatString("LP%d", 1 + k - 2));
224                 }
225                 else if (singlePatch.nr > 1)
226                 {
227                     /* adding more than one atom, number them */
228                     patch->nname.append(gmx::formatString("%d", 1 + k));
229                 }
230             }
231
232             /* add hacks to atoms we've just added */
233             if (singlePatch.tp > 0 || singlePatch.oname.empty())
234             {
235                 for (int k = 0; k < singlePatch.nr; k++)
236                 {
237                     expand_hackblocks_one(
238                             newPatch, globalPatches->at(globalPatches->size() - singlePatch.nr + k).nname,
239                             globalPatches, bN, bC);
240                 }
241             }
242         }
243         pos++;
244     }
245 }
246
247 static void expand_hackblocks(const t_atoms*                             pdba,
248                               gmx::ArrayRef<const MoleculePatchDatabase> hb,
249                               gmx::ArrayRef<std::vector<MoleculePatch>>  patches,
250                               int                                        nterpairs,
251                               gmx::ArrayRef<const int>                   rN,
252                               gmx::ArrayRef<const int>                   rC)
253 {
254     for (int i = 0; i < pdba->nr; i++)
255     {
256         bool bN = false;
257         for (int j = 0; j < nterpairs && !bN; j++)
258         {
259             bN = pdba->atom[i].resind == rN[j];
260         }
261         bool bC = false;
262         for (int j = 0; j < nterpairs && !bC; j++)
263         {
264             bC = pdba->atom[i].resind == rC[j];
265         }
266
267         /* add hacks to this atom */
268         expand_hackblocks_one(hb[pdba->atom[i].resind], *pdba->atomname[i], &patches[i], bN, bC);
269     }
270 }
271
272 static int check_atoms_present(const t_atoms* pdba, gmx::ArrayRef<std::vector<MoleculePatch>> patches)
273 {
274     int nadd = 0;
275     for (int i = 0; i < pdba->nr; i++)
276     {
277         int rnr = pdba->atom[i].resind;
278         for (auto patch = patches[i].begin(); patch != patches[i].end(); patch++)
279         {
280             switch (patch->type())
281             {
282                 case MoleculePatchType::Add:
283                 {
284                     /* we're adding */
285                     /* check if the atom is already present */
286                     int k = pdbasearch_atom(patch->nname.c_str(), rnr, pdba, "check", TRUE);
287                     if (k != -1)
288                     {
289                         /* We found the added atom. */
290                         patch->bAlreadyPresent = true;
291                     }
292                     else
293                     {
294                         patch->bAlreadyPresent = false;
295                         /* count how many atoms we'll add */
296                         nadd++;
297                     }
298                     break;
299                 }
300                 case MoleculePatchType::Delete:
301                 {
302                     /* we're deleting */
303                     nadd--;
304                     break;
305                 }
306                 case MoleculePatchType::Replace: { break;
307                 }
308                 default: { GMX_THROW(gmx::InternalError("Case not handled"));
309                 }
310             }
311         }
312     }
313     return nadd;
314 }
315
316 static void calc_all_pos(const t_atoms*                            pdba,
317                          gmx::ArrayRef<const gmx::RVec>            x,
318                          gmx::ArrayRef<std::vector<MoleculePatch>> patches,
319                          bool                                      bCheckMissing)
320 {
321     int ii, l = 0;
322 #define MAXH 4
323     rvec xa[4];    /* control atoms for calc_h_pos */
324     rvec xh[MAXH]; /* hydrogen positions from calc_h_pos */
325
326     int jj = 0;
327
328     for (int i = 0; i < pdba->nr; i++)
329     {
330         int rnr = pdba->atom[i].resind;
331         for (auto patch = patches[i].begin(); patch != patches[i].end(); patch += patch->nr)
332         {
333             GMX_RELEASE_ASSERT(patch < patches[i].end(),
334                                "The number of patches in the last patch can not exceed the total "
335                                "number of patches");
336             /* check if we're adding: */
337             if (patch->type() == MoleculePatchType::Add && patch->tp > 0)
338             {
339                 bool bFoundAll = true;
340                 for (int m = 0; (m < patch->nctl && bFoundAll); m++)
341                 {
342                     int ia = pdbasearch_atom(patch->a[m].c_str(), rnr, pdba,
343                                              bCheckMissing ? "atom" : "check", !bCheckMissing);
344                     if (ia < 0)
345                     {
346                         /* not found in original atoms, might still be in
347                          * the patch Instructions (patches) */
348                         hacksearch_atom(&ii, &jj, patch->a[m].c_str(), patches, rnr, pdba);
349                         if (ii >= 0)
350                         {
351                             copy_rvec(patches[ii][jj].newx, xa[m]);
352                         }
353                         else
354                         {
355                             bFoundAll = false;
356                             if (bCheckMissing)
357                             {
358                                 gmx_fatal(FARGS,
359                                           "Atom %s not found in residue %s %d"
360                                           ", rtp entry %s"
361                                           " while adding hydrogens",
362                                           patch->a[m].c_str(), *pdba->resinfo[rnr].name,
363                                           pdba->resinfo[rnr].nr, *pdba->resinfo[rnr].rtp);
364                             }
365                         }
366                     }
367                     else
368                     {
369                         copy_rvec(x[ia], xa[m]);
370                     }
371                 }
372                 if (bFoundAll)
373                 {
374                     for (int m = 0; (m < MAXH); m++)
375                     {
376                         for (int d = 0; d < DIM; d++)
377                         {
378                             if (m < patch->nr)
379                             {
380                                 xh[m][d] = 0;
381                             }
382                             else
383                             {
384                                 xh[m][d] = NOTSET;
385                             }
386                         }
387                     }
388                     calc_h_pos(patch->tp, xa, xh, &l);
389                     for (int m = 0; m < patch->nr; m++)
390                     {
391                         auto next = patch + m;
392                         copy_rvec(xh[m], next->newx);
393                         next->bXSet = true;
394                     }
395                 }
396             }
397         }
398     }
399 }
400
401 static int add_h_low(t_atoms**                                   initialAtoms,
402                      t_atoms**                                   modifiedAtoms,
403                      std::vector<gmx::RVec>*                     xptr,
404                      gmx::ArrayRef<const MoleculePatchDatabase>  globalPatches,
405                      t_symtab*                                   symtab,
406                      const int                                   nterpairs,
407                      gmx::ArrayRef<MoleculePatchDatabase* const> ntdb,
408                      gmx::ArrayRef<MoleculePatchDatabase* const> ctdb,
409                      gmx::ArrayRef<const int>                    rN,
410                      gmx::ArrayRef<const int>                    rC,
411                      const bool                                  bCheckMissing)
412 {
413     int                                     nadd;
414     int                                     newi, natoms, nalreadypresent;
415     std::vector<std::vector<MoleculePatch>> patches;
416     std::vector<gmx::RVec>                  xn;
417
418     t_atoms* pdba = *initialAtoms;
419
420     /* set flags for adding hydrogens (according to hdb) */
421     natoms = pdba->nr;
422
423     {
424         /* We'll have to do all the hard work */
425         /* first get all the hackblocks for each residue: */
426         std::vector<MoleculePatchDatabase> hb =
427                 getMoleculePatchDatabases(pdba, globalPatches, nterpairs, ntdb, ctdb, rN, rC);
428
429         /* expand the hackblocks to atom level */
430         patches.resize(natoms);
431         expand_hackblocks(pdba, hb, patches, nterpairs, rN, rC);
432     }
433
434     /* Now calc the positions */
435     calc_all_pos(pdba, *xptr, patches, bCheckMissing);
436
437     /* we don't have to add atoms that are already present in initialAtoms,
438        so we will remove them from the patches (MoleculePatch) */
439     nadd = check_atoms_present(pdba, patches);
440
441     /* Copy old atoms, making space for new ones */
442     if (nadd > 0)
443     {
444         srenew(*modifiedAtoms, 1);
445         init_t_atoms(*modifiedAtoms, natoms + nadd, FALSE);
446         (*modifiedAtoms)->nres = pdba->nres;
447         srenew((*modifiedAtoms)->resinfo, pdba->nres);
448         std::copy(pdba->resinfo, pdba->resinfo + pdba->nres, (*modifiedAtoms)->resinfo);
449     }
450     if (nadd == 0)
451     {
452         return natoms;
453     }
454
455     xn.resize(natoms + nadd);
456     newi = 0;
457     for (int i = 0; (i < natoms); i++)
458     {
459         /* check if this atom wasn't scheduled for deletion */
460         if (patches[i].empty() || (!patches[i][0].nname.empty()))
461         {
462             if (newi >= natoms + nadd)
463             {
464                 /*gmx_fatal(FARGS,"Not enough space for adding atoms");*/
465                 nadd += 10;
466                 xn.resize(natoms + nadd);
467                 srenew((*modifiedAtoms)->atom, natoms + nadd);
468                 srenew((*modifiedAtoms)->atomname, natoms + nadd);
469             }
470             copy_atom(pdba, i, (*modifiedAtoms), newi, symtab);
471             copy_rvec((*xptr)[i], xn[newi]);
472             /* process the hacks for this atom */
473             nalreadypresent = 0;
474             for (auto patch = patches[i].begin(); patch != patches[i].end(); patch++)
475             {
476                 if (patch->type() == MoleculePatchType::Add) /* add */
477                 {
478                     newi++;
479                     if (newi >= natoms + nadd)
480                     {
481                         /* gmx_fatal(FARGS,"Not enough space for adding atoms");*/
482                         nadd += 10;
483                         xn.resize(natoms + nadd);
484                         srenew((*modifiedAtoms)->atom, natoms + nadd);
485                         srenew((*modifiedAtoms)->atomname, natoms + nadd);
486                     }
487                     (*modifiedAtoms)->atom[newi].resind = pdba->atom[i].resind;
488                 }
489                 if (!patch->nname.empty()
490                     && (patch->oname.empty() || patch->oname == *(*modifiedAtoms)->atomname[newi]))
491                 {
492                     /* add or replace */
493                     if (patch->type() == MoleculePatchType::Add && patch->bAlreadyPresent)
494                     {
495                         /* This atom is already present, copy it from the input. */
496                         nalreadypresent++;
497                         copy_atom(pdba, i + nalreadypresent, (*modifiedAtoms), newi, symtab);
498                         copy_rvec((*xptr)[i + nalreadypresent], xn[newi]);
499                     }
500                     else
501                     {
502                         if (gmx_debug_at)
503                         {
504                             fprintf(debug, "Replacing %d '%s' with (old name '%s') %s\n", newi,
505                                     ((*modifiedAtoms)->atomname[newi] && *(*modifiedAtoms)->atomname[newi])
506                                             ? *(*modifiedAtoms)->atomname[newi]
507                                             : "",
508                                     patch->oname.empty() ? "" : patch->oname.c_str(),
509                                     patch->nname.c_str());
510                         }
511                         (*modifiedAtoms)->atomname[newi] = put_symtab(symtab, patch->nname.c_str());
512                         if (patch->bXSet)
513                         {
514                             copy_rvec(patch->newx, xn[newi]);
515                         }
516                     }
517                     if (debug)
518                     {
519                         fprintf(debug, " %s %g %g", *(*modifiedAtoms)->atomname[newi],
520                                 (*modifiedAtoms)->atom[newi].m, (*modifiedAtoms)->atom[newi].q);
521                     }
522                 }
523             }
524             newi++;
525             i += nalreadypresent;
526         }
527     }
528     (*modifiedAtoms)->nr = newi;
529
530     done_atom(pdba);
531     *initialAtoms = *modifiedAtoms;
532
533     *xptr = xn;
534
535     return newi;
536 }
537
538 int add_h(t_atoms**                                   initialAtoms,
539           t_atoms**                                   localAtoms,
540           std::vector<gmx::RVec>*                     xptr,
541           gmx::ArrayRef<const MoleculePatchDatabase>  globalPatches,
542           t_symtab*                                   symtab,
543           const int                                   nterpairs,
544           gmx::ArrayRef<MoleculePatchDatabase* const> ntdb,
545           gmx::ArrayRef<MoleculePatchDatabase* const> ctdb,
546           gmx::ArrayRef<const int>                    rN,
547           gmx::ArrayRef<const int>                    rC,
548           const bool                                  bAllowMissing)
549 {
550     int nold, nnew, niter;
551
552     /* Here we loop to be able to add atoms to added atoms.
553      * We should not check for missing atoms here.
554      */
555     niter = 0;
556     nnew  = 0;
557     do
558     {
559         nold = nnew;
560         nnew = add_h_low(initialAtoms, localAtoms, xptr, globalPatches, symtab, nterpairs, ntdb,
561                          ctdb, rN, rC, FALSE);
562         niter++;
563         if (niter > 100)
564         {
565             gmx_fatal(FARGS,
566                       "More than 100 iterations of add_h. Maybe you are trying to replace an added "
567                       "atom (this is not supported)?");
568         }
569     } while (nnew > nold);
570
571     if (!bAllowMissing)
572     {
573         /* Call add_h_low once more, now only for the missing atoms check */
574         add_h_low(initialAtoms, localAtoms, xptr, globalPatches, symtab, nterpairs, ntdb, ctdb, rN,
575                   rC, TRUE);
576     }
577
578     return nnew;
579 }