Remove unused function generate_excls and make clean_excls static
[gromacs.git] / src / gromacs / gmxpreprocess / gen_vsite.cpp
blob7255916acb83999a3b1fe3fc0bec5e2954b4a1e0
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,2016,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 "gen_vsite.h"
41 #include <cmath>
42 #include <cstdio>
43 #include <cstdlib>
44 #include <cstring>
46 #include <algorithm>
47 #include <string>
48 #include <utility>
49 #include <vector>
51 #include "gromacs/fileio/pdbio.h"
52 #include "gromacs/gmxpreprocess/add_par.h"
53 #include "gromacs/gmxpreprocess/fflibutil.h"
54 #include "gromacs/gmxpreprocess/gpp_atomtype.h"
55 #include "gromacs/gmxpreprocess/grompp_impl.h"
56 #include "gromacs/gmxpreprocess/notset.h"
57 #include "gromacs/gmxpreprocess/toputil.h"
58 #include "gromacs/math/functions.h"
59 #include "gromacs/math/units.h"
60 #include "gromacs/math/vec.h"
61 #include "gromacs/mdtypes/md_enums.h"
62 #include "gromacs/topology/ifunc.h"
63 #include "gromacs/topology/residuetypes.h"
64 #include "gromacs/topology/symtab.h"
65 #include "gromacs/utility/basedefinitions.h"
66 #include "gromacs/utility/cstringutil.h"
67 #include "gromacs/utility/fatalerror.h"
68 #include "gromacs/utility/futil.h"
69 #include "gromacs/utility/real.h"
70 #include "gromacs/utility/smalloc.h"
72 #include "hackblock.h"
73 #include "resall.h"
75 #define MAXNAME 32
76 #define OPENDIR '[' /* starting sign for directive */
77 #define CLOSEDIR ']' /* ending sign for directive */
79 /*! \libinternal \brief
80 * The configuration describing a virtual site.
82 struct VirtualSiteConfiguration
84 /*! \brief
85 * Explicit constructor.
87 * \param[in] type Atomtype for vsite configuration.
88 * \param[in] planar Is the input conf planar.
89 * \param[in] nhyd How many hydrogens are in the configuration.
90 * \param[in] nextheavy Type of bonded heavy atom.
91 * \param[in] dummy What kind of dummy is used in the vsite.
93 explicit VirtualSiteConfiguration(const std::string &type, bool planar,
94 int nhyd, const std::string &nextheavy, const std::string &dummy)
95 : atomtype(type), isplanar(planar), nHydrogens(nhyd), nextHeavyType(nextheavy),
96 dummyMass(dummy)
98 //! Type for the XH3/XH2 atom.
99 std::string atomtype;
100 /*! \brief Is the configuration planar?
102 * If true, the atomtype above and the three connected
103 * ones are in a planar geometry. The two next entries
104 * are undefined in that case.
106 bool isplanar = false;
107 //!cnumber of connected hydrogens.
108 int nHydrogens;
109 //! Type for the heavy atom bonded to XH2/XH3.
110 std::string nextHeavyType;
111 //! The type of MNH* or MCH3* dummy mass to use.
112 std::string dummyMass;
116 /*!\libinternal \brief
117 * Virtual site topology datastructure.
119 * Structure to represent average bond and angles values in vsite aromatic
120 * residues. Note that these are NOT necessarily the bonds and angles from the
121 * forcefield; many forcefields (like Amber, OPLS) have some inherent strain in
122 * 5-rings (i.e. the sum of angles is !=540, but impropers keep it planar)
124 struct VirtualSiteTopology
126 /*! \brief
127 * Explicit constructor
129 * \param[in] name Residue name.
131 explicit VirtualSiteTopology(const std::string &name) : resname(name)
133 //! Residue name.
134 std::string resname;
135 //! Helper struct for single bond in virtual site.
136 struct VirtualSiteBond
138 /*! \brief
139 * Explicit constructor
141 * \param[in] a1 First atom name.
142 * \param[in] a2 Second atom name.
143 * \param[in] v Value for distance.
145 VirtualSiteBond(const std::string &a1, const std::string &a2, real v) :
146 atom1(a1), atom2(a2), value(v)
148 //! Atom 1 in bond.
149 std::string atom1;
150 //! Atom 2 in bond.
151 std::string atom2;
152 //! Distance value between atoms.
153 float value;
155 //! Container of all bonds in virtual site.
156 std::vector<VirtualSiteBond> bond;
157 //! Helper struct for single angle in virtual site.
158 struct VirtualSiteAngle
160 /*! \brief
161 * Explicit constructor
163 * \param[in] a1 First atom name.
164 * \param[in] a2 Second atom name.
165 * \param[in] a3 Third atom name.
166 * \param[in] v Value for angle.
168 VirtualSiteAngle(const std::string &a1, const std::string &a2, const std::string &a3, real v) :
169 atom1(a1), atom2(a2), atom3(a3), value(v)
171 //! Atom 1 in angle.
172 std::string atom1;
173 //! Atom 2 in angle.
174 std::string atom2;
175 //! Atom 3 in angle.
176 std::string atom3;
177 //! Value for angle.
178 float value;
180 //! Container for all angles in virtual site.
181 std::vector<VirtualSiteAngle> angle;
185 enum {
186 DDB_CH3, DDB_NH3, DDB_NH2, DDB_PHE, DDB_TYR,
187 DDB_TRP, DDB_HISA, DDB_HISB, DDB_HISH, DDB_DIR_NR
190 typedef char t_dirname[STRLEN];
192 static const t_dirname ddb_dirnames[DDB_DIR_NR] = {
193 "CH3",
194 "NH3",
195 "NH2",
196 "PHE",
197 "TYR",
198 "TRP",
199 "HISA",
200 "HISB",
201 "HISH"
204 static int ddb_name2dir(char *name)
206 /* Translate a directive name to the number of the directive.
207 * HID/HIE/HIP names are translated to the ones we use in Gromacs.
210 int i, index;
212 index = -1;
214 for (i = 0; i < DDB_DIR_NR && index < 0; i++)
216 if (!gmx_strcasecmp(name, ddb_dirnames[i]))
218 index = i;
222 return index;
226 static void read_vsite_database(const char *ddbname,
227 std::vector<VirtualSiteConfiguration> *vsiteconflist,
228 std::vector<VirtualSiteTopology> *vsitetoplist)
230 /* This routine is a quick hack to fix the problem with hardcoded atomtypes
231 * and aromatic vsite parameters by reading them from a ff???.vsd file.
233 * The file can contain sections [ NH3 ], [ CH3 ], [ NH2 ], and ring residue names.
234 * For the NH3 and CH3 section each line has three fields. The first is the atomtype
235 * (nb: not bonded type) of the N/C atom to be replaced, the second field is
236 * the type of the next heavy atom it is bonded to, and the third field the type
237 * of dummy mass that will be used for this group.
239 * If the NH2 group planar (sp2 N) a different vsite construct is used, so in this
240 * case the second field should just be the word planar.
243 char dirstr[STRLEN];
244 char pline[STRLEN];
245 int curdir;
246 char *ch;
247 char s1[MAXNAME], s2[MAXNAME], s3[MAXNAME], s4[MAXNAME];
249 gmx::FilePtr ddb = gmx::openLibraryFile(ddbname);
251 curdir = -1;
253 while (fgets2(pline, STRLEN-2, ddb.get()) != nullptr)
255 strip_comment(pline);
256 trim(pline);
257 if (strlen(pline) > 0)
259 if (pline[0] == OPENDIR)
261 strncpy(dirstr, pline+1, STRLEN-2);
262 if ((ch = strchr (dirstr, CLOSEDIR)) != nullptr)
264 (*ch) = 0;
266 trim (dirstr);
268 if (!gmx_strcasecmp(dirstr, "HID") ||
269 !gmx_strcasecmp(dirstr, "HISD"))
271 sprintf(dirstr, "HISA");
273 else if (!gmx_strcasecmp(dirstr, "HIE") ||
274 !gmx_strcasecmp(dirstr, "HISE"))
276 sprintf(dirstr, "HISB");
278 else if (!gmx_strcasecmp(dirstr, "HIP"))
280 sprintf(dirstr, "HISH");
283 curdir = ddb_name2dir(dirstr);
284 if (curdir < 0)
286 gmx_fatal(FARGS, "Invalid directive %s in vsite database %s",
287 dirstr, ddbname);
290 else
292 switch (curdir)
294 case -1:
295 gmx_fatal(FARGS, "First entry in vsite database must be a directive.\n");
296 case DDB_CH3:
297 case DDB_NH3:
298 case DDB_NH2:
300 int numberOfSites = sscanf(pline, "%s%s%s", s1, s2, s3);
301 std::string s1String = s1;
302 std::string s2String = s2;
303 std::string s3String = s3;
304 if (numberOfSites < 3 && gmx::equalCaseInsensitive(s2String, "planar"))
306 VirtualSiteConfiguration newVsiteConf(s1String, true, 2, "0", "0");
307 vsiteconflist->push_back(newVsiteConf);
309 else if (numberOfSites == 3)
311 VirtualSiteConfiguration newVsiteConf(s1String, false, -1, s2String, s3String);
312 if (curdir == DDB_NH2)
314 newVsiteConf.nHydrogens = 2;
316 else
318 newVsiteConf.nHydrogens = 3;
320 vsiteconflist->push_back(newVsiteConf);
322 else
324 gmx_fatal(FARGS, "Not enough directives in vsite database line: %s\n", pline);
327 break;
328 case DDB_PHE:
329 case DDB_TYR:
330 case DDB_TRP:
331 case DDB_HISA:
332 case DDB_HISB:
333 case DDB_HISH:
335 const auto found = std::find_if(vsitetoplist->begin(), vsitetoplist->end(),
336 [&dirstr](const auto &entry)
337 { return gmx::equalCaseInsensitive(dirstr, entry.resname); });
338 /* Allocate a new topology entry if this is a new residue */
339 if (found == vsitetoplist->end())
341 vsitetoplist->push_back(VirtualSiteTopology(dirstr));
343 int numberOfSites = sscanf(pline, "%s%s%s%s", s1, s2, s3, s4);
344 std::string s1String = s1;
345 std::string s2String = s2;
346 std::string s3String = s3;
348 if (numberOfSites == 3)
350 /* bond */
351 vsitetoplist->back().bond.emplace_back(s1String, s2String, strtod(s3, nullptr));
353 else if (numberOfSites == 4)
355 /* angle */
356 vsitetoplist->back().angle.emplace_back(s1String, s2String, s3String, strtod(s4, nullptr));
357 /* angle */
359 else
361 gmx_fatal(FARGS, "Need 3 or 4 values to specify bond/angle values in %s: %s\n", ddbname, pline);
364 break;
365 default:
366 gmx_fatal(FARGS, "Didnt find a case for directive %s in read_vsite_database\n", dirstr);
373 static int nitrogen_is_planar(gmx::ArrayRef<const VirtualSiteConfiguration> vsiteconflist,
374 const std::string &atomtype)
376 /* Return 1 if atomtype exists in database list and is planar, 0 if not,
377 * and -1 if not found.
379 int res;
380 const auto found = std::find_if(vsiteconflist.begin(), vsiteconflist.end(),
381 [&atomtype](const auto &entry)
382 { return (gmx::equalCaseInsensitive(entry.atomtype, atomtype) &&
383 entry.nHydrogens == 2); });
384 if (found != vsiteconflist.end())
386 res = static_cast<int>(found->isplanar);
388 else
390 res = -1;
393 return res;
396 static std::string get_dummymass_name(gmx::ArrayRef<const VirtualSiteConfiguration> vsiteconflist,
397 const std::string &atom, const std::string &nextheavy)
399 /* Return the dummy mass name if found, or NULL if not set in ddb database */
400 const auto found = std::find_if(vsiteconflist.begin(), vsiteconflist.end(),
401 [&atom, &nextheavy](const auto &entry)
402 { return (gmx::equalCaseInsensitive(atom, entry.atomtype) &&
403 gmx::equalCaseInsensitive(nextheavy, entry.nextHeavyType)); });
404 if (found != vsiteconflist.end())
406 return found->dummyMass;
408 else
410 return "";
416 static real get_ddb_bond(gmx::ArrayRef<const VirtualSiteTopology> vsitetop,
417 const std::string &res,
418 const std::string &atom1,
419 const std::string &atom2)
421 const auto found = std::find_if(vsitetop.begin(), vsitetop.end(),
422 [&res](const auto &entry)
423 { return gmx::equalCaseInsensitive(res, entry.resname); });
425 if (found == vsitetop.end())
427 gmx_fatal(FARGS, "No vsite information for residue %s found in vsite database.\n", res.c_str());
429 const auto foundBond = std::find_if(found->bond.begin(), found->bond.end(),
430 [&atom1, &atom2](const auto &entry)
431 { return ((atom1 == entry.atom1 && atom2 == entry.atom2) ||
432 (atom1 == entry.atom2 && atom2 == entry.atom1)); });
433 if (foundBond == found->bond.end())
435 gmx_fatal(FARGS, "Couldnt find bond %s-%s for residue %s in vsite database.\n", atom1.c_str(), atom2.c_str(), res.c_str());
438 return foundBond->value;
442 static real get_ddb_angle(gmx::ArrayRef<const VirtualSiteTopology> vsitetop,
443 const std::string &res,
444 const std::string &atom1,
445 const std::string &atom2,
446 const std::string &atom3)
448 const auto found = std::find_if(vsitetop.begin(), vsitetop.end(),
449 [&res](const auto &entry)
450 { return gmx::equalCaseInsensitive(res, entry.resname); });
452 if (found == vsitetop.end())
454 gmx_fatal(FARGS, "No vsite information for residue %s found in vsite database.\n", res.c_str());
456 const auto foundAngle = std::find_if(found->angle.begin(), found->angle.end(),
457 [&atom1, &atom2, &atom3](const auto &entry)
458 { return ((atom1 == entry.atom1 && atom2 == entry.atom2 && atom3 == entry.atom3) ||
459 (atom1 == entry.atom3 && atom2 == entry.atom2 && atom3 == entry.atom1) ||
460 (atom1 == entry.atom2 && atom2 == entry.atom1 && atom3 == entry.atom3) ||
461 (atom1 == entry.atom3 && atom2 == entry.atom1 && atom3 == entry.atom2)); });
463 if (foundAngle == found->angle.end())
465 gmx_fatal(FARGS, "Couldnt find angle %s-%s-%s for residue %s in vsite database.\n", atom1.c_str(), atom2.c_str(), atom3.c_str(), res.c_str());
468 return foundAngle->value;
472 static void count_bonds(int atom, InteractionsOfType *psb, char ***atomname,
473 int *nrbonds, int *nrHatoms, int Hatoms[], int *Heavy,
474 int *nrheavies, int heavies[])
476 int heavy, other, nrb, nrH, nrhv;
478 /* find heavy atom bound to this hydrogen */
479 heavy = NOTSET;
480 for (auto parm = psb->interactionTypes.begin(); (parm != psb->interactionTypes.end()) && (heavy == NOTSET); parm++)
482 if (parm->ai() == atom)
484 heavy = parm->aj();
486 else if (parm->aj() == atom)
488 heavy = parm->ai();
491 if (heavy == NOTSET)
493 gmx_fatal(FARGS, "unbound hydrogen atom %d", atom+1);
495 /* find all atoms bound to heavy atom */
496 other = NOTSET;
497 nrb = 0;
498 nrH = 0;
499 nrhv = 0;
500 for (const auto &parm : psb->interactionTypes)
502 if (parm.ai() == heavy)
504 other = parm.aj();
506 else if (parm.aj() == heavy)
508 other = parm.ai();
510 if (other != NOTSET)
512 nrb++;
513 if (is_hydrogen(*(atomname[other])))
515 Hatoms[nrH] = other;
516 nrH++;
518 else
520 heavies[nrhv] = other;
521 nrhv++;
523 other = NOTSET;
526 *Heavy = heavy;
527 *nrbonds = nrb;
528 *nrHatoms = nrH;
529 *nrheavies = nrhv;
532 static void print_bonds(FILE *fp, int o2n[],
533 int nrHatoms, const int Hatoms[], int Heavy,
534 int nrheavies, const int heavies[])
536 int i;
538 fprintf(fp, "Found: %d Hatoms: ", nrHatoms);
539 for (i = 0; i < nrHatoms; i++)
541 fprintf(fp, " %d", o2n[Hatoms[i]]+1);
543 fprintf(fp, "; %d Heavy atoms: %d", nrheavies+1, o2n[Heavy]+1);
544 for (i = 0; i < nrheavies; i++)
546 fprintf(fp, " %d", o2n[heavies[i]]+1);
548 fprintf(fp, "\n");
551 static int get_atype(int atom, t_atoms *at, gmx::ArrayRef<const PreprocessResidue> rtpFFDB,
552 ResidueType *rt)
554 int type;
555 bool bNterm;
557 if (at->atom[atom].m != 0.0F)
559 type = at->atom[atom].type;
561 else
563 /* get type from rtpFFDB */
564 auto localPpResidue = getDatabaseEntry(*(at->resinfo[at->atom[atom].resind].name), rtpFFDB);
565 bNterm = rt->namedResidueHasType(*(at->resinfo[at->atom[atom].resind].name), "Protein") &&
566 (at->atom[atom].resind == 0);
567 int j = search_jtype(*localPpResidue, *(at->atomname[atom]), bNterm);
568 type = localPpResidue->atom[j].type;
570 return type;
573 static int vsite_nm2type(const char *name, PreprocessingAtomTypes *atype)
575 int tp;
577 tp = atype->atomTypeFromName(name);
578 if (tp == NOTSET)
580 gmx_fatal(FARGS, "Dummy mass type (%s) not found in atom type database",
581 name);
584 return tp;
587 static real get_amass(int atom, t_atoms *at, gmx::ArrayRef<const PreprocessResidue> rtpFFDB,
588 ResidueType *rt)
590 real mass;
591 bool bNterm;
593 if (at->atom[atom].m != 0.0F)
595 mass = at->atom[atom].m;
597 else
599 /* get mass from rtpFFDB */
600 auto localPpResidue = getDatabaseEntry(*(at->resinfo[at->atom[atom].resind].name), rtpFFDB);
601 bNterm = rt->namedResidueHasType(*(at->resinfo[at->atom[atom].resind].name), "Protein") &&
602 (at->atom[atom].resind == 0);
603 int j = search_jtype(*localPpResidue, *(at->atomname[atom]), bNterm);
604 mass = localPpResidue->atom[j].m;
606 return mass;
609 static void my_add_param(InteractionsOfType *plist, int ai, int aj, real b)
611 static real c[MAXFORCEPARAM] =
612 { NOTSET, NOTSET, NOTSET, NOTSET, NOTSET, NOTSET };
614 c[0] = b;
615 add_param(plist, ai, aj, c, nullptr);
618 static void add_vsites(gmx::ArrayRef<InteractionsOfType> plist, int vsite_type[],
619 int Heavy, int nrHatoms, int Hatoms[],
620 int nrheavies, int heavies[])
622 int other, moreheavy;
624 for (int i = 0; i < nrHatoms; i++)
626 int ftype = vsite_type[Hatoms[i]];
627 /* Errors in setting the vsite_type should really be caugth earlier,
628 * because here it's not possible to print any useful error message.
629 * But it's still better to print a message than to segfault.
631 if (ftype == NOTSET)
633 gmx_incons("Undetected error in setting up virtual sites");
635 bool bSwapParity = (ftype < 0);
636 vsite_type[Hatoms[i]] = ftype = abs(ftype);
637 if (ftype == F_BONDS)
639 if ( (nrheavies != 1) && (nrHatoms != 1) )
641 gmx_fatal(FARGS, "cannot make constraint in add_vsites for %d heavy "
642 "atoms and %d hydrogen atoms", nrheavies, nrHatoms);
644 my_add_param(&(plist[F_CONSTRNC]), Hatoms[i], heavies[0], NOTSET);
646 else
648 switch (ftype)
650 case F_VSITE3:
651 case F_VSITE3FD:
652 case F_VSITE3OUT:
653 if (nrheavies < 2)
655 gmx_fatal(FARGS, "Not enough heavy atoms (%d) for %s (min 3)",
656 nrheavies+1,
657 interaction_function[vsite_type[Hatoms[i]]].name);
659 add_vsite3_atoms(&plist[ftype], Hatoms[i], Heavy, heavies[0], heavies[1],
660 bSwapParity);
661 break;
662 case F_VSITE3FAD:
664 if (nrheavies > 1)
666 moreheavy = heavies[1];
668 else
670 /* find more heavy atoms */
671 other = moreheavy = NOTSET;
672 for (auto parm = plist[F_BONDS].interactionTypes.begin();
673 (parm != plist[F_BONDS].interactionTypes.end()) && (moreheavy == NOTSET); parm++)
675 if (parm->ai() == heavies[0])
677 other = parm->aj();
679 else if (parm->aj() == heavies[0])
681 other = parm->ai();
683 if ( (other != NOTSET) && (other != Heavy) )
685 moreheavy = other;
688 if (moreheavy == NOTSET)
690 gmx_fatal(FARGS, "Unbound molecule part %d-%d", Heavy+1, Hatoms[0]+1);
693 add_vsite3_atoms(&plist[ftype], Hatoms[i], Heavy, heavies[0], moreheavy,
694 bSwapParity);
695 break;
697 case F_VSITE4FD:
698 case F_VSITE4FDN:
699 if (nrheavies < 3)
701 gmx_fatal(FARGS, "Not enough heavy atoms (%d) for %s (min 4)",
702 nrheavies+1,
703 interaction_function[vsite_type[Hatoms[i]]].name);
705 add_vsite4_atoms(&plist[ftype],
706 Hatoms[0], Heavy, heavies[0], heavies[1], heavies[2]);
707 break;
709 default:
710 gmx_fatal(FARGS, "can't use add_vsites for interaction function %s",
711 interaction_function[vsite_type[Hatoms[i]]].name);
712 } /* switch ftype */
713 } /* else */
714 } /* for i */
717 #define ANGLE_6RING (DEG2RAD*120)
719 /* cosine rule: a^2 = b^2 + c^2 - 2 b c cos(alpha) */
720 /* get a^2 when a, b and alpha are given: */
721 #define cosrule(b, c, alpha) ( gmx::square(b) + gmx::square(c) - 2*(b)*(c)*std::cos(alpha) )
722 /* get cos(alpha) when a, b and c are given: */
723 #define acosrule(a, b, c) ( (gmx::square(b)+gmx::square(c)-gmx::square(a))/(2*(b)*(c)) )
725 static int gen_vsites_6ring(t_atoms *at, int *vsite_type[], gmx::ArrayRef<InteractionsOfType> plist,
726 int nrfound, int *ats, real bond_cc, real bond_ch,
727 real xcom, bool bDoZ)
729 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
730 enum {
731 atCG, atCD1, atHD1, atCD2, atHD2, atCE1, atHE1, atCE2, atHE2,
732 atCZ, atHZ, atNR
735 int i, nvsite;
736 real a, b, dCGCE, tmp1, tmp2, mtot, mG, mrest;
737 real xCG;
738 /* CG, CE1 and CE2 stay and each get a part of the total mass,
739 * so the c-o-m stays the same.
742 if (bDoZ)
744 if (atNR != nrfound)
746 gmx_incons("Generating vsites on 6-rings");
750 /* constraints between CG, CE1 and CE2: */
751 dCGCE = std::sqrt( cosrule(bond_cc, bond_cc, ANGLE_6RING) );
752 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], ats[atCE1], dCGCE);
753 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], ats[atCE2], dCGCE);
754 my_add_param(&(plist[F_CONSTRNC]), ats[atCE1], ats[atCE2], dCGCE);
756 /* rest will be vsite3 */
757 mtot = 0;
758 nvsite = 0;
759 for (i = 0; i < (bDoZ ? atNR : atHZ); i++)
761 mtot += at->atom[ats[i]].m;
762 if (i != atCG && i != atCE1 && i != atCE2 && (bDoZ || (i != atHZ && i != atCZ) ) )
764 at->atom[ats[i]].m = at->atom[ats[i]].mB = 0;
765 (*vsite_type)[ats[i]] = F_VSITE3;
766 nvsite++;
769 /* Distribute mass so center-of-mass stays the same.
770 * The center-of-mass in the call is defined with x=0 at
771 * the CE1-CE2 bond and y=0 at the line from CG to the middle of CE1-CE2 bond.
773 xCG = -bond_cc+bond_cc*std::cos(ANGLE_6RING);
775 mG = at->atom[ats[atCG]].m = at->atom[ats[atCG]].mB = xcom*mtot/xCG;
776 mrest = mtot-mG;
777 at->atom[ats[atCE1]].m = at->atom[ats[atCE1]].mB =
778 at->atom[ats[atCE2]].m = at->atom[ats[atCE2]].mB = mrest / 2;
780 /* vsite3 construction: r_d = r_i + a r_ij + b r_ik */
781 tmp1 = dCGCE*std::sin(ANGLE_6RING*0.5);
782 tmp2 = bond_cc*std::cos(0.5*ANGLE_6RING) + tmp1;
783 tmp1 *= 2;
784 a = b = -bond_ch / tmp1;
785 /* HE1 and HE2: */
786 add_vsite3_param(&plist[F_VSITE3],
787 ats[atHE1], ats[atCE1], ats[atCE2], ats[atCG], a, b);
788 add_vsite3_param(&plist[F_VSITE3],
789 ats[atHE2], ats[atCE2], ats[atCE1], ats[atCG], a, b);
790 /* CD1, CD2 and CZ: */
791 a = b = tmp2 / tmp1;
792 add_vsite3_param(&plist[F_VSITE3],
793 ats[atCD1], ats[atCE2], ats[atCE1], ats[atCG], a, b);
794 add_vsite3_param(&plist[F_VSITE3],
795 ats[atCD2], ats[atCE1], ats[atCE2], ats[atCG], a, b);
796 if (bDoZ)
798 add_vsite3_param(&plist[F_VSITE3],
799 ats[atCZ], ats[atCG], ats[atCE1], ats[atCE2], a, b);
801 /* HD1, HD2 and HZ: */
802 a = b = ( bond_ch + tmp2 ) / tmp1;
803 add_vsite3_param(&plist[F_VSITE3],
804 ats[atHD1], ats[atCE2], ats[atCE1], ats[atCG], a, b);
805 add_vsite3_param(&plist[F_VSITE3],
806 ats[atHD2], ats[atCE1], ats[atCE2], ats[atCG], a, b);
807 if (bDoZ)
809 add_vsite3_param(&plist[F_VSITE3],
810 ats[atHZ], ats[atCG], ats[atCE1], ats[atCE2], a, b);
813 return nvsite;
816 static int gen_vsites_phe(t_atoms *at, int *vsite_type[], gmx::ArrayRef<InteractionsOfType> plist,
817 int nrfound, int *ats, gmx::ArrayRef<const VirtualSiteTopology> vsitetop)
819 real bond_cc, bond_ch;
820 real xcom, mtot;
821 int i;
822 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
823 enum {
824 atCG, atCD1, atHD1, atCD2, atHD2, atCE1, atHE1, atCE2, atHE2,
825 atCZ, atHZ, atNR
827 real x[atNR];
828 /* Aromatic rings have 6-fold symmetry, so we only need one bond length.
829 * (angle is always 120 degrees).
831 bond_cc = get_ddb_bond(vsitetop, "PHE", "CD1", "CE1");
832 bond_ch = get_ddb_bond(vsitetop, "PHE", "CD1", "HD1");
834 x[atCG] = -bond_cc+bond_cc*std::cos(ANGLE_6RING);
835 x[atCD1] = -bond_cc;
836 x[atHD1] = x[atCD1]+bond_ch*std::cos(ANGLE_6RING);
837 x[atCE1] = 0;
838 x[atHE1] = x[atCE1]-bond_ch*std::cos(ANGLE_6RING);
839 x[atCD2] = x[atCD1];
840 x[atHD2] = x[atHD1];
841 x[atCE2] = x[atCE1];
842 x[atHE2] = x[atHE1];
843 x[atCZ] = bond_cc*std::cos(0.5*ANGLE_6RING);
844 x[atHZ] = x[atCZ]+bond_ch;
846 xcom = mtot = 0;
847 for (i = 0; i < atNR; i++)
849 xcom += x[i]*at->atom[ats[i]].m;
850 mtot += at->atom[ats[i]].m;
852 xcom /= mtot;
854 return gen_vsites_6ring(at, vsite_type, plist, nrfound, ats, bond_cc, bond_ch, xcom, TRUE);
857 static void calc_vsite3_param(real xd, real yd, real xi, real yi, real xj, real yj,
858 real xk, real yk, real *a, real *b)
860 /* determine parameters by solving the equation system, since we know the
861 * virtual site coordinates here.
863 real dx_ij, dx_ik, dy_ij, dy_ik;
865 dx_ij = xj-xi;
866 dy_ij = yj-yi;
867 dx_ik = xk-xi;
868 dy_ik = yk-yi;
870 *a = ( (xd-xi)*dy_ik - dx_ik*(yd-yi) ) / (dx_ij*dy_ik - dx_ik*dy_ij);
871 *b = ( yd - yi - (*a)*dy_ij ) / dy_ik;
875 static int gen_vsites_trp(PreprocessingAtomTypes *atype,
876 std::vector<gmx::RVec> *newx,
877 t_atom *newatom[], char ***newatomname[],
878 int *o2n[], int *newvsite_type[], int *newcgnr[],
879 t_symtab *symtab, int *nadd,
880 gmx::ArrayRef<const gmx::RVec> x, int *cgnr[],
881 t_atoms *at, int *vsite_type[],
882 gmx::ArrayRef<InteractionsOfType> plist,
883 int nrfound, int *ats, int add_shift,
884 gmx::ArrayRef<const VirtualSiteTopology> vsitetop)
886 #define NMASS 2
887 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
888 enum {
889 atCB, atCG, atCD1, atHD1, atCD2, atNE1, atHE1, atCE2, atCE3, atHE3,
890 atCZ2, atHZ2, atCZ3, atHZ3, atCH2, atHH2, atNR
892 /* weights for determining the COM's of both rings (M1 and M2): */
893 real mw[NMASS][atNR] = {
894 { 0, 1, 1, 1, 0.5, 1, 1, 0.5, 0, 0,
895 0, 0, 0, 0, 0, 0 },
896 { 0, 0, 0, 0, 0.5, 0, 0, 0.5, 1, 1,
897 1, 1, 1, 1, 1, 1 }
900 real xi[atNR], yi[atNR];
901 real xcom[NMASS], ycom[NMASS], alpha;
902 real b_CD2_CE2, b_NE1_CE2, b_CG_CD2, b_CH2_HH2, b_CE2_CZ2;
903 real b_NE1_HE1, b_CD2_CE3, b_CE3_CZ3, b_CB_CG;
904 real b_CZ2_CH2, b_CZ2_HZ2, b_CD1_HD1, b_CE3_HE3;
905 real b_CG_CD1, b_CZ3_HZ3;
906 real a_NE1_CE2_CD2, a_CE2_CD2_CG, a_CB_CG_CD2, a_CE2_CD2_CE3;
907 real a_CD2_CG_CD1, a_CE2_CZ2_HZ2, a_CZ2_CH2_HH2;
908 real a_CD2_CE2_CZ2, a_CD2_CE3_CZ3, a_CE3_CZ3_HZ3, a_CG_CD1_HD1;
909 real a_CE2_CZ2_CH2, a_HE1_NE1_CE2, a_CD2_CE3_HE3;
910 int atM[NMASS], tpM, i, i0, j, nvsite;
911 real mM[NMASS], dCBM1, dCBM2, dM1M2;
912 real a, b;
913 rvec r_ij, r_ik, t1, t2;
914 char name[10];
916 if (atNR != nrfound)
918 gmx_incons("atom types in gen_vsites_trp");
920 /* Get geometry from database */
921 b_CD2_CE2 = get_ddb_bond(vsitetop, "TRP", "CD2", "CE2");
922 b_NE1_CE2 = get_ddb_bond(vsitetop, "TRP", "NE1", "CE2");
923 b_CG_CD1 = get_ddb_bond(vsitetop, "TRP", "CG", "CD1");
924 b_CG_CD2 = get_ddb_bond(vsitetop, "TRP", "CG", "CD2");
925 b_CB_CG = get_ddb_bond(vsitetop, "TRP", "CB", "CG");
926 b_CE2_CZ2 = get_ddb_bond(vsitetop, "TRP", "CE2", "CZ2");
927 b_CD2_CE3 = get_ddb_bond(vsitetop, "TRP", "CD2", "CE3");
928 b_CE3_CZ3 = get_ddb_bond(vsitetop, "TRP", "CE3", "CZ3");
929 b_CZ2_CH2 = get_ddb_bond(vsitetop, "TRP", "CZ2", "CH2");
931 b_CD1_HD1 = get_ddb_bond(vsitetop, "TRP", "CD1", "HD1");
932 b_CZ2_HZ2 = get_ddb_bond(vsitetop, "TRP", "CZ2", "HZ2");
933 b_NE1_HE1 = get_ddb_bond(vsitetop, "TRP", "NE1", "HE1");
934 b_CH2_HH2 = get_ddb_bond(vsitetop, "TRP", "CH2", "HH2");
935 b_CE3_HE3 = get_ddb_bond(vsitetop, "TRP", "CE3", "HE3");
936 b_CZ3_HZ3 = get_ddb_bond(vsitetop, "TRP", "CZ3", "HZ3");
938 a_NE1_CE2_CD2 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "NE1", "CE2", "CD2");
939 a_CE2_CD2_CG = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CE2", "CD2", "CG");
940 a_CB_CG_CD2 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CB", "CG", "CD2");
941 a_CD2_CG_CD1 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CD2", "CG", "CD1");
943 a_CE2_CD2_CE3 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CE2", "CD2", "CE3");
944 a_CD2_CE2_CZ2 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CD2", "CE2", "CZ2");
945 a_CD2_CE3_CZ3 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CD2", "CE3", "CZ3");
946 a_CE3_CZ3_HZ3 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CE3", "CZ3", "HZ3");
947 a_CZ2_CH2_HH2 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CZ2", "CH2", "HH2");
948 a_CE2_CZ2_HZ2 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CE2", "CZ2", "HZ2");
949 a_CE2_CZ2_CH2 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CE2", "CZ2", "CH2");
950 a_CG_CD1_HD1 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CG", "CD1", "HD1");
951 a_HE1_NE1_CE2 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "HE1", "NE1", "CE2");
952 a_CD2_CE3_HE3 = DEG2RAD*get_ddb_angle(vsitetop, "TRP", "CD2", "CE3", "HE3");
954 /* Calculate local coordinates.
955 * y-axis (x=0) is the bond CD2-CE2.
956 * x-axis (y=0) is perpendicular to the bond CD2-CE2 and
957 * intersects the middle of the bond.
959 xi[atCD2] = 0;
960 yi[atCD2] = -0.5*b_CD2_CE2;
962 xi[atCE2] = 0;
963 yi[atCE2] = 0.5*b_CD2_CE2;
965 xi[atNE1] = -b_NE1_CE2*std::sin(a_NE1_CE2_CD2);
966 yi[atNE1] = yi[atCE2]-b_NE1_CE2*std::cos(a_NE1_CE2_CD2);
968 xi[atCG] = -b_CG_CD2*std::sin(a_CE2_CD2_CG);
969 yi[atCG] = yi[atCD2]+b_CG_CD2*std::cos(a_CE2_CD2_CG);
971 alpha = a_CE2_CD2_CG + M_PI - a_CB_CG_CD2;
972 xi[atCB] = xi[atCG]-b_CB_CG*std::sin(alpha);
973 yi[atCB] = yi[atCG]+b_CB_CG*std::cos(alpha);
975 alpha = a_CE2_CD2_CG + a_CD2_CG_CD1 - M_PI;
976 xi[atCD1] = xi[atCG]-b_CG_CD1*std::sin(alpha);
977 yi[atCD1] = yi[atCG]+b_CG_CD1*std::cos(alpha);
979 xi[atCE3] = b_CD2_CE3*std::sin(a_CE2_CD2_CE3);
980 yi[atCE3] = yi[atCD2]+b_CD2_CE3*std::cos(a_CE2_CD2_CE3);
982 xi[atCZ2] = b_CE2_CZ2*std::sin(a_CD2_CE2_CZ2);
983 yi[atCZ2] = yi[atCE2]-b_CE2_CZ2*std::cos(a_CD2_CE2_CZ2);
985 alpha = a_CE2_CD2_CE3 + a_CD2_CE3_CZ3 - M_PI;
986 xi[atCZ3] = xi[atCE3]+b_CE3_CZ3*std::sin(alpha);
987 yi[atCZ3] = yi[atCE3]+b_CE3_CZ3*std::cos(alpha);
989 alpha = a_CD2_CE2_CZ2 + a_CE2_CZ2_CH2 - M_PI;
990 xi[atCH2] = xi[atCZ2]+b_CZ2_CH2*std::sin(alpha);
991 yi[atCH2] = yi[atCZ2]-b_CZ2_CH2*std::cos(alpha);
993 /* hydrogens */
994 alpha = a_CE2_CD2_CG + a_CD2_CG_CD1 - a_CG_CD1_HD1;
995 xi[atHD1] = xi[atCD1]-b_CD1_HD1*std::sin(alpha);
996 yi[atHD1] = yi[atCD1]+b_CD1_HD1*std::cos(alpha);
998 alpha = a_NE1_CE2_CD2 + M_PI - a_HE1_NE1_CE2;
999 xi[atHE1] = xi[atNE1]-b_NE1_HE1*std::sin(alpha);
1000 yi[atHE1] = yi[atNE1]-b_NE1_HE1*std::cos(alpha);
1002 alpha = a_CE2_CD2_CE3 + M_PI - a_CD2_CE3_HE3;
1003 xi[atHE3] = xi[atCE3]+b_CE3_HE3*std::sin(alpha);
1004 yi[atHE3] = yi[atCE3]+b_CE3_HE3*std::cos(alpha);
1006 alpha = a_CD2_CE2_CZ2 + M_PI - a_CE2_CZ2_HZ2;
1007 xi[atHZ2] = xi[atCZ2]+b_CZ2_HZ2*std::sin(alpha);
1008 yi[atHZ2] = yi[atCZ2]-b_CZ2_HZ2*std::cos(alpha);
1010 alpha = a_CD2_CE2_CZ2 + a_CE2_CZ2_CH2 - a_CZ2_CH2_HH2;
1011 xi[atHZ3] = xi[atCZ3]+b_CZ3_HZ3*std::sin(alpha);
1012 yi[atHZ3] = yi[atCZ3]+b_CZ3_HZ3*std::cos(alpha);
1014 alpha = a_CE2_CD2_CE3 + a_CD2_CE3_CZ3 - a_CE3_CZ3_HZ3;
1015 xi[atHH2] = xi[atCH2]+b_CH2_HH2*std::sin(alpha);
1016 yi[atHH2] = yi[atCH2]-b_CH2_HH2*std::cos(alpha);
1018 /* Calculate masses for each ring and put it on the dummy masses */
1019 for (j = 0; j < NMASS; j++)
1021 mM[j] = xcom[j] = ycom[j] = 0;
1023 for (i = 0; i < atNR; i++)
1025 if (i != atCB)
1027 for (j = 0; j < NMASS; j++)
1029 mM[j] += mw[j][i] * at->atom[ats[i]].m;
1030 xcom[j] += xi[i] * mw[j][i] * at->atom[ats[i]].m;
1031 ycom[j] += yi[i] * mw[j][i] * at->atom[ats[i]].m;
1035 for (j = 0; j < NMASS; j++)
1037 xcom[j] /= mM[j];
1038 ycom[j] /= mM[j];
1041 /* get dummy mass type */
1042 tpM = vsite_nm2type("MW", atype);
1043 /* make space for 2 masses: shift all atoms starting with CB */
1044 i0 = ats[atCB];
1045 for (j = 0; j < NMASS; j++)
1047 atM[j] = i0+*nadd+j;
1049 if (debug)
1051 fprintf(stderr, "Inserting %d dummy masses at %d\n", NMASS, (*o2n)[i0]+1);
1053 *nadd += NMASS;
1054 for (j = i0; j < at->nr; j++)
1056 (*o2n)[j] = j+*nadd;
1058 newx->resize(at->nr+*nadd);
1059 srenew(*newatom, at->nr+*nadd);
1060 srenew(*newatomname, at->nr+*nadd);
1061 srenew(*newvsite_type, at->nr+*nadd);
1062 srenew(*newcgnr, at->nr+*nadd);
1063 for (j = 0; j < NMASS; j++)
1065 (*newatomname)[at->nr+*nadd-1-j] = nullptr;
1068 /* Dummy masses will be placed at the center-of-mass in each ring. */
1070 /* calc initial position for dummy masses in real (non-local) coordinates.
1071 * Cheat by using the routine to calculate virtual site parameters. It is
1072 * much easier when we have the coordinates expressed in terms of
1073 * CB, CG, CD2.
1075 rvec_sub(x[ats[atCB]], x[ats[atCG]], r_ij);
1076 rvec_sub(x[ats[atCD2]], x[ats[atCG]], r_ik);
1077 calc_vsite3_param(xcom[0], ycom[0], xi[atCG], yi[atCG], xi[atCB], yi[atCB],
1078 xi[atCD2], yi[atCD2], &a, &b);
1079 svmul(a, r_ij, t1);
1080 svmul(b, r_ik, t2);
1081 rvec_add(t1, t2, t1);
1082 rvec_add(t1, x[ats[atCG]], (*newx)[atM[0]]);
1084 calc_vsite3_param(xcom[1], ycom[1], xi[atCG], yi[atCG], xi[atCB], yi[atCB],
1085 xi[atCD2], yi[atCD2], &a, &b);
1086 svmul(a, r_ij, t1);
1087 svmul(b, r_ik, t2);
1088 rvec_add(t1, t2, t1);
1089 rvec_add(t1, x[ats[atCG]], (*newx)[atM[1]]);
1091 /* set parameters for the masses */
1092 for (j = 0; j < NMASS; j++)
1094 sprintf(name, "MW%d", j+1);
1095 (*newatomname) [atM[j]] = put_symtab(symtab, name);
1096 (*newatom) [atM[j]].m = (*newatom)[atM[j]].mB = mM[j];
1097 (*newatom) [atM[j]].q = (*newatom)[atM[j]].qB = 0.0;
1098 (*newatom) [atM[j]].type = (*newatom)[atM[j]].typeB = tpM;
1099 (*newatom) [atM[j]].ptype = eptAtom;
1100 (*newatom) [atM[j]].resind = at->atom[i0].resind;
1101 (*newatom) [atM[j]].elem[0] = 'M';
1102 (*newatom) [atM[j]].elem[1] = '\0';
1103 (*newvsite_type)[atM[j]] = NOTSET;
1104 (*newcgnr) [atM[j]] = (*cgnr)[i0];
1106 /* renumber cgnr: */
1107 for (i = i0; i < at->nr; i++)
1109 (*cgnr)[i]++;
1112 /* constraints between CB, M1 and M2 */
1113 /* 'add_shift' says which atoms won't be renumbered afterwards */
1114 dCBM1 = std::hypot( xcom[0]-xi[atCB], ycom[0]-yi[atCB] );
1115 dM1M2 = std::hypot( xcom[0]-xcom[1], ycom[0]-ycom[1] );
1116 dCBM2 = std::hypot( xcom[1]-xi[atCB], ycom[1]-yi[atCB] );
1117 my_add_param(&(plist[F_CONSTRNC]), ats[atCB], add_shift+atM[0], dCBM1);
1118 my_add_param(&(plist[F_CONSTRNC]), ats[atCB], add_shift+atM[1], dCBM2);
1119 my_add_param(&(plist[F_CONSTRNC]), add_shift+atM[0], add_shift+atM[1], dM1M2);
1121 /* rest will be vsite3 */
1122 nvsite = 0;
1123 for (i = 0; i < atNR; i++)
1125 if (i != atCB)
1127 at->atom[ats[i]].m = at->atom[ats[i]].mB = 0;
1128 (*vsite_type)[ats[i]] = F_VSITE3;
1129 nvsite++;
1133 /* now define all vsites from M1, M2, CB, ie:
1134 r_d = r_M1 + a r_M1_M2 + b r_M1_CB */
1135 for (i = 0; i < atNR; i++)
1137 if ( (*vsite_type)[ats[i]] == F_VSITE3)
1139 calc_vsite3_param(xi[i], yi[i], xcom[0], ycom[0], xcom[1], ycom[1], xi[atCB], yi[atCB], &a, &b);
1140 add_vsite3_param(&plist[F_VSITE3],
1141 ats[i], add_shift+atM[0], add_shift+atM[1], ats[atCB], a, b);
1144 return nvsite;
1145 #undef NMASS
1149 static int gen_vsites_tyr(PreprocessingAtomTypes *atype,
1150 std::vector<gmx::RVec> *newx,
1151 t_atom *newatom[], char ***newatomname[],
1152 int *o2n[], int *newvsite_type[], int *newcgnr[],
1153 t_symtab *symtab, int *nadd,
1154 gmx::ArrayRef<const gmx::RVec> x, int *cgnr[],
1155 t_atoms *at, int *vsite_type[],
1156 gmx::ArrayRef<InteractionsOfType> plist,
1157 int nrfound, int *ats, int add_shift,
1158 gmx::ArrayRef<const VirtualSiteTopology> vsitetop)
1160 int nvsite, i, i0, j, atM, tpM;
1161 real dCGCE, dCEOH, dCGM, tmp1, a, b;
1162 real bond_cc, bond_ch, bond_co, bond_oh, angle_coh;
1163 real xcom, mtot;
1164 real vdist, mM;
1165 rvec r1;
1166 char name[10];
1168 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
1169 enum {
1170 atCG, atCD1, atHD1, atCD2, atHD2, atCE1, atHE1, atCE2, atHE2,
1171 atCZ, atOH, atHH, atNR
1173 real xi[atNR];
1174 /* CG, CE1, CE2 (as in general 6-ring) and OH and HH stay,
1175 rest gets virtualized.
1176 Now we have two linked triangles with one improper keeping them flat */
1177 if (atNR != nrfound)
1179 gmx_incons("Number of atom types in gen_vsites_tyr");
1182 /* Aromatic rings have 6-fold symmetry, so we only need one bond length
1183 * for the ring part (angle is always 120 degrees).
1185 bond_cc = get_ddb_bond(vsitetop, "TYR", "CD1", "CE1");
1186 bond_ch = get_ddb_bond(vsitetop, "TYR", "CD1", "HD1");
1187 bond_co = get_ddb_bond(vsitetop, "TYR", "CZ", "OH");
1188 bond_oh = get_ddb_bond(vsitetop, "TYR", "OH", "HH");
1189 angle_coh = DEG2RAD*get_ddb_angle(vsitetop, "TYR", "CZ", "OH", "HH");
1191 xi[atCG] = -bond_cc+bond_cc*std::cos(ANGLE_6RING);
1192 xi[atCD1] = -bond_cc;
1193 xi[atHD1] = xi[atCD1]+bond_ch*std::cos(ANGLE_6RING);
1194 xi[atCE1] = 0;
1195 xi[atHE1] = xi[atCE1]-bond_ch*std::cos(ANGLE_6RING);
1196 xi[atCD2] = xi[atCD1];
1197 xi[atHD2] = xi[atHD1];
1198 xi[atCE2] = xi[atCE1];
1199 xi[atHE2] = xi[atHE1];
1200 xi[atCZ] = bond_cc*std::cos(0.5*ANGLE_6RING);
1201 xi[atOH] = xi[atCZ]+bond_co;
1203 xcom = mtot = 0;
1204 for (i = 0; i < atOH; i++)
1206 xcom += xi[i]*at->atom[ats[i]].m;
1207 mtot += at->atom[ats[i]].m;
1209 xcom /= mtot;
1211 /* first do 6 ring as default,
1212 except CZ (we'll do that different) and HZ (we don't have that): */
1213 nvsite = gen_vsites_6ring(at, vsite_type, plist, nrfound, ats, bond_cc, bond_ch, xcom, FALSE);
1215 /* then construct CZ from the 2nd triangle */
1216 /* vsite3 construction: r_d = r_i + a r_ij + b r_ik */
1217 a = b = 0.5 * bond_co / ( bond_co - bond_cc*std::cos(ANGLE_6RING) );
1218 add_vsite3_param(&plist[F_VSITE3],
1219 ats[atCZ], ats[atOH], ats[atCE1], ats[atCE2], a, b);
1220 at->atom[ats[atCZ]].m = at->atom[ats[atCZ]].mB = 0;
1222 /* constraints between CE1, CE2 and OH */
1223 dCGCE = std::sqrt( cosrule(bond_cc, bond_cc, ANGLE_6RING) );
1224 dCEOH = std::sqrt( cosrule(bond_cc, bond_co, ANGLE_6RING) );
1225 my_add_param(&(plist[F_CONSTRNC]), ats[atCE1], ats[atOH], dCEOH);
1226 my_add_param(&(plist[F_CONSTRNC]), ats[atCE2], ats[atOH], dCEOH);
1228 /* We also want to constrain the angle C-O-H, but since CZ is constructed
1229 * we need to introduce a constraint to CG.
1230 * CG is much further away, so that will lead to instabilities in LINCS
1231 * when we constrain both CG-HH and OH-HH distances. Instead of requiring
1232 * the use of lincs_order=8 we introduce a dummy mass three times further
1233 * away from OH than HH. The mass is accordingly a third, with the remaining
1234 * 2/3 moved to OH. This shouldn't cause any problems since the forces will
1235 * apply to the HH constructed atom and not directly on the virtual mass.
1238 vdist = 2.0*bond_oh;
1239 mM = at->atom[ats[atHH]].m/2.0;
1240 at->atom[ats[atOH]].m += mM; /* add 1/2 of original H mass */
1241 at->atom[ats[atOH]].mB += mM; /* add 1/2 of original H mass */
1242 at->atom[ats[atHH]].m = at->atom[ats[atHH]].mB = 0;
1244 /* get dummy mass type */
1245 tpM = vsite_nm2type("MW", atype);
1246 /* make space for 1 mass: shift HH only */
1247 i0 = ats[atHH];
1248 atM = i0+*nadd;
1249 if (debug)
1251 fprintf(stderr, "Inserting 1 dummy mass at %d\n", (*o2n)[i0]+1);
1253 (*nadd)++;
1254 for (j = i0; j < at->nr; j++)
1256 (*o2n)[j] = j+*nadd;
1258 newx->resize(at->nr+*nadd);
1259 srenew(*newatom, at->nr+*nadd);
1260 srenew(*newatomname, at->nr+*nadd);
1261 srenew(*newvsite_type, at->nr+*nadd);
1262 srenew(*newcgnr, at->nr+*nadd);
1263 (*newatomname)[at->nr+*nadd-1] = nullptr;
1265 /* Calc the dummy mass initial position */
1266 rvec_sub(x[ats[atHH]], x[ats[atOH]], r1);
1267 svmul(2.0, r1, r1);
1268 rvec_add(r1, x[ats[atHH]], (*newx)[atM]);
1270 strcpy(name, "MW1");
1271 (*newatomname) [atM] = put_symtab(symtab, name);
1272 (*newatom) [atM].m = (*newatom)[atM].mB = mM;
1273 (*newatom) [atM].q = (*newatom)[atM].qB = 0.0;
1274 (*newatom) [atM].type = (*newatom)[atM].typeB = tpM;
1275 (*newatom) [atM].ptype = eptAtom;
1276 (*newatom) [atM].resind = at->atom[i0].resind;
1277 (*newatom) [atM].elem[0] = 'M';
1278 (*newatom) [atM].elem[1] = '\0';
1279 (*newvsite_type)[atM] = NOTSET;
1280 (*newcgnr) [atM] = (*cgnr)[i0];
1281 /* renumber cgnr: */
1282 for (i = i0; i < at->nr; i++)
1284 (*cgnr)[i]++;
1287 (*vsite_type)[ats[atHH]] = F_VSITE2;
1288 nvsite++;
1289 /* assume we also want the COH angle constrained: */
1290 tmp1 = bond_cc*std::cos(0.5*ANGLE_6RING) + dCGCE*std::sin(ANGLE_6RING*0.5) + bond_co;
1291 dCGM = std::sqrt( cosrule(tmp1, vdist, angle_coh) );
1292 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], add_shift+atM, dCGM);
1293 my_add_param(&(plist[F_CONSTRNC]), ats[atOH], add_shift+atM, vdist);
1295 add_vsite2_param(&plist[F_VSITE2],
1296 ats[atHH], ats[atOH], add_shift+atM, 1.0/2.0);
1297 return nvsite;
1300 static int gen_vsites_his(t_atoms *at, int *vsite_type[],
1301 gmx::ArrayRef<InteractionsOfType> plist,
1302 int nrfound, int *ats, gmx::ArrayRef<const VirtualSiteTopology> vsitetop)
1304 int nvsite, i;
1305 real a, b, alpha, dCGCE1, dCGNE2;
1306 real sinalpha, cosalpha;
1307 real xcom, ycom, mtot;
1308 real mG, mrest, mCE1, mNE2;
1309 real b_CG_ND1, b_ND1_CE1, b_CE1_NE2, b_CG_CD2, b_CD2_NE2;
1310 real b_ND1_HD1, b_NE2_HE2, b_CE1_HE1, b_CD2_HD2;
1311 real a_CG_ND1_CE1, a_CG_CD2_NE2, a_ND1_CE1_NE2, a_CE1_NE2_CD2;
1312 real a_NE2_CE1_HE1, a_NE2_CD2_HD2, a_CE1_ND1_HD1, a_CE1_NE2_HE2;
1313 char resname[10];
1315 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
1316 enum {
1317 atCG, atND1, atHD1, atCD2, atHD2, atCE1, atHE1, atNE2, atHE2, atNR
1319 real x[atNR], y[atNR];
1321 /* CG, CE1 and NE2 stay, each gets part of the total mass,
1322 rest gets virtualized */
1323 /* check number of atoms, 3 hydrogens may be missing: */
1324 /* assert( nrfound >= atNR-3 || nrfound <= atNR );
1325 * Don't understand the above logic. Shouldn't it be && rather than || ???
1327 if ((nrfound < atNR-3) || (nrfound > atNR))
1329 gmx_incons("Generating vsites for HIS");
1332 /* avoid warnings about uninitialized variables */
1333 b_ND1_HD1 = b_NE2_HE2 = b_CE1_HE1 = b_CD2_HD2 = a_NE2_CE1_HE1 =
1334 a_NE2_CD2_HD2 = a_CE1_ND1_HD1 = a_CE1_NE2_HE2 = 0;
1336 if (ats[atHD1] != NOTSET)
1338 if (ats[atHE2] != NOTSET)
1340 sprintf(resname, "HISH");
1342 else
1344 sprintf(resname, "HISA");
1347 else
1349 sprintf(resname, "HISB");
1352 /* Get geometry from database */
1353 b_CG_ND1 = get_ddb_bond(vsitetop, resname, "CG", "ND1");
1354 b_ND1_CE1 = get_ddb_bond(vsitetop, resname, "ND1", "CE1");
1355 b_CE1_NE2 = get_ddb_bond(vsitetop, resname, "CE1", "NE2");
1356 b_CG_CD2 = get_ddb_bond(vsitetop, resname, "CG", "CD2");
1357 b_CD2_NE2 = get_ddb_bond(vsitetop, resname, "CD2", "NE2");
1358 a_CG_ND1_CE1 = DEG2RAD*get_ddb_angle(vsitetop, resname, "CG", "ND1", "CE1");
1359 a_CG_CD2_NE2 = DEG2RAD*get_ddb_angle(vsitetop, resname, "CG", "CD2", "NE2");
1360 a_ND1_CE1_NE2 = DEG2RAD*get_ddb_angle(vsitetop, resname, "ND1", "CE1", "NE2");
1361 a_CE1_NE2_CD2 = DEG2RAD*get_ddb_angle(vsitetop, resname, "CE1", "NE2", "CD2");
1363 if (ats[atHD1] != NOTSET)
1365 b_ND1_HD1 = get_ddb_bond(vsitetop, resname, "ND1", "HD1");
1366 a_CE1_ND1_HD1 = DEG2RAD*get_ddb_angle(vsitetop, resname, "CE1", "ND1", "HD1");
1368 if (ats[atHE2] != NOTSET)
1370 b_NE2_HE2 = get_ddb_bond(vsitetop, resname, "NE2", "HE2");
1371 a_CE1_NE2_HE2 = DEG2RAD*get_ddb_angle(vsitetop, resname, "CE1", "NE2", "HE2");
1373 if (ats[atHD2] != NOTSET)
1375 b_CD2_HD2 = get_ddb_bond(vsitetop, resname, "CD2", "HD2");
1376 a_NE2_CD2_HD2 = DEG2RAD*get_ddb_angle(vsitetop, resname, "NE2", "CD2", "HD2");
1378 if (ats[atHE1] != NOTSET)
1380 b_CE1_HE1 = get_ddb_bond(vsitetop, resname, "CE1", "HE1");
1381 a_NE2_CE1_HE1 = DEG2RAD*get_ddb_angle(vsitetop, resname, "NE2", "CE1", "HE1");
1384 /* constraints between CG, CE1 and NE1 */
1385 dCGCE1 = std::sqrt( cosrule(b_CG_ND1, b_ND1_CE1, a_CG_ND1_CE1) );
1386 dCGNE2 = std::sqrt( cosrule(b_CG_CD2, b_CD2_NE2, a_CG_CD2_NE2) );
1388 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], ats[atCE1], dCGCE1);
1389 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], ats[atNE2], dCGNE2);
1390 /* we already have a constraint CE1-NE2, so we don't add it again */
1392 /* calculate the positions in a local frame of reference.
1393 * The x-axis is the line from CG that makes a right angle
1394 * with the bond CE1-NE2, and the y-axis the bond CE1-NE2.
1396 /* First calculate the x-axis intersection with y-axis (=yCE1).
1397 * Get cos(angle CG-CE1-NE2) :
1399 cosalpha = acosrule(dCGNE2, dCGCE1, b_CE1_NE2);
1400 x[atCE1] = 0;
1401 y[atCE1] = cosalpha*dCGCE1;
1402 x[atNE2] = 0;
1403 y[atNE2] = y[atCE1]-b_CE1_NE2;
1404 sinalpha = std::sqrt(1-cosalpha*cosalpha);
1405 x[atCG] = -sinalpha*dCGCE1;
1406 y[atCG] = 0;
1407 x[atHE1] = x[atHE2] = x[atHD1] = x[atHD2] = 0;
1408 y[atHE1] = y[atHE2] = y[atHD1] = y[atHD2] = 0;
1410 /* calculate ND1 and CD2 positions from CE1 and NE2 */
1412 x[atND1] = -b_ND1_CE1*std::sin(a_ND1_CE1_NE2);
1413 y[atND1] = y[atCE1]-b_ND1_CE1*std::cos(a_ND1_CE1_NE2);
1415 x[atCD2] = -b_CD2_NE2*std::sin(a_CE1_NE2_CD2);
1416 y[atCD2] = y[atNE2]+b_CD2_NE2*std::cos(a_CE1_NE2_CD2);
1418 /* And finally the hydrogen positions */
1419 if (ats[atHE1] != NOTSET)
1421 x[atHE1] = x[atCE1] + b_CE1_HE1*std::sin(a_NE2_CE1_HE1);
1422 y[atHE1] = y[atCE1] - b_CE1_HE1*std::cos(a_NE2_CE1_HE1);
1424 /* HD2 - first get (ccw) angle from (positive) y-axis */
1425 if (ats[atHD2] != NOTSET)
1427 alpha = a_CE1_NE2_CD2 + M_PI - a_NE2_CD2_HD2;
1428 x[atHD2] = x[atCD2] - b_CD2_HD2*std::sin(alpha);
1429 y[atHD2] = y[atCD2] + b_CD2_HD2*std::cos(alpha);
1431 if (ats[atHD1] != NOTSET)
1433 /* HD1 - first get (cw) angle from (positive) y-axis */
1434 alpha = a_ND1_CE1_NE2 + M_PI - a_CE1_ND1_HD1;
1435 x[atHD1] = x[atND1] - b_ND1_HD1*std::sin(alpha);
1436 y[atHD1] = y[atND1] - b_ND1_HD1*std::cos(alpha);
1438 if (ats[atHE2] != NOTSET)
1440 x[atHE2] = x[atNE2] + b_NE2_HE2*std::sin(a_CE1_NE2_HE2);
1441 y[atHE2] = y[atNE2] + b_NE2_HE2*std::cos(a_CE1_NE2_HE2);
1443 /* Have all coordinates now */
1445 /* calc center-of-mass; keep atoms CG, CE1, NE2 and
1446 * set the rest to vsite3
1448 mtot = xcom = ycom = 0;
1449 nvsite = 0;
1450 for (i = 0; i < atNR; i++)
1452 if (ats[i] != NOTSET)
1454 mtot += at->atom[ats[i]].m;
1455 xcom += x[i]*at->atom[ats[i]].m;
1456 ycom += y[i]*at->atom[ats[i]].m;
1457 if (i != atCG && i != atCE1 && i != atNE2)
1459 at->atom[ats[i]].m = at->atom[ats[i]].mB = 0;
1460 (*vsite_type)[ats[i]] = F_VSITE3;
1461 nvsite++;
1465 if (nvsite+3 != nrfound)
1467 gmx_incons("Generating vsites for HIS");
1470 xcom /= mtot;
1471 ycom /= mtot;
1473 /* distribute mass so that com stays the same */
1474 mG = xcom*mtot/x[atCG];
1475 mrest = mtot-mG;
1476 mCE1 = (ycom-y[atNE2])*mrest/(y[atCE1]-y[atNE2]);
1477 mNE2 = mrest-mCE1;
1479 at->atom[ats[atCG]].m = at->atom[ats[atCG]].mB = mG;
1480 at->atom[ats[atCE1]].m = at->atom[ats[atCE1]].mB = mCE1;
1481 at->atom[ats[atNE2]].m = at->atom[ats[atNE2]].mB = mNE2;
1483 /* HE1 */
1484 if (ats[atHE1] != NOTSET)
1486 calc_vsite3_param(x[atHE1], y[atHE1], x[atCE1], y[atCE1], x[atNE2], y[atNE2],
1487 x[atCG], y[atCG], &a, &b);
1488 add_vsite3_param(&plist[F_VSITE3],
1489 ats[atHE1], ats[atCE1], ats[atNE2], ats[atCG], a, b);
1491 /* HE2 */
1492 if (ats[atHE2] != NOTSET)
1494 calc_vsite3_param(x[atHE2], y[atHE2], x[atNE2], y[atNE2], x[atCE1], y[atCE1],
1495 x[atCG], y[atCG], &a, &b);
1496 add_vsite3_param(&plist[F_VSITE3],
1497 ats[atHE2], ats[atNE2], ats[atCE1], ats[atCG], a, b);
1500 /* ND1 */
1501 calc_vsite3_param(x[atND1], y[atND1], x[atNE2], y[atNE2], x[atCE1], y[atCE1],
1502 x[atCG], y[atCG], &a, &b);
1503 add_vsite3_param(&plist[F_VSITE3],
1504 ats[atND1], ats[atNE2], ats[atCE1], ats[atCG], a, b);
1506 /* CD2 */
1507 calc_vsite3_param(x[atCD2], y[atCD2], x[atCE1], y[atCE1], x[atNE2], y[atNE2],
1508 x[atCG], y[atCG], &a, &b);
1509 add_vsite3_param(&plist[F_VSITE3],
1510 ats[atCD2], ats[atCE1], ats[atNE2], ats[atCG], a, b);
1512 /* HD1 */
1513 if (ats[atHD1] != NOTSET)
1515 calc_vsite3_param(x[atHD1], y[atHD1], x[atNE2], y[atNE2], x[atCE1], y[atCE1],
1516 x[atCG], y[atCG], &a, &b);
1517 add_vsite3_param(&plist[F_VSITE3],
1518 ats[atHD1], ats[atNE2], ats[atCE1], ats[atCG], a, b);
1520 /* HD2 */
1521 if (ats[atHD2] != NOTSET)
1523 calc_vsite3_param(x[atHD2], y[atHD2], x[atCE1], y[atCE1], x[atNE2], y[atNE2],
1524 x[atCG], y[atCG], &a, &b);
1525 add_vsite3_param(&plist[F_VSITE3],
1526 ats[atHD2], ats[atCE1], ats[atNE2], ats[atCG], a, b);
1528 return nvsite;
1531 static bool is_vsite(int vsite_type)
1533 if (vsite_type == NOTSET)
1535 return FALSE;
1537 switch (abs(vsite_type) )
1539 case F_VSITE3:
1540 case F_VSITE3FD:
1541 case F_VSITE3OUT:
1542 case F_VSITE3FAD:
1543 case F_VSITE4FD:
1544 case F_VSITE4FDN:
1545 return TRUE;
1546 default:
1547 return FALSE;
1551 static char atomnamesuffix[] = "1234";
1553 void do_vsites(gmx::ArrayRef<const PreprocessResidue> rtpFFDB, PreprocessingAtomTypes *atype,
1554 t_atoms *at, t_symtab *symtab,
1555 std::vector<gmx::RVec> *x,
1556 gmx::ArrayRef<InteractionsOfType> plist, int *vsite_type[], int *cgnr[],
1557 real mHmult, bool bVsiteAromatics,
1558 const char *ffdir)
1560 #define MAXATOMSPERRESIDUE 16
1561 int k, m, i0, ni0, whatres, add_shift, nvsite, nadd;
1562 int ai, aj, ak, al;
1563 int nrfound = 0, needed, nrbonds, nrHatoms, Heavy, nrheavies, tpM, tpHeavy;
1564 int Hatoms[4], heavies[4];
1565 bool bWARNING, bAddVsiteParam, bFirstWater;
1566 matrix tmpmat;
1567 real mHtot, mtot, fact, fact2;
1568 rvec rpar, rperp, temp;
1569 char tpname[32], nexttpname[32];
1570 int *o2n, *newvsite_type, *newcgnr, ats[MAXATOMSPERRESIDUE];
1571 t_atom *newatom;
1572 char ***newatomname;
1573 int cmplength;
1574 bool isN, planarN, bFound;
1576 /* if bVsiteAromatics=TRUE do_vsites will specifically convert atoms in
1577 PHE, TRP, TYR and HIS to a construction of virtual sites */
1578 enum {
1579 resPHE, resTRP, resTYR, resHIS, resNR
1581 const char *resnms[resNR] = { "PHE", "TRP", "TYR", "HIS" };
1582 /* Amber03 alternative names for termini */
1583 const char *resnmsN[resNR] = { "NPHE", "NTRP", "NTYR", "NHIS" };
1584 const char *resnmsC[resNR] = { "CPHE", "CTRP", "CTYR", "CHIS" };
1585 /* HIS can be known as HISH, HIS1, HISA, HID, HIE, HIP, etc. too */
1586 bool bPartial[resNR] = { FALSE, FALSE, FALSE, TRUE };
1587 /* the atnms for every residue MUST correspond to the enums in the
1588 gen_vsites_* (one for each residue) routines! */
1589 /* also the atom names in atnms MUST be in the same order as in the .rtp! */
1590 const char *atnms[resNR][MAXATOMSPERRESIDUE+1] = {
1591 { "CG", /* PHE */
1592 "CD1", "HD1", "CD2", "HD2",
1593 "CE1", "HE1", "CE2", "HE2",
1594 "CZ", "HZ", nullptr },
1595 { "CB", /* TRP */
1596 "CG",
1597 "CD1", "HD1", "CD2",
1598 "NE1", "HE1", "CE2", "CE3", "HE3",
1599 "CZ2", "HZ2", "CZ3", "HZ3",
1600 "CH2", "HH2", nullptr },
1601 { "CG", /* TYR */
1602 "CD1", "HD1", "CD2", "HD2",
1603 "CE1", "HE1", "CE2", "HE2",
1604 "CZ", "OH", "HH", nullptr },
1605 { "CG", /* HIS */
1606 "ND1", "HD1", "CD2", "HD2",
1607 "CE1", "HE1", "NE2", "HE2", nullptr }
1610 if (debug)
1612 printf("Searching for atoms to make virtual sites ...\n");
1613 fprintf(debug, "# # # VSITES # # #\n");
1616 std::vector<std::string> db = fflib_search_file_end(ffdir, ".vsd", FALSE);
1618 /* Container of CH3/NH3/NH2 configuration entries.
1619 * See comments in read_vsite_database. It isnt beautiful,
1620 * but it had to be fixed, and I dont even want to try to
1621 * maintain this part of the code...
1623 std::vector<VirtualSiteConfiguration> vsiteconflist;
1625 // TODO those have been deprecated and should be removed completely.
1626 /* Container of geometry (bond/angle) entries for
1627 * residues like PHE, TRP, TYR, HIS, etc., where we need
1628 * to know the geometry to construct vsite aromatics.
1629 * Note that equilibrium geometry isnt necessarily the same
1630 * as the individual bond and angle values given in the
1631 * force field (rings can be strained).
1633 std::vector<VirtualSiteTopology> vsitetop;
1634 for (const auto &filename : db)
1636 read_vsite_database(filename.c_str(), &vsiteconflist, &vsitetop);
1639 bFirstWater = TRUE;
1640 nvsite = 0;
1641 nadd = 0;
1642 /* we need a marker for which atoms should *not* be renumbered afterwards */
1643 add_shift = 10*at->nr;
1644 /* make arrays where masses can be inserted into */
1645 std::vector<gmx::RVec> newx(at->nr);
1646 snew(newatom, at->nr);
1647 snew(newatomname, at->nr);
1648 snew(newvsite_type, at->nr);
1649 snew(newcgnr, at->nr);
1650 /* make index array to tell where the atoms go to when masses are inserted */
1651 snew(o2n, at->nr);
1652 for (int i = 0; i < at->nr; i++)
1654 o2n[i] = i;
1656 /* make index to tell which residues were already processed */
1657 std::vector<bool> bResProcessed(at->nres);
1659 ResidueType rt;
1661 /* generate vsite constructions */
1662 /* loop over all atoms */
1663 int resind = -1;
1664 for (int i = 0; (i < at->nr); i++)
1666 if (at->atom[i].resind != resind)
1668 resind = at->atom[i].resind;
1670 const char *resnm = *(at->resinfo[resind].name);
1671 /* first check for aromatics to virtualize */
1672 /* don't waste our effort on DNA, water etc. */
1673 /* Only do the vsite aromatic stuff when we reach the
1674 * CA atom, since there might be an X2/X3 group on the
1675 * N-terminus that must be treated first.
1677 if (bVsiteAromatics &&
1678 (strcmp(*(at->atomname[i]), "CA") == 0) &&
1679 !bResProcessed[resind] &&
1680 rt.namedResidueHasType(*(at->resinfo[resind].name), "Protein") )
1682 /* mark this residue */
1683 bResProcessed[resind] = TRUE;
1684 /* find out if this residue needs converting */
1685 whatres = NOTSET;
1686 for (int j = 0; j < resNR && whatres == NOTSET; j++)
1689 cmplength = bPartial[j] ? strlen(resnm)-1 : strlen(resnm);
1691 bFound = ((gmx::equalCaseInsensitive(resnm, resnms[j], cmplength)) ||
1692 (gmx::equalCaseInsensitive(resnm, resnmsN[j], cmplength)) ||
1693 (gmx::equalCaseInsensitive(resnm, resnmsC[j], cmplength)));
1695 if (bFound)
1697 whatres = j;
1698 /* get atoms we will be needing for the conversion */
1699 nrfound = 0;
1700 for (k = 0; atnms[j][k]; k++)
1702 ats[k] = NOTSET;
1703 for (m = i; m < at->nr && at->atom[m].resind == resind && ats[k] == NOTSET; m++)
1705 if (gmx_strcasecmp(*(at->atomname[m]), atnms[j][k]) == 0)
1707 ats[k] = m;
1708 nrfound++;
1713 /* now k is number of atom names in atnms[j] */
1714 if (j == resHIS)
1716 needed = k-3;
1718 else
1720 needed = k;
1722 if (nrfound < needed)
1724 gmx_fatal(FARGS, "not enough atoms found (%d, need %d) in "
1725 "residue %s %d while\n "
1726 "generating aromatics virtual site construction",
1727 nrfound, needed, resnm, at->resinfo[resind].nr);
1729 /* Advance overall atom counter */
1730 i++;
1733 /* the enums for every residue MUST correspond to atnms[residue] */
1734 switch (whatres)
1736 case resPHE:
1737 if (debug)
1739 fprintf(stderr, "PHE at %d\n", o2n[ats[0]]+1);
1741 nvsite += gen_vsites_phe(at, vsite_type, plist, nrfound, ats, vsitetop);
1742 break;
1743 case resTRP:
1744 if (debug)
1746 fprintf(stderr, "TRP at %d\n", o2n[ats[0]]+1);
1748 nvsite += gen_vsites_trp(atype, &newx, &newatom, &newatomname, &o2n,
1749 &newvsite_type, &newcgnr, symtab, &nadd, *x, cgnr,
1750 at, vsite_type, plist, nrfound, ats, add_shift, vsitetop);
1751 break;
1752 case resTYR:
1753 if (debug)
1755 fprintf(stderr, "TYR at %d\n", o2n[ats[0]]+1);
1757 nvsite += gen_vsites_tyr(atype, &newx, &newatom, &newatomname, &o2n,
1758 &newvsite_type, &newcgnr, symtab, &nadd, *x, cgnr,
1759 at, vsite_type, plist, nrfound, ats, add_shift, vsitetop);
1760 break;
1761 case resHIS:
1762 if (debug)
1764 fprintf(stderr, "HIS at %d\n", o2n[ats[0]]+1);
1766 nvsite += gen_vsites_his(at, vsite_type, plist, nrfound, ats, vsitetop);
1767 break;
1768 case NOTSET:
1769 /* this means this residue won't be processed */
1770 break;
1771 default:
1772 gmx_fatal(FARGS, "DEATH HORROR in do_vsites (%s:%d)",
1773 __FILE__, __LINE__);
1774 } /* switch whatres */
1775 /* skip back to beginning of residue */
1776 while (i > 0 && at->atom[i-1].resind == resind)
1778 i--;
1780 } /* if bVsiteAromatics & is protein */
1782 /* now process the rest of the hydrogens */
1783 /* only process hydrogen atoms which are not already set */
1784 if ( ((*vsite_type)[i] == NOTSET) && is_hydrogen(*(at->atomname[i])))
1786 /* find heavy atom, count #bonds from it and #H atoms bound to it
1787 and return H atom numbers (Hatoms) and heavy atom numbers (heavies) */
1788 count_bonds(i, &plist[F_BONDS], at->atomname,
1789 &nrbonds, &nrHatoms, Hatoms, &Heavy, &nrheavies, heavies);
1790 /* get Heavy atom type */
1791 tpHeavy = get_atype(Heavy, at, rtpFFDB, &rt);
1792 strcpy(tpname, atype->atomNameFromAtomType(tpHeavy));
1794 bWARNING = FALSE;
1795 bAddVsiteParam = TRUE;
1796 /* nested if's which check nrHatoms, nrbonds and atomname */
1797 if (nrHatoms == 1)
1799 switch (nrbonds)
1801 case 2: /* -O-H */
1802 (*vsite_type)[i] = F_BONDS;
1803 break;
1804 case 3: /* =CH-, -NH- or =NH+- */
1805 (*vsite_type)[i] = F_VSITE3FD;
1806 break;
1807 case 4: /* --CH- (tert) */
1808 /* The old type 4FD had stability issues, so
1809 * all new constructs should use 4FDN
1811 (*vsite_type)[i] = F_VSITE4FDN;
1813 /* Check parity of heavy atoms from coordinates */
1814 ai = Heavy;
1815 aj = heavies[0];
1816 ak = heavies[1];
1817 al = heavies[2];
1818 rvec_sub((*x)[aj], (*x)[ai], tmpmat[0]);
1819 rvec_sub((*x)[ak], (*x)[ai], tmpmat[1]);
1820 rvec_sub((*x)[al], (*x)[ai], tmpmat[2]);
1822 if (det(tmpmat) > 0)
1824 /* swap parity */
1825 heavies[1] = aj;
1826 heavies[0] = ak;
1829 break;
1830 default: /* nrbonds != 2, 3 or 4 */
1831 bWARNING = TRUE;
1835 else if ( (nrHatoms == 2) && (nrbonds == 2) &&
1836 (at->atom[Heavy].atomnumber == 8) )
1838 bAddVsiteParam = FALSE; /* this is water: skip these hydrogens */
1839 if (bFirstWater)
1841 bFirstWater = FALSE;
1842 if (debug)
1844 fprintf(debug,
1845 "Not converting hydrogens in water to virtual sites\n");
1849 else if ( (nrHatoms == 2) && (nrbonds == 4) )
1851 /* -CH2- , -NH2+- */
1852 (*vsite_type)[Hatoms[0]] = F_VSITE3OUT;
1853 (*vsite_type)[Hatoms[1]] = -F_VSITE3OUT;
1855 else
1857 /* 2 or 3 hydrogen atom, with 3 or 4 bonds in total to the heavy atom.
1858 * If it is a nitrogen, first check if it is planar.
1860 isN = planarN = FALSE;
1861 if ((nrHatoms == 2) && ((*at->atomname[Heavy])[0] == 'N'))
1863 isN = TRUE;
1864 int j = nitrogen_is_planar(vsiteconflist, tpname);
1865 if (j < 0)
1867 gmx_fatal(FARGS, "No vsite database NH2 entry for type %s\n", tpname);
1869 planarN = (j == 1);
1871 if ( (nrHatoms == 2) && (nrbonds == 3) && ( !isN || planarN ) )
1873 /* =CH2 or, if it is a nitrogen NH2, it is a planar one */
1874 (*vsite_type)[Hatoms[0]] = F_VSITE3FAD;
1875 (*vsite_type)[Hatoms[1]] = -F_VSITE3FAD;
1877 else if ( ( (nrHatoms == 2) && (nrbonds == 3) &&
1878 ( isN && !planarN ) ) ||
1879 ( (nrHatoms == 3) && (nrbonds == 4) ) )
1881 /* CH3, NH3 or non-planar NH2 group */
1882 int Hat_vsite_type[3] = { F_VSITE3, F_VSITE3OUT, F_VSITE3OUT };
1883 bool Hat_SwapParity[3] = { FALSE, TRUE, FALSE };
1885 if (debug)
1887 fprintf(stderr, "-XH3 or nonplanar NH2 group at %d\n", i+1);
1889 bAddVsiteParam = FALSE; /* we'll do this ourselves! */
1890 /* -NH2 (umbrella), -NH3+ or -CH3 */
1891 (*vsite_type)[Heavy] = F_VSITE3;
1892 for (int j = 0; j < nrHatoms; j++)
1894 (*vsite_type)[Hatoms[j]] = Hat_vsite_type[j];
1896 /* get dummy mass type from first char of heavy atom type (N or C) */
1898 strcpy(nexttpname, atype->atomNameFromAtomType(get_atype(heavies[0], at, rtpFFDB, &rt)));
1899 std::string ch = get_dummymass_name(vsiteconflist, tpname, nexttpname);
1900 std::string name;
1901 if (ch.empty())
1903 if (!db.empty())
1905 gmx_fatal(FARGS, "Can't find dummy mass for type %s bonded to type %s in the virtual site database (.vsd files). Add it to the database!\n", tpname, nexttpname);
1907 else
1909 gmx_fatal(FARGS, "A dummy mass for type %s bonded to type %s is required, but no virtual site database (.vsd) files where found.\n", tpname, nexttpname);
1912 else
1914 name = ch;
1917 tpM = vsite_nm2type(name.c_str(), atype);
1918 /* make space for 2 masses: shift all atoms starting with 'Heavy' */
1919 #define NMASS 2
1920 i0 = Heavy;
1921 ni0 = i0+nadd;
1922 if (debug)
1924 fprintf(stderr, "Inserting %d dummy masses at %d\n", NMASS, o2n[i0]+1);
1926 nadd += NMASS;
1927 for (int j = i0; j < at->nr; j++)
1929 o2n[j] = j+nadd;
1932 newx.resize(at->nr+nadd);
1933 srenew(newatom, at->nr+nadd);
1934 srenew(newatomname, at->nr+nadd);
1935 srenew(newvsite_type, at->nr+nadd);
1936 srenew(newcgnr, at->nr+nadd);
1938 for (int j = 0; j < NMASS; j++)
1940 newatomname[at->nr+nadd-1-j] = nullptr;
1943 /* calculate starting position for the masses */
1944 mHtot = 0;
1945 /* get atom masses, and set Heavy and Hatoms mass to zero */
1946 for (int j = 0; j < nrHatoms; j++)
1948 mHtot += get_amass(Hatoms[j], at, rtpFFDB, &rt);
1949 at->atom[Hatoms[j]].m = at->atom[Hatoms[j]].mB = 0;
1951 mtot = mHtot + get_amass(Heavy, at, rtpFFDB, &rt);
1952 at->atom[Heavy].m = at->atom[Heavy].mB = 0;
1953 if (mHmult != 1.0)
1955 mHtot *= mHmult;
1957 fact2 = mHtot/mtot;
1958 fact = std::sqrt(fact2);
1959 /* generate vectors parallel and perpendicular to rotational axis:
1960 * rpar = Heavy -> Hcom
1961 * rperp = Hcom -> H1 */
1962 clear_rvec(rpar);
1963 for (int j = 0; j < nrHatoms; j++)
1965 rvec_inc(rpar, (*x)[Hatoms[j]]);
1967 svmul(1.0/nrHatoms, rpar, rpar); /* rpar = ( H1+H2+H3 ) / 3 */
1968 rvec_dec(rpar, (*x)[Heavy]); /* - Heavy */
1969 rvec_sub((*x)[Hatoms[0]], (*x)[Heavy], rperp);
1970 rvec_dec(rperp, rpar); /* rperp = H1 - Heavy - rpar */
1971 /* calc mass positions */
1972 svmul(fact2, rpar, temp);
1973 for (int j = 0; (j < NMASS); j++) /* xM = xN + fact2 * rpar +/- fact * rperp */
1975 rvec_add((*x)[Heavy], temp, newx[ni0+j]);
1977 svmul(fact, rperp, temp);
1978 rvec_inc(newx[ni0 ], temp);
1979 rvec_dec(newx[ni0+1], temp);
1980 /* set atom parameters for the masses */
1981 for (int j = 0; (j < NMASS); j++)
1983 /* make name: "M??#" or "M?#" (? is atomname, # is number) */
1984 name[0] = 'M';
1985 int k;
1986 for (k = 0; (*at->atomname[Heavy])[k] && ( k < NMASS ); k++)
1988 name[k+1] = (*at->atomname[Heavy])[k];
1990 name[k+1] = atomnamesuffix[j];
1991 name[k+2] = '\0';
1992 newatomname[ni0+j] = put_symtab(symtab, name.c_str());
1993 newatom[ni0+j].m = newatom[ni0+j].mB = mtot/NMASS;
1994 newatom[ni0+j].q = newatom[ni0+j].qB = 0.0;
1995 newatom[ni0+j].type = newatom[ni0+j].typeB = tpM;
1996 newatom[ni0+j].ptype = eptAtom;
1997 newatom[ni0+j].resind = at->atom[i0].resind;
1998 newatom[ni0+j].elem[0] = 'M';
1999 newatom[ni0+j].elem[1] = '\0';
2000 newvsite_type[ni0+j] = NOTSET;
2001 newcgnr[ni0+j] = (*cgnr)[i0];
2003 /* add constraints between dummy masses and to heavies[0] */
2004 /* 'add_shift' says which atoms won't be renumbered afterwards */
2005 my_add_param(&(plist[F_CONSTRNC]), heavies[0], add_shift+ni0, NOTSET);
2006 my_add_param(&(plist[F_CONSTRNC]), heavies[0], add_shift+ni0+1, NOTSET);
2007 my_add_param(&(plist[F_CONSTRNC]), add_shift+ni0, add_shift+ni0+1, NOTSET);
2009 /* generate Heavy, H1, H2 and H3 from M1, M2 and heavies[0] */
2010 /* note that vsite_type cannot be NOTSET, because we just set it */
2011 add_vsite3_atoms (&plist[(*vsite_type)[Heavy]],
2012 Heavy, heavies[0], add_shift+ni0, add_shift+ni0+1,
2013 FALSE);
2014 for (int j = 0; j < nrHatoms; j++)
2016 add_vsite3_atoms(&plist[(*vsite_type)[Hatoms[j]]],
2017 Hatoms[j], heavies[0], add_shift+ni0, add_shift+ni0+1,
2018 Hat_SwapParity[j]);
2020 #undef NMASS
2022 else
2024 bWARNING = TRUE;
2028 if (bWARNING)
2030 gmx_fatal(FARGS, "Cannot convert atom %d %s (bound to a heavy atom "
2031 "%s with \n"
2032 " %d bonds and %d bound hydrogens atoms) to virtual site\n",
2033 i+1, *(at->atomname[i]), tpname, nrbonds, nrHatoms);
2035 if (bAddVsiteParam)
2037 /* add vsite parameters to topology,
2038 also get rid of negative vsite_types */
2039 add_vsites(plist, (*vsite_type), Heavy, nrHatoms, Hatoms,
2040 nrheavies, heavies);
2041 /* transfer mass of virtual site to Heavy atom */
2042 for (int j = 0; j < nrHatoms; j++)
2044 if (is_vsite((*vsite_type)[Hatoms[j]]))
2046 at->atom[Heavy].m += at->atom[Hatoms[j]].m;
2047 at->atom[Heavy].mB = at->atom[Heavy].m;
2048 at->atom[Hatoms[j]].m = at->atom[Hatoms[j]].mB = 0;
2052 nvsite += nrHatoms;
2053 if (debug)
2055 fprintf(debug, "atom %d: ", o2n[i]+1);
2056 print_bonds(debug, o2n, nrHatoms, Hatoms, Heavy, nrheavies, heavies);
2058 } /* if vsite NOTSET & is hydrogen */
2060 } /* for i < at->nr */
2062 if (debug)
2064 fprintf(debug, "Before inserting new atoms:\n");
2065 for (int i = 0; i < at->nr; i++)
2067 fprintf(debug, "%4d %4d %4s %4d %4s %6d %-10s\n", i+1, o2n[i]+1,
2068 at->atomname[i] ? *(at->atomname[i]) : "(NULL)",
2069 at->resinfo[at->atom[i].resind].nr,
2070 at->resinfo[at->atom[i].resind].name ?
2071 *(at->resinfo[at->atom[i].resind].name) : "(NULL)",
2072 (*cgnr)[i],
2073 ((*vsite_type)[i] == NOTSET) ?
2074 "NOTSET" : interaction_function[(*vsite_type)[i]].name);
2076 fprintf(debug, "new atoms to be inserted:\n");
2077 for (int i = 0; i < at->nr+nadd; i++)
2079 if (newatomname[i])
2081 fprintf(debug, "%4d %4s %4d %6d %-10s\n", i+1,
2082 newatomname[i] ? *(newatomname[i]) : "(NULL)",
2083 newatom[i].resind, newcgnr[i],
2084 (newvsite_type[i] == NOTSET) ?
2085 "NOTSET" : interaction_function[newvsite_type[i]].name);
2090 /* add all original atoms to the new arrays, using o2n index array */
2091 for (int i = 0; i < at->nr; i++)
2093 newatomname [o2n[i]] = at->atomname [i];
2094 newatom [o2n[i]] = at->atom [i];
2095 newvsite_type[o2n[i]] = (*vsite_type)[i];
2096 newcgnr [o2n[i]] = (*cgnr) [i];
2097 copy_rvec((*x)[i], newx[o2n[i]]);
2099 /* throw away old atoms */
2100 sfree(at->atom);
2101 sfree(at->atomname);
2102 sfree(*vsite_type);
2103 sfree(*cgnr);
2104 /* put in the new ones */
2105 at->nr += nadd;
2106 at->atom = newatom;
2107 at->atomname = newatomname;
2108 *vsite_type = newvsite_type;
2109 *cgnr = newcgnr;
2110 *x = newx;
2111 if (at->nr > add_shift)
2113 gmx_fatal(FARGS, "Added impossible amount of dummy masses "
2114 "(%d on a total of %d atoms)\n", nadd, at->nr-nadd);
2117 if (debug)
2119 fprintf(debug, "After inserting new atoms:\n");
2120 for (int i = 0; i < at->nr; i++)
2122 fprintf(debug, "%4d %4s %4d %4s %6d %-10s\n", i+1,
2123 at->atomname[i] ? *(at->atomname[i]) : "(NULL)",
2124 at->resinfo[at->atom[i].resind].nr,
2125 at->resinfo[at->atom[i].resind].name ?
2126 *(at->resinfo[at->atom[i].resind].name) : "(NULL)",
2127 (*cgnr)[i],
2128 ((*vsite_type)[i] == NOTSET) ?
2129 "NOTSET" : interaction_function[(*vsite_type)[i]].name);
2133 /* now renumber all the interactions because of the added atoms */
2134 for (int ftype = 0; ftype < F_NRE; ftype++)
2136 InteractionsOfType *params = &(plist[ftype]);
2137 if (debug)
2139 fprintf(debug, "Renumbering %zu %s\n", params->size(),
2140 interaction_function[ftype].longname);
2142 /* Horrible hacks needed here to get this to work */
2143 for (auto parm = params->interactionTypes.begin(); parm != params->interactionTypes.end(); parm++)
2145 gmx::ArrayRef<const int> atomNumbers(parm->atoms());
2146 std::vector<int> newAtomNumber;
2147 for (int j = 0; j < NRAL(ftype); j++)
2149 if (atomNumbers[j] >= add_shift)
2151 if (debug)
2153 fprintf(debug, " [%d -> %d]", atomNumbers[j],
2154 atomNumbers[j]-add_shift);
2156 newAtomNumber.emplace_back(atomNumbers[j]-add_shift);
2158 else
2160 if (debug)
2162 fprintf(debug, " [%d -> %d]", atomNumbers[j],
2163 o2n[atomNumbers[j]]);
2165 newAtomNumber.emplace_back(o2n[atomNumbers[j]]);
2168 *parm = InteractionOfType(newAtomNumber, parm->forceParam(), parm->interactionTypeName());
2169 if (debug)
2171 fprintf(debug, "\n");
2175 /* sort constraint parameters */
2176 InteractionsOfType *params = &(plist[F_CONSTRNC]);
2177 for (auto &type : params->interactionTypes)
2179 type.sortAtomIds();
2182 /* clean up */
2183 sfree(o2n);
2185 /* tell the user what we did */
2186 fprintf(stderr, "Marked %d virtual sites\n", nvsite);
2187 fprintf(stderr, "Added %d dummy masses\n", nadd);
2188 fprintf(stderr, "Added %zu new constraints\n", plist[F_CONSTRNC].size());
2191 void do_h_mass(InteractionsOfType *psb, int vsite_type[], t_atoms *at, real mHmult,
2192 bool bDeuterate)
2194 /* loop over all atoms */
2195 for (int i = 0; i < at->nr; i++)
2197 /* adjust masses if i is hydrogen and not a virtual site */
2198 if (!is_vsite(vsite_type[i]) && is_hydrogen(*(at->atomname[i])) )
2200 /* find bonded heavy atom */
2201 int a = NOTSET;
2202 for (auto parm = psb->interactionTypes.begin(); (parm != psb->interactionTypes.end()) && (a == NOTSET); parm++)
2204 /* if other atom is not a virtual site, it is the one we want */
2205 if ( (parm->ai() == i) &&
2206 !is_vsite(vsite_type[parm->aj()]) )
2208 a = parm->aj();
2210 else if ( (parm->aj() == i) &&
2211 !is_vsite(vsite_type[parm->ai()]) )
2213 a = parm->ai();
2216 if (a == NOTSET)
2218 gmx_fatal(FARGS, "Unbound hydrogen atom (%d) found while adjusting mass",
2219 i+1);
2222 /* adjust mass of i (hydrogen) with mHmult
2223 and correct mass of a (bonded atom) with same amount */
2224 if (!bDeuterate)
2226 at->atom[a].m -= (mHmult-1.0)*at->atom[i].m;
2227 at->atom[a].mB -= (mHmult-1.0)*at->atom[i].m;
2229 at->atom[i].m *= mHmult;
2230 at->atom[i].mB *= mHmult;