Precision fix for rescbt code.
[gromacs.git] / src / gromacs / gmxpreprocess / genhydro.cpp
blob8599ec0a6737901b875e88cee3efb385ba0f6835
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,2017,2018,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.
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 #include "gmxpre.h"
39 #include "genhydro.h"
41 #include <cstring>
42 #include <ctime>
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"
60 #include "hackblock.h"
61 #include "resall.h"
63 static void copy_atom(const t_atoms *atoms1, int a1, t_atoms *atoms2, int a2, t_symtab *symtab)
65 atoms2->atom[a2] = atoms1->atom[a1];
66 atoms2->atomname[a2] = put_symtab(symtab, *atoms1->atomname[a1]);
69 static int pdbasearch_atom(const char *name, int resind, const t_atoms *pdba,
70 const char *searchtype, bool bAllowMissing)
72 int i;
74 for (i = 0; (i < pdba->nr) && (pdba->atom[i].resind != resind); i++)
79 return search_atom(name, i, pdba,
80 searchtype, bAllowMissing);
83 static void hacksearch_atom(int *ii, int *jj, const char *name,
84 gmx::ArrayRef < const std::vector < MoleculePatch>> patches,
85 int resind, const t_atoms *pdba)
87 int i;
89 *ii = -1;
90 if (name[0] == '-')
92 name++;
93 resind--;
95 for (i = 0; (i < pdba->nr) && (pdba->atom[i].resind != resind); i++)
99 for (; (i < pdba->nr) && (pdba->atom[i].resind == resind) && (*ii < 0); i++)
101 int j = 0;
102 for (const auto &patch : patches[i])
104 if (patch.nname == name)
106 *ii = i;
107 *jj = j;
109 j++;
115 static std::vector<MoleculePatchDatabase>
116 getMoleculePatchDatabases(const t_atoms *pdba,
117 gmx::ArrayRef<MoleculePatchDatabase> globalPatches,
118 int nterpairs,
119 gmx::ArrayRef<MoleculePatchDatabase *> ntdb,
120 gmx::ArrayRef<MoleculePatchDatabase *> ctdb,
121 gmx::ArrayRef<const int> rN,
122 gmx::ArrayRef<const int> rC)
124 std::vector<MoleculePatchDatabase> modBlock(pdba->nres);
125 /* make space */
126 /* first the termini */
127 for (int i = 0; i < nterpairs; i++)
129 if (ntdb[i] != nullptr)
131 copyModificationBlocks(*ntdb[i], &modBlock[rN[i]]);
133 if (ctdb[i] != nullptr)
135 mergeAtomAndBondModifications(*ctdb[i], &modBlock[rC[i]]);
138 /* then the whole hdb */
139 for (int rnr = 0; rnr < pdba->nres; rnr++)
141 auto ahptr = search_h_db(globalPatches, *pdba->resinfo[rnr].rtp);
142 if (ahptr != globalPatches.end())
144 if (globalPatches[rnr].name.empty())
146 globalPatches[rnr].name = ahptr->name;
148 mergeAtomModifications(*ahptr, &modBlock[rnr]);
151 return modBlock;
154 static void expand_hackblocks_one(const MoleculePatchDatabase &newPatch,
155 const std::string localAtomName, //NOLINT(performance-unnecessary-value-param)
156 std::vector<MoleculePatch> *globalPatches,
157 bool bN, bool bC)
159 /* we'll recursively add atoms to atoms */
160 int pos = 0;
161 for (auto &singlePatch : newPatch.hack)
163 /* first check if we're in the N- or C-terminus, then we should ignore
164 all hacks involving atoms from resp. previous or next residue
165 (i.e. which name begins with '-' (N) or '+' (C) */
166 bool bIgnore = false;
167 if (bN) /* N-terminus: ignore '-' */
169 for (int k = 0; k < 4 && !singlePatch.a[k].empty() && !bIgnore; k++)
171 bIgnore = singlePatch.a[k][0] == '-';
174 if (bC) /* C-terminus: ignore '+' */
176 for (int k = 0; k < 4 && !singlePatch.a[k].empty() && !bIgnore; k++)
178 bIgnore = singlePatch.a[k][0] == '+';
181 /* must be either hdb entry (tp>0) or add from tdb (oname==NULL)
182 and first control aton (AI) matches this atom or
183 delete/replace from tdb (oname!=NULL) and oname matches this atom */
185 if (!bIgnore &&
186 ( ( ( singlePatch.tp > 0 || singlePatch.oname.empty() ) &&
187 singlePatch.a[0] == localAtomName ) ||
188 ( singlePatch.oname == localAtomName) ) )
190 /* now expand all hacks for this atom */
191 for (int k = 0; k < singlePatch.nr; k++)
193 globalPatches->push_back(singlePatch);
194 MoleculePatch *patch = &globalPatches->back();
195 patch->bXSet = false;
196 /* if we're adding (oname==NULL) and don't have a new name (nname)
197 yet, build it from localAtomName */
198 if (patch->nname.empty())
200 if (patch->oname.empty())
202 patch->nname = localAtomName;
203 patch->nname[0] = 'H';
206 else
208 if (gmx_debug_at)
210 fprintf(debug, "Hack '%s' %d, replacing nname '%s' with '%s' (old name '%s')\n",
211 localAtomName.c_str(), pos,
212 patch->nname.c_str(), singlePatch.nname.c_str(),
213 patch->oname.empty() ? "" : patch->oname.c_str());
215 patch->nname = singlePatch.nname;
218 if (singlePatch.tp == 10 && k == 2)
220 /* This is a water virtual site, not a hydrogen */
221 /* Ugly hardcoded name hack */
222 patch->nname.assign("M");
224 else if (singlePatch.tp == 11 && k >= 2)
226 /* This is a water lone pair, not a hydrogen */
227 /* Ugly hardcoded name hack */
228 patch->nname.assign(gmx::formatString("LP%d", 1+k-2));
230 else if (singlePatch.nr > 1)
232 /* adding more than one atom, number them */
233 patch->nname.append(gmx::formatString("%d", 1+k));
237 /* add hacks to atoms we've just added */
238 if (singlePatch.tp > 0 || singlePatch.oname.empty())
240 for (int k = 0; k < singlePatch.nr; k++)
242 expand_hackblocks_one(newPatch,
243 globalPatches->at(
244 globalPatches->size() -
245 singlePatch.nr +
246 k).nname,
247 globalPatches, bN, bC);
251 pos++;
255 static void expand_hackblocks(const t_atoms *pdba,
256 gmx::ArrayRef<const MoleculePatchDatabase> hb,
257 gmx::ArrayRef < std::vector < MoleculePatch>> patches,
258 int nterpairs,
259 gmx::ArrayRef<const int> rN,
260 gmx::ArrayRef<const int> rC)
262 for (int i = 0; i < pdba->nr; i++)
264 bool bN = false;
265 for (int j = 0; j < nterpairs && !bN; j++)
267 bN = pdba->atom[i].resind == rN[j];
269 bool bC = false;
270 for (int j = 0; j < nterpairs && !bC; j++)
272 bC = pdba->atom[i].resind == rC[j];
275 /* add hacks to this atom */
276 expand_hackblocks_one(hb[pdba->atom[i].resind], *pdba->atomname[i],
277 &patches[i], bN, bC);
281 static int check_atoms_present(const t_atoms *pdba,
282 gmx::ArrayRef < std::vector < MoleculePatch>> patches)
284 int nadd = 0;
285 for (int i = 0; i < pdba->nr; i++)
287 int rnr = pdba->atom[i].resind;
288 for (auto patch = patches[i].begin();
289 patch != patches[i].end();
290 patch++)
292 switch (patch->type())
294 case MoleculePatchType::Add:
296 /* we're adding */
297 /* check if the atom is already present */
298 int k = pdbasearch_atom(patch->nname.c_str(), rnr, pdba, "check", TRUE);
299 if (k != -1)
301 /* We found the added atom. */
302 patch->bAlreadyPresent = true;
304 else
306 patch->bAlreadyPresent = false;
307 /* count how many atoms we'll add */
308 nadd++;
310 break;
312 case MoleculePatchType::Delete:
314 /* we're deleting */
315 nadd--;
316 break;
318 case MoleculePatchType::Replace:
320 break;
322 default:
324 GMX_THROW(gmx::InternalError("Case not handled"));
329 return nadd;
332 static void calc_all_pos(const t_atoms *pdba,
333 gmx::ArrayRef<const gmx::RVec> x,
334 gmx::ArrayRef < std::vector < MoleculePatch>> patches,
335 bool bCheckMissing)
337 int ii, l = 0;
338 #define MAXH 4
339 rvec xa[4]; /* control atoms for calc_h_pos */
340 rvec xh[MAXH]; /* hydrogen positions from calc_h_pos */
342 int jj = 0;
344 for (int i = 0; i < pdba->nr; i++)
346 int rnr = pdba->atom[i].resind;
347 for (auto patch = patches[i].begin(); patch != patches[i].end();
348 patch += patch->nr)
350 GMX_RELEASE_ASSERT(patch < patches[i].end(),
351 "The number of patches in the last patch can not exceed the total number of patches");
352 /* check if we're adding: */
353 if (patch->type() == MoleculePatchType::Add && patch->tp > 0)
355 bool bFoundAll = true;
356 for (int m = 0; (m < patch->nctl && bFoundAll); m++)
358 int ia = pdbasearch_atom(patch->a[m].c_str(), rnr, pdba,
359 bCheckMissing ? "atom" : "check",
360 !bCheckMissing);
361 if (ia < 0)
363 /* not found in original atoms, might still be in
364 * the patch Instructions (patches) */
365 hacksearch_atom(&ii, &jj, patch->a[m].c_str(), patches, rnr, pdba);
366 if (ii >= 0)
368 copy_rvec(patches[ii][jj].newx, xa[m]);
370 else
372 bFoundAll = false;
373 if (bCheckMissing)
375 gmx_fatal(FARGS, "Atom %s not found in residue %s %d"
376 ", rtp entry %s"
377 " while adding hydrogens",
378 patch->a[m].c_str(),
379 *pdba->resinfo[rnr].name,
380 pdba->resinfo[rnr].nr,
381 *pdba->resinfo[rnr].rtp);
385 else
387 copy_rvec(x[ia], xa[m]);
390 if (bFoundAll)
392 for (int m = 0; (m < MAXH); m++)
394 for (int d = 0; d < DIM; d++)
396 if (m < patch->nr)
398 xh[m][d] = 0;
400 else
402 xh[m][d] = NOTSET;
406 calc_h_pos(patch->tp, xa, xh, &l);
407 for (int m = 0; m < patch->nr; m++)
409 auto next = patch + m;
410 copy_rvec(xh[m], next->newx);
411 next->bXSet = true;
419 static int add_h_low(t_atoms **initialAtoms,
420 t_atoms **modifiedAtoms,
421 std::vector<gmx::RVec> *xptr,
422 gmx::ArrayRef<MoleculePatchDatabase> globalPatches,
423 t_symtab *symtab,
424 int nterpairs,
425 std::vector<MoleculePatchDatabase *> ntdb,
426 std::vector<MoleculePatchDatabase *> ctdb,
427 gmx::ArrayRef<int> rN,
428 gmx::ArrayRef<int> rC,
429 bool bCheckMissing)
431 int nadd;
432 int newi, natoms, nalreadypresent;
433 std::vector < std::vector < MoleculePatch>> patches;
434 std::vector<gmx::RVec> xn;
436 t_atoms *pdba = *initialAtoms;
438 /* set flags for adding hydrogens (according to hdb) */
439 natoms = pdba->nr;
442 /* We'll have to do all the hard work */
443 /* first get all the hackblocks for each residue: */
444 std::vector<MoleculePatchDatabase> hb =
445 getMoleculePatchDatabases(pdba, globalPatches, nterpairs, ntdb, ctdb, rN, rC);
447 /* expand the hackblocks to atom level */
448 patches.resize(natoms);
449 expand_hackblocks(pdba, hb, patches, nterpairs, rN, rC);
452 /* Now calc the positions */
453 calc_all_pos(pdba, *xptr, patches, bCheckMissing);
455 /* we don't have to add atoms that are already present in initialAtoms,
456 so we will remove them from the patches (MoleculePatch) */
457 nadd = check_atoms_present(pdba, patches);
459 /* Copy old atoms, making space for new ones */
460 if (nadd > 0)
462 srenew(*modifiedAtoms, 1);
463 init_t_atoms(*modifiedAtoms, natoms+nadd, FALSE);
464 (*modifiedAtoms)->nres = pdba->nres;
465 srenew((*modifiedAtoms)->resinfo, pdba->nres);
466 std::copy(pdba->resinfo, pdba->resinfo + pdba->nres, (*modifiedAtoms)->resinfo);
468 if (nadd == 0)
470 return natoms;
473 xn.resize(natoms+nadd);
474 newi = 0;
475 for (int i = 0; (i < natoms); i++)
477 /* check if this atom wasn't scheduled for deletion */
478 if (patches[i].empty() || (!patches[i][0].nname.empty()) )
480 if (newi >= natoms+nadd)
482 /*gmx_fatal(FARGS,"Not enough space for adding atoms");*/
483 nadd += 10;
484 xn.resize(natoms+nadd);
485 srenew((*modifiedAtoms)->atom, natoms+nadd);
486 srenew((*modifiedAtoms)->atomname, natoms+nadd);
488 copy_atom(pdba, i, (*modifiedAtoms), newi, symtab);
489 copy_rvec((*xptr)[i], xn[newi]);
490 /* process the hacks for this atom */
491 nalreadypresent = 0;
492 for (auto patch = patches[i].begin();
493 patch != patches[i].end();
494 patch++)
496 if (patch->type() == MoleculePatchType::Add) /* add */
498 newi++;
499 if (newi >= natoms+nadd)
501 /* gmx_fatal(FARGS,"Not enough space for adding atoms");*/
502 nadd += 10;
503 xn.resize(natoms+nadd);
504 srenew((*modifiedAtoms)->atom, natoms+nadd);
505 srenew((*modifiedAtoms)->atomname, natoms+nadd);
507 (*modifiedAtoms)->atom[newi].resind = pdba->atom[i].resind;
509 if (!patch->nname.empty() &&
510 (patch->oname.empty() ||
511 patch->oname == *(*modifiedAtoms)->atomname[newi]))
513 /* add or replace */
514 if (patch->type() == MoleculePatchType::Add && patch->bAlreadyPresent)
516 /* This atom is already present, copy it from the input. */
517 nalreadypresent++;
518 copy_atom(pdba, i+nalreadypresent, (*modifiedAtoms), newi, symtab);
519 copy_rvec((*xptr)[i+nalreadypresent], xn[newi]);
521 else
523 if (gmx_debug_at)
525 fprintf(debug, "Replacing %d '%s' with (old name '%s') %s\n",
526 newi,
527 ((*modifiedAtoms)->atomname[newi] &&
528 *(*modifiedAtoms)->atomname[newi]) ?
529 *(*modifiedAtoms)->atomname[newi] : "",
530 patch->oname.empty() ? "" : patch->oname.c_str(),
531 patch->nname.c_str());
533 (*modifiedAtoms)->atomname[newi] = put_symtab(symtab, patch->nname.c_str());
534 if (patch->bXSet)
536 copy_rvec(patch->newx, xn[newi]);
539 if (debug)
541 fprintf(debug, " %s %g %g", *(*modifiedAtoms)->atomname[newi],
542 (*modifiedAtoms)->atom[newi].m, (*modifiedAtoms)->atom[newi].q);
546 newi++;
547 i += nalreadypresent;
550 (*modifiedAtoms)->nr = newi;
552 done_atom(pdba);
553 *initialAtoms = *modifiedAtoms;
555 *xptr = xn;
557 return newi;
560 int add_h(t_atoms **initialAtoms,
561 t_atoms **localAtoms,
562 std::vector<gmx::RVec> *xptr,
563 gmx::ArrayRef<MoleculePatchDatabase> globalPatches,
564 t_symtab *symtab,
565 int nterpairs,
566 const std::vector<MoleculePatchDatabase *> &ntdb,
567 const std::vector<MoleculePatchDatabase *> &ctdb,
568 gmx::ArrayRef<int> rN,
569 gmx::ArrayRef<int> rC,
570 bool bAllowMissing)
572 int nold, nnew, niter;
574 /* Here we loop to be able to add atoms to added atoms.
575 * We should not check for missing atoms here.
577 niter = 0;
578 nnew = 0;
581 nold = nnew;
582 nnew = add_h_low(initialAtoms, localAtoms, xptr, globalPatches, symtab, nterpairs, ntdb, ctdb, rN, rC, FALSE);
583 niter++;
584 if (niter > 100)
586 gmx_fatal(FARGS, "More than 100 iterations of add_h. Maybe you are trying to replace an added atom (this is not supported)?");
589 while (nnew > nold);
591 if (!bAllowMissing)
593 /* Call add_h_low once more, now only for the missing atoms check */
594 add_h_low(initialAtoms, localAtoms, xptr, globalPatches, symtab, nterpairs, ntdb, ctdb, rN, rC, TRUE);
597 return nnew;