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.
39 #include "gen_vsite.h"
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"
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
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,
96 const std::string& nextheavy,
97 const std::string& dummy) :
101 nextHeavyType(nextheavy),
105 //! Type for the XH3/XH2 atom.
106 std::string atomtype;
107 /*! \brief Is the configuration planar?
109 * If true, the atomtype above and the three connected
110 * ones are in a planar geometry. The two next entries
111 * are undefined in that case.
113 bool isplanar = false;
114 //! cnumber of connected hydrogens.
116 //! Type for the heavy atom bonded to XH2/XH3.
117 std::string nextHeavyType;
118 //! The type of MNH* or MCH3* dummy mass to use.
119 std::string dummyMass;
123 /*!\libinternal \brief
124 * Virtual site topology datastructure.
126 * Structure to represent average bond and angles values in vsite aromatic
127 * residues. Note that these are NOT necessarily the bonds and angles from the
128 * forcefield; many forcefields (like Amber, OPLS) have some inherent strain in
129 * 5-rings (i.e. the sum of angles is !=540, but impropers keep it planar)
131 struct VirtualSiteTopology
134 * Explicit constructor
136 * \param[in] name Residue name.
138 explicit VirtualSiteTopology(const std::string& name) : resname(name) {}
141 //! Helper struct for single bond in virtual site.
142 struct VirtualSiteBond
145 * Explicit constructor
147 * \param[in] a1 First atom name.
148 * \param[in] a2 Second atom name.
149 * \param[in] v Value for distance.
151 VirtualSiteBond(const std::string& a1, const std::string& a2, real v) :
161 //! Distance value between atoms.
164 //! Container of all bonds in virtual site.
165 std::vector<VirtualSiteBond> bond;
166 //! Helper struct for single angle in virtual site.
167 struct VirtualSiteAngle
170 * Explicit constructor
172 * \param[in] a1 First atom name.
173 * \param[in] a2 Second atom name.
174 * \param[in] a3 Third atom name.
175 * \param[in] v Value for angle.
177 VirtualSiteAngle(const std::string& a1, const std::string& a2, const std::string& a3, real v) :
193 //! Container for all angles in virtual site.
194 std::vector<VirtualSiteAngle> angle;
212 typedef char t_dirname[STRLEN];
214 static const t_dirname ddb_dirnames[DDB_DIR_NR] = { "CH3", "NH3", "NH2", "PHE", "TYR",
215 "TRP", "HISA", "HISB", "HISH" };
217 static int ddb_name2dir(char* name)
219 /* Translate a directive name to the number of the directive.
220 * HID/HIE/HIP names are translated to the ones we use in Gromacs.
227 for (i = 0; i < DDB_DIR_NR && index < 0; i++)
229 if (!gmx_strcasecmp(name, ddb_dirnames[i]))
239 static void read_vsite_database(const char* ddbname,
240 std::vector<VirtualSiteConfiguration>* vsiteconflist,
241 std::vector<VirtualSiteTopology>* vsitetoplist)
243 /* This routine is a quick hack to fix the problem with hardcoded atomtypes
244 * and aromatic vsite parameters by reading them from a ff???.vsd file.
246 * The file can contain sections [ NH3 ], [ CH3 ], [ NH2 ], and ring residue names.
247 * For the NH3 and CH3 section each line has three fields. The first is the atomtype
248 * (nb: not bonded type) of the N/C atom to be replaced, the second field is
249 * the type of the next heavy atom it is bonded to, and the third field the type
250 * of dummy mass that will be used for this group.
252 * If the NH2 group planar (sp2 N) a different vsite construct is used, so in this
253 * case the second field should just be the word planar.
260 char s1[MAXNAME], s2[MAXNAME], s3[MAXNAME], s4[MAXNAME];
262 gmx::FilePtr ddb = gmx::openLibraryFile(ddbname);
266 while (fgets2(pline, STRLEN - 2, ddb.get()) != nullptr)
268 strip_comment(pline);
270 if (strlen(pline) > 0)
272 if (pline[0] == OPENDIR)
274 strncpy(dirstr, pline + 1, STRLEN - 2);
275 if ((ch = strchr(dirstr, CLOSEDIR)) != nullptr)
281 if (!gmx_strcasecmp(dirstr, "HID") || !gmx_strcasecmp(dirstr, "HISD"))
283 sprintf(dirstr, "HISA");
285 else if (!gmx_strcasecmp(dirstr, "HIE") || !gmx_strcasecmp(dirstr, "HISE"))
287 sprintf(dirstr, "HISB");
289 else if (!gmx_strcasecmp(dirstr, "HIP"))
291 sprintf(dirstr, "HISH");
294 curdir = ddb_name2dir(dirstr);
297 gmx_fatal(FARGS, "Invalid directive %s in vsite database %s", dirstr, ddbname);
305 gmx_fatal(FARGS, "First entry in vsite database must be a directive.\n");
310 int numberOfSites = sscanf(pline, "%s%s%s", s1, s2, s3);
311 std::string s1String = s1;
312 std::string s2String = s2;
313 std::string s3String = s3;
314 if (numberOfSites < 3 && gmx::equalCaseInsensitive(s2String, "planar"))
316 VirtualSiteConfiguration newVsiteConf(s1String, true, 2, "0", "0");
317 vsiteconflist->push_back(newVsiteConf);
319 else if (numberOfSites == 3)
321 VirtualSiteConfiguration newVsiteConf(s1String, false, -1, s2String, s3String);
322 if (curdir == DDB_NH2)
324 newVsiteConf.nHydrogens = 2;
328 newVsiteConf.nHydrogens = 3;
330 vsiteconflist->push_back(newVsiteConf);
334 gmx_fatal(FARGS, "Not enough directives in vsite database line: %s\n", pline);
345 const auto found = std::find_if(
346 vsitetoplist->begin(), vsitetoplist->end(), [&dirstr](const auto& entry) {
347 return gmx::equalCaseInsensitive(dirstr, entry.resname);
349 /* Allocate a new topology entry if this is a new residue */
350 if (found == vsitetoplist->end())
352 vsitetoplist->push_back(VirtualSiteTopology(dirstr));
354 int numberOfSites = sscanf(pline, "%s%s%s%s", s1, s2, s3, s4);
355 std::string s1String = s1;
356 std::string s2String = s2;
357 std::string s3String = s3;
359 if (numberOfSites == 3)
362 vsitetoplist->back().bond.emplace_back(s1String, s2String, strtod(s3, nullptr));
364 else if (numberOfSites == 4)
367 vsitetoplist->back().angle.emplace_back(s1String, s2String, s3String,
368 strtod(s4, nullptr));
374 "Need 3 or 4 values to specify bond/angle values in %s: %s\n",
381 "Didnt find a case for directive %s in read_vsite_database\n", dirstr);
388 static int nitrogen_is_planar(gmx::ArrayRef<const VirtualSiteConfiguration> vsiteconflist,
389 const std::string& atomtype)
391 /* Return 1 if atomtype exists in database list and is planar, 0 if not,
392 * and -1 if not found.
396 std::find_if(vsiteconflist.begin(), vsiteconflist.end(), [&atomtype](const auto& entry) {
397 return (gmx::equalCaseInsensitive(entry.atomtype, atomtype) && entry.nHydrogens == 2);
399 if (found != vsiteconflist.end())
401 res = static_cast<int>(found->isplanar);
411 static std::string get_dummymass_name(gmx::ArrayRef<const VirtualSiteConfiguration> vsiteconflist,
412 const std::string& atom,
413 const std::string& nextheavy)
415 /* Return the dummy mass name if found, or NULL if not set in ddb database */
416 const auto found = std::find_if(
417 vsiteconflist.begin(), vsiteconflist.end(), [&atom, &nextheavy](const auto& entry) {
418 return (gmx::equalCaseInsensitive(atom, entry.atomtype)
419 && gmx::equalCaseInsensitive(nextheavy, entry.nextHeavyType));
421 if (found != vsiteconflist.end())
423 return found->dummyMass;
432 static real get_ddb_bond(gmx::ArrayRef<const VirtualSiteTopology> vsitetop,
433 const std::string& res,
434 const std::string& atom1,
435 const std::string& atom2)
437 const auto found = std::find_if(vsitetop.begin(), vsitetop.end(), [&res](const auto& entry) {
438 return gmx::equalCaseInsensitive(res, entry.resname);
441 if (found == vsitetop.end())
443 gmx_fatal(FARGS, "No vsite information for residue %s found in vsite database.\n", res.c_str());
445 const auto foundBond =
446 std::find_if(found->bond.begin(), found->bond.end(), [&atom1, &atom2](const auto& entry) {
447 return ((atom1 == entry.atom1 && atom2 == entry.atom2)
448 || (atom1 == entry.atom2 && atom2 == entry.atom1));
450 if (foundBond == found->bond.end())
452 gmx_fatal(FARGS, "Couldnt find bond %s-%s for residue %s in vsite database.\n",
453 atom1.c_str(), atom2.c_str(), res.c_str());
456 return foundBond->value;
460 static real get_ddb_angle(gmx::ArrayRef<const VirtualSiteTopology> vsitetop,
461 const std::string& res,
462 const std::string& atom1,
463 const std::string& atom2,
464 const std::string& atom3)
466 const auto found = std::find_if(vsitetop.begin(), vsitetop.end(), [&res](const auto& entry) {
467 return gmx::equalCaseInsensitive(res, entry.resname);
470 if (found == vsitetop.end())
472 gmx_fatal(FARGS, "No vsite information for residue %s found in vsite database.\n", res.c_str());
474 const auto foundAngle = std::find_if(
475 found->angle.begin(), found->angle.end(), [&atom1, &atom2, &atom3](const auto& entry) {
476 return ((atom1 == entry.atom1 && atom2 == entry.atom2 && atom3 == entry.atom3)
477 || (atom1 == entry.atom3 && atom2 == entry.atom2 && atom3 == entry.atom1)
478 || (atom1 == entry.atom2 && atom2 == entry.atom1 && atom3 == entry.atom3)
479 || (atom1 == entry.atom3 && atom2 == entry.atom1 && atom3 == entry.atom2));
482 if (foundAngle == found->angle.end())
484 gmx_fatal(FARGS, "Couldnt find angle %s-%s-%s for residue %s in vsite database.\n",
485 atom1.c_str(), atom2.c_str(), atom3.c_str(), res.c_str());
488 return foundAngle->value;
492 static void count_bonds(int atom,
493 InteractionsOfType* psb,
502 int heavy, other, nrb, nrH, nrhv;
504 /* find heavy atom bound to this hydrogen */
506 for (auto parm = psb->interactionTypes.begin();
507 (parm != psb->interactionTypes.end()) && (heavy == NOTSET); parm++)
509 if (parm->ai() == atom)
513 else if (parm->aj() == atom)
520 gmx_fatal(FARGS, "unbound hydrogen atom %d", atom + 1);
522 /* find all atoms bound to heavy atom */
527 for (const auto& parm : psb->interactionTypes)
529 if (parm.ai() == heavy)
533 else if (parm.aj() == heavy)
540 if (is_hydrogen(*(atomname[other])))
547 heavies[nrhv] = other;
560 print_bonds(FILE* fp, int o2n[], int nrHatoms, const int Hatoms[], int Heavy, int nrheavies, const int heavies[])
564 fprintf(fp, "Found: %d Hatoms: ", nrHatoms);
565 for (i = 0; i < nrHatoms; i++)
567 fprintf(fp, " %d", o2n[Hatoms[i]] + 1);
569 fprintf(fp, "; %d Heavy atoms: %d", nrheavies + 1, o2n[Heavy] + 1);
570 for (i = 0; i < nrheavies; i++)
572 fprintf(fp, " %d", o2n[heavies[i]] + 1);
577 static int get_atype(int atom, t_atoms* at, gmx::ArrayRef<const PreprocessResidue> rtpFFDB, ResidueType* rt)
582 if (at->atom[atom].m != 0.0F)
584 type = at->atom[atom].type;
588 /* get type from rtpFFDB */
589 auto localPpResidue = getDatabaseEntry(*(at->resinfo[at->atom[atom].resind].name), rtpFFDB);
590 bNterm = rt->namedResidueHasType(*(at->resinfo[at->atom[atom].resind].name), "Protein")
591 && (at->atom[atom].resind == 0);
592 int j = search_jtype(*localPpResidue, *(at->atomname[atom]), bNterm);
593 type = localPpResidue->atom[j].type;
598 static int vsite_nm2type(const char* name, PreprocessingAtomTypes* atype)
602 tp = atype->atomTypeFromName(name);
605 gmx_fatal(FARGS, "Dummy mass type (%s) not found in atom type database", name);
611 static real get_amass(int atom, t_atoms* at, gmx::ArrayRef<const PreprocessResidue> rtpFFDB, ResidueType* rt)
616 if (at->atom[atom].m != 0.0F)
618 mass = at->atom[atom].m;
622 /* get mass from rtpFFDB */
623 auto localPpResidue = getDatabaseEntry(*(at->resinfo[at->atom[atom].resind].name), rtpFFDB);
624 bNterm = rt->namedResidueHasType(*(at->resinfo[at->atom[atom].resind].name), "Protein")
625 && (at->atom[atom].resind == 0);
626 int j = search_jtype(*localPpResidue, *(at->atomname[atom]), bNterm);
627 mass = localPpResidue->atom[j].m;
632 static void my_add_param(InteractionsOfType* plist, int ai, int aj, real b)
634 static real c[MAXFORCEPARAM] = { NOTSET, NOTSET, NOTSET, NOTSET, NOTSET, NOTSET };
637 add_param(plist, ai, aj, c, nullptr);
640 static void add_vsites(gmx::ArrayRef<InteractionsOfType> plist,
648 int other, moreheavy;
650 for (int i = 0; i < nrHatoms; i++)
652 int ftype = vsite_type[Hatoms[i]];
653 /* Errors in setting the vsite_type should really be caugth earlier,
654 * because here it's not possible to print any useful error message.
655 * But it's still better to print a message than to segfault.
659 gmx_incons("Undetected error in setting up virtual sites");
661 bool bSwapParity = (ftype < 0);
662 vsite_type[Hatoms[i]] = ftype = abs(ftype);
663 if (ftype == F_BONDS)
665 if ((nrheavies != 1) && (nrHatoms != 1))
668 "cannot make constraint in add_vsites for %d heavy "
669 "atoms and %d hydrogen atoms",
670 nrheavies, nrHatoms);
672 my_add_param(&(plist[F_CONSTRNC]), Hatoms[i], heavies[0], NOTSET);
683 gmx_fatal(FARGS, "Not enough heavy atoms (%d) for %s (min 3)",
684 nrheavies + 1, interaction_function[vsite_type[Hatoms[i]]].name);
686 add_vsite3_atoms(&plist[ftype], Hatoms[i], Heavy, heavies[0], heavies[1], bSwapParity);
692 moreheavy = heavies[1];
696 /* find more heavy atoms */
697 other = moreheavy = NOTSET;
698 for (auto parm = plist[F_BONDS].interactionTypes.begin();
699 (parm != plist[F_BONDS].interactionTypes.end()) && (moreheavy == NOTSET);
702 if (parm->ai() == heavies[0])
706 else if (parm->aj() == heavies[0])
710 if ((other != NOTSET) && (other != Heavy))
715 if (moreheavy == NOTSET)
717 gmx_fatal(FARGS, "Unbound molecule part %d-%d", Heavy + 1, Hatoms[0] + 1);
720 add_vsite3_atoms(&plist[ftype], Hatoms[i], Heavy, heavies[0], moreheavy, bSwapParity);
727 gmx_fatal(FARGS, "Not enough heavy atoms (%d) for %s (min 4)",
728 nrheavies + 1, interaction_function[vsite_type[Hatoms[i]]].name);
730 add_vsite4_atoms(&plist[ftype], Hatoms[0], Heavy, heavies[0], heavies[1], heavies[2]);
734 gmx_fatal(FARGS, "can't use add_vsites for interaction function %s",
735 interaction_function[vsite_type[Hatoms[i]]].name);
741 #define ANGLE_6RING (DEG2RAD * 120)
743 /* cosine rule: a^2 = b^2 + c^2 - 2 b c cos(alpha) */
744 /* get a^2 when a, b and alpha are given: */
745 #define cosrule(b, c, alpha) (gmx::square(b) + gmx::square(c) - 2 * (b) * (c)*std::cos(alpha))
746 /* get cos(alpha) when a, b and c are given: */
747 #define acosrule(a, b, c) ((gmx::square(b) + gmx::square(c) - gmx::square(a)) / (2 * (b) * (c)))
749 static int gen_vsites_6ring(t_atoms* at,
751 gmx::ArrayRef<InteractionsOfType> plist,
759 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
777 real a, b, dCGCE, tmp1, tmp2, mtot, mG, mrest;
779 /* CG, CE1 and CE2 stay and each get a part of the total mass,
780 * so the c-o-m stays the same.
787 gmx_incons("Generating vsites on 6-rings");
791 /* constraints between CG, CE1 and CE2: */
792 dCGCE = std::sqrt(cosrule(bond_cc, bond_cc, ANGLE_6RING));
793 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], ats[atCE1], dCGCE);
794 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], ats[atCE2], dCGCE);
795 my_add_param(&(plist[F_CONSTRNC]), ats[atCE1], ats[atCE2], dCGCE);
797 /* rest will be vsite3 */
800 for (i = 0; i < (bDoZ ? atNR : atHZ); i++)
802 mtot += at->atom[ats[i]].m;
803 if (i != atCG && i != atCE1 && i != atCE2 && (bDoZ || (i != atHZ && i != atCZ)))
805 at->atom[ats[i]].m = at->atom[ats[i]].mB = 0;
806 (*vsite_type)[ats[i]] = F_VSITE3;
810 /* Distribute mass so center-of-mass stays the same.
811 * The center-of-mass in the call is defined with x=0 at
812 * the CE1-CE2 bond and y=0 at the line from CG to the middle of CE1-CE2 bond.
814 xCG = -bond_cc + bond_cc * std::cos(ANGLE_6RING);
816 mG = at->atom[ats[atCG]].m = at->atom[ats[atCG]].mB = xcom * mtot / xCG;
818 at->atom[ats[atCE1]].m = at->atom[ats[atCE1]].mB = at->atom[ats[atCE2]].m =
819 at->atom[ats[atCE2]].mB = mrest / 2;
821 /* vsite3 construction: r_d = r_i + a r_ij + b r_ik */
822 tmp1 = dCGCE * std::sin(ANGLE_6RING * 0.5);
823 tmp2 = bond_cc * std::cos(0.5 * ANGLE_6RING) + tmp1;
825 a = b = -bond_ch / tmp1;
827 add_vsite3_param(&plist[F_VSITE3], ats[atHE1], ats[atCE1], ats[atCE2], ats[atCG], a, b);
828 add_vsite3_param(&plist[F_VSITE3], ats[atHE2], ats[atCE2], ats[atCE1], ats[atCG], a, b);
829 /* CD1, CD2 and CZ: */
831 add_vsite3_param(&plist[F_VSITE3], ats[atCD1], ats[atCE2], ats[atCE1], ats[atCG], a, b);
832 add_vsite3_param(&plist[F_VSITE3], ats[atCD2], ats[atCE1], ats[atCE2], ats[atCG], a, b);
835 add_vsite3_param(&plist[F_VSITE3], ats[atCZ], ats[atCG], ats[atCE1], ats[atCE2], a, b);
837 /* HD1, HD2 and HZ: */
838 a = b = (bond_ch + tmp2) / tmp1;
839 add_vsite3_param(&plist[F_VSITE3], ats[atHD1], ats[atCE2], ats[atCE1], ats[atCG], a, b);
840 add_vsite3_param(&plist[F_VSITE3], ats[atHD2], ats[atCE1], ats[atCE2], ats[atCG], a, b);
843 add_vsite3_param(&plist[F_VSITE3], ats[atHZ], ats[atCG], ats[atCE1], ats[atCE2], a, b);
849 static int gen_vsites_phe(t_atoms* at,
851 gmx::ArrayRef<InteractionsOfType> plist,
854 gmx::ArrayRef<const VirtualSiteTopology> vsitetop)
856 real bond_cc, bond_ch;
859 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
876 /* Aromatic rings have 6-fold symmetry, so we only need one bond length.
877 * (angle is always 120 degrees).
879 bond_cc = get_ddb_bond(vsitetop, "PHE", "CD1", "CE1");
880 bond_ch = get_ddb_bond(vsitetop, "PHE", "CD1", "HD1");
882 x[atCG] = -bond_cc + bond_cc * std::cos(ANGLE_6RING);
884 x[atHD1] = x[atCD1] + bond_ch * std::cos(ANGLE_6RING);
886 x[atHE1] = x[atCE1] - bond_ch * std::cos(ANGLE_6RING);
891 x[atCZ] = bond_cc * std::cos(0.5 * ANGLE_6RING);
892 x[atHZ] = x[atCZ] + bond_ch;
895 for (i = 0; i < atNR; i++)
897 xcom += x[i] * at->atom[ats[i]].m;
898 mtot += at->atom[ats[i]].m;
902 return gen_vsites_6ring(at, vsite_type, plist, nrfound, ats, bond_cc, bond_ch, xcom, TRUE);
906 calc_vsite3_param(real xd, real yd, real xi, real yi, real xj, real yj, real xk, real yk, real* a, real* b)
908 /* determine parameters by solving the equation system, since we know the
909 * virtual site coordinates here.
911 real dx_ij, dx_ik, dy_ij, dy_ik;
918 *a = ((xd - xi) * dy_ik - dx_ik * (yd - yi)) / (dx_ij * dy_ik - dx_ik * dy_ij);
919 *b = (yd - yi - (*a) * dy_ij) / dy_ik;
923 static int gen_vsites_trp(PreprocessingAtomTypes* atype,
924 std::vector<gmx::RVec>* newx,
926 char*** newatomname[],
928 int* newvsite_type[],
932 gmx::ArrayRef<const gmx::RVec> x,
936 gmx::ArrayRef<InteractionsOfType> plist,
940 gmx::ArrayRef<const VirtualSiteTopology> vsitetop)
943 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
964 /* weights for determining the COM's of both rings (M1 and M2): */
965 real mw[NMASS][atNR] = { { 0, 1, 1, 1, 0.5, 1, 1, 0.5, 0, 0, 0, 0, 0, 0, 0, 0 },
966 { 0, 0, 0, 0, 0.5, 0, 0, 0.5, 1, 1, 1, 1, 1, 1, 1, 1 } };
968 real xi[atNR], yi[atNR];
969 real xcom[NMASS], ycom[NMASS], alpha;
970 real b_CD2_CE2, b_NE1_CE2, b_CG_CD2, b_CH2_HH2, b_CE2_CZ2;
971 real b_NE1_HE1, b_CD2_CE3, b_CE3_CZ3, b_CB_CG;
972 real b_CZ2_CH2, b_CZ2_HZ2, b_CD1_HD1, b_CE3_HE3;
973 real b_CG_CD1, b_CZ3_HZ3;
974 real a_NE1_CE2_CD2, a_CE2_CD2_CG, a_CB_CG_CD2, a_CE2_CD2_CE3;
975 real a_CD2_CG_CD1, a_CE2_CZ2_HZ2, a_CZ2_CH2_HH2;
976 real a_CD2_CE2_CZ2, a_CD2_CE3_CZ3, a_CE3_CZ3_HZ3, a_CG_CD1_HD1;
977 real a_CE2_CZ2_CH2, a_HE1_NE1_CE2, a_CD2_CE3_HE3;
978 int atM[NMASS], tpM, i, i0, j, nvsite;
979 real mM[NMASS], dCBM1, dCBM2, dM1M2;
981 rvec r_ij, r_ik, t1, t2;
986 gmx_incons("atom types in gen_vsites_trp");
988 /* Get geometry from database */
989 b_CD2_CE2 = get_ddb_bond(vsitetop, "TRP", "CD2", "CE2");
990 b_NE1_CE2 = get_ddb_bond(vsitetop, "TRP", "NE1", "CE2");
991 b_CG_CD1 = get_ddb_bond(vsitetop, "TRP", "CG", "CD1");
992 b_CG_CD2 = get_ddb_bond(vsitetop, "TRP", "CG", "CD2");
993 b_CB_CG = get_ddb_bond(vsitetop, "TRP", "CB", "CG");
994 b_CE2_CZ2 = get_ddb_bond(vsitetop, "TRP", "CE2", "CZ2");
995 b_CD2_CE3 = get_ddb_bond(vsitetop, "TRP", "CD2", "CE3");
996 b_CE3_CZ3 = get_ddb_bond(vsitetop, "TRP", "CE3", "CZ3");
997 b_CZ2_CH2 = get_ddb_bond(vsitetop, "TRP", "CZ2", "CH2");
999 b_CD1_HD1 = get_ddb_bond(vsitetop, "TRP", "CD1", "HD1");
1000 b_CZ2_HZ2 = get_ddb_bond(vsitetop, "TRP", "CZ2", "HZ2");
1001 b_NE1_HE1 = get_ddb_bond(vsitetop, "TRP", "NE1", "HE1");
1002 b_CH2_HH2 = get_ddb_bond(vsitetop, "TRP", "CH2", "HH2");
1003 b_CE3_HE3 = get_ddb_bond(vsitetop, "TRP", "CE3", "HE3");
1004 b_CZ3_HZ3 = get_ddb_bond(vsitetop, "TRP", "CZ3", "HZ3");
1006 a_NE1_CE2_CD2 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "NE1", "CE2", "CD2");
1007 a_CE2_CD2_CG = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CE2", "CD2", "CG");
1008 a_CB_CG_CD2 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CB", "CG", "CD2");
1009 a_CD2_CG_CD1 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CD2", "CG", "CD1");
1011 a_CE2_CD2_CE3 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CE2", "CD2", "CE3");
1012 a_CD2_CE2_CZ2 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CD2", "CE2", "CZ2");
1013 a_CD2_CE3_CZ3 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CD2", "CE3", "CZ3");
1014 a_CE3_CZ3_HZ3 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CE3", "CZ3", "HZ3");
1015 a_CZ2_CH2_HH2 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CZ2", "CH2", "HH2");
1016 a_CE2_CZ2_HZ2 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CE2", "CZ2", "HZ2");
1017 a_CE2_CZ2_CH2 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CE2", "CZ2", "CH2");
1018 a_CG_CD1_HD1 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CG", "CD1", "HD1");
1019 a_HE1_NE1_CE2 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "HE1", "NE1", "CE2");
1020 a_CD2_CE3_HE3 = DEG2RAD * get_ddb_angle(vsitetop, "TRP", "CD2", "CE3", "HE3");
1022 /* Calculate local coordinates.
1023 * y-axis (x=0) is the bond CD2-CE2.
1024 * x-axis (y=0) is perpendicular to the bond CD2-CE2 and
1025 * intersects the middle of the bond.
1028 yi[atCD2] = -0.5 * b_CD2_CE2;
1031 yi[atCE2] = 0.5 * b_CD2_CE2;
1033 xi[atNE1] = -b_NE1_CE2 * std::sin(a_NE1_CE2_CD2);
1034 yi[atNE1] = yi[atCE2] - b_NE1_CE2 * std::cos(a_NE1_CE2_CD2);
1036 xi[atCG] = -b_CG_CD2 * std::sin(a_CE2_CD2_CG);
1037 yi[atCG] = yi[atCD2] + b_CG_CD2 * std::cos(a_CE2_CD2_CG);
1039 alpha = a_CE2_CD2_CG + M_PI - a_CB_CG_CD2;
1040 xi[atCB] = xi[atCG] - b_CB_CG * std::sin(alpha);
1041 yi[atCB] = yi[atCG] + b_CB_CG * std::cos(alpha);
1043 alpha = a_CE2_CD2_CG + a_CD2_CG_CD1 - M_PI;
1044 xi[atCD1] = xi[atCG] - b_CG_CD1 * std::sin(alpha);
1045 yi[atCD1] = yi[atCG] + b_CG_CD1 * std::cos(alpha);
1047 xi[atCE3] = b_CD2_CE3 * std::sin(a_CE2_CD2_CE3);
1048 yi[atCE3] = yi[atCD2] + b_CD2_CE3 * std::cos(a_CE2_CD2_CE3);
1050 xi[atCZ2] = b_CE2_CZ2 * std::sin(a_CD2_CE2_CZ2);
1051 yi[atCZ2] = yi[atCE2] - b_CE2_CZ2 * std::cos(a_CD2_CE2_CZ2);
1053 alpha = a_CE2_CD2_CE3 + a_CD2_CE3_CZ3 - M_PI;
1054 xi[atCZ3] = xi[atCE3] + b_CE3_CZ3 * std::sin(alpha);
1055 yi[atCZ3] = yi[atCE3] + b_CE3_CZ3 * std::cos(alpha);
1057 alpha = a_CD2_CE2_CZ2 + a_CE2_CZ2_CH2 - M_PI;
1058 xi[atCH2] = xi[atCZ2] + b_CZ2_CH2 * std::sin(alpha);
1059 yi[atCH2] = yi[atCZ2] - b_CZ2_CH2 * std::cos(alpha);
1062 alpha = a_CE2_CD2_CG + a_CD2_CG_CD1 - a_CG_CD1_HD1;
1063 xi[atHD1] = xi[atCD1] - b_CD1_HD1 * std::sin(alpha);
1064 yi[atHD1] = yi[atCD1] + b_CD1_HD1 * std::cos(alpha);
1066 alpha = a_NE1_CE2_CD2 + M_PI - a_HE1_NE1_CE2;
1067 xi[atHE1] = xi[atNE1] - b_NE1_HE1 * std::sin(alpha);
1068 yi[atHE1] = yi[atNE1] - b_NE1_HE1 * std::cos(alpha);
1070 alpha = a_CE2_CD2_CE3 + M_PI - a_CD2_CE3_HE3;
1071 xi[atHE3] = xi[atCE3] + b_CE3_HE3 * std::sin(alpha);
1072 yi[atHE3] = yi[atCE3] + b_CE3_HE3 * std::cos(alpha);
1074 alpha = a_CD2_CE2_CZ2 + M_PI - a_CE2_CZ2_HZ2;
1075 xi[atHZ2] = xi[atCZ2] + b_CZ2_HZ2 * std::sin(alpha);
1076 yi[atHZ2] = yi[atCZ2] - b_CZ2_HZ2 * std::cos(alpha);
1078 alpha = a_CD2_CE2_CZ2 + a_CE2_CZ2_CH2 - a_CZ2_CH2_HH2;
1079 xi[atHZ3] = xi[atCZ3] + b_CZ3_HZ3 * std::sin(alpha);
1080 yi[atHZ3] = yi[atCZ3] + b_CZ3_HZ3 * std::cos(alpha);
1082 alpha = a_CE2_CD2_CE3 + a_CD2_CE3_CZ3 - a_CE3_CZ3_HZ3;
1083 xi[atHH2] = xi[atCH2] + b_CH2_HH2 * std::sin(alpha);
1084 yi[atHH2] = yi[atCH2] - b_CH2_HH2 * std::cos(alpha);
1086 /* Calculate masses for each ring and put it on the dummy masses */
1087 for (j = 0; j < NMASS; j++)
1089 mM[j] = xcom[j] = ycom[j] = 0;
1091 for (i = 0; i < atNR; i++)
1095 for (j = 0; j < NMASS; j++)
1097 mM[j] += mw[j][i] * at->atom[ats[i]].m;
1098 xcom[j] += xi[i] * mw[j][i] * at->atom[ats[i]].m;
1099 ycom[j] += yi[i] * mw[j][i] * at->atom[ats[i]].m;
1103 for (j = 0; j < NMASS; j++)
1109 /* get dummy mass type */
1110 tpM = vsite_nm2type("MW", atype);
1111 /* make space for 2 masses: shift all atoms starting with CB */
1113 for (j = 0; j < NMASS; j++)
1115 atM[j] = i0 + *nadd + j;
1119 fprintf(stderr, "Inserting %d dummy masses at %d\n", NMASS, (*o2n)[i0] + 1);
1122 for (j = i0; j < at->nr; j++)
1124 (*o2n)[j] = j + *nadd;
1126 newx->resize(at->nr + *nadd);
1127 srenew(*newatom, at->nr + *nadd);
1128 srenew(*newatomname, at->nr + *nadd);
1129 srenew(*newvsite_type, at->nr + *nadd);
1130 srenew(*newcgnr, at->nr + *nadd);
1131 for (j = 0; j < NMASS; j++)
1133 (*newatomname)[at->nr + *nadd - 1 - j] = nullptr;
1136 /* Dummy masses will be placed at the center-of-mass in each ring. */
1138 /* calc initial position for dummy masses in real (non-local) coordinates.
1139 * Cheat by using the routine to calculate virtual site parameters. It is
1140 * much easier when we have the coordinates expressed in terms of
1143 rvec_sub(x[ats[atCB]], x[ats[atCG]], r_ij);
1144 rvec_sub(x[ats[atCD2]], x[ats[atCG]], r_ik);
1145 calc_vsite3_param(xcom[0], ycom[0], xi[atCG], yi[atCG], xi[atCB], yi[atCB], xi[atCD2],
1149 rvec_add(t1, t2, t1);
1150 rvec_add(t1, x[ats[atCG]], (*newx)[atM[0]]);
1152 calc_vsite3_param(xcom[1], ycom[1], xi[atCG], yi[atCG], xi[atCB], yi[atCB], xi[atCD2],
1156 rvec_add(t1, t2, t1);
1157 rvec_add(t1, x[ats[atCG]], (*newx)[atM[1]]);
1159 /* set parameters for the masses */
1160 for (j = 0; j < NMASS; j++)
1162 sprintf(name, "MW%d", j + 1);
1163 (*newatomname)[atM[j]] = put_symtab(symtab, name);
1164 (*newatom)[atM[j]].m = (*newatom)[atM[j]].mB = mM[j];
1165 (*newatom)[atM[j]].q = (*newatom)[atM[j]].qB = 0.0;
1166 (*newatom)[atM[j]].type = (*newatom)[atM[j]].typeB = tpM;
1167 (*newatom)[atM[j]].ptype = eptAtom;
1168 (*newatom)[atM[j]].resind = at->atom[i0].resind;
1169 (*newatom)[atM[j]].elem[0] = 'M';
1170 (*newatom)[atM[j]].elem[1] = '\0';
1171 (*newvsite_type)[atM[j]] = NOTSET;
1172 (*newcgnr)[atM[j]] = (*cgnr)[i0];
1174 /* renumber cgnr: */
1175 for (i = i0; i < at->nr; i++)
1180 /* constraints between CB, M1 and M2 */
1181 /* 'add_shift' says which atoms won't be renumbered afterwards */
1182 dCBM1 = std::hypot(xcom[0] - xi[atCB], ycom[0] - yi[atCB]);
1183 dM1M2 = std::hypot(xcom[0] - xcom[1], ycom[0] - ycom[1]);
1184 dCBM2 = std::hypot(xcom[1] - xi[atCB], ycom[1] - yi[atCB]);
1185 my_add_param(&(plist[F_CONSTRNC]), ats[atCB], add_shift + atM[0], dCBM1);
1186 my_add_param(&(plist[F_CONSTRNC]), ats[atCB], add_shift + atM[1], dCBM2);
1187 my_add_param(&(plist[F_CONSTRNC]), add_shift + atM[0], add_shift + atM[1], dM1M2);
1189 /* rest will be vsite3 */
1191 for (i = 0; i < atNR; i++)
1195 at->atom[ats[i]].m = at->atom[ats[i]].mB = 0;
1196 (*vsite_type)[ats[i]] = F_VSITE3;
1201 /* now define all vsites from M1, M2, CB, ie:
1202 r_d = r_M1 + a r_M1_M2 + b r_M1_CB */
1203 for (i = 0; i < atNR; i++)
1205 if ((*vsite_type)[ats[i]] == F_VSITE3)
1207 calc_vsite3_param(xi[i], yi[i], xcom[0], ycom[0], xcom[1], ycom[1], xi[atCB], yi[atCB],
1209 add_vsite3_param(&plist[F_VSITE3], ats[i], add_shift + atM[0], add_shift + atM[1],
1218 static int gen_vsites_tyr(PreprocessingAtomTypes* atype,
1219 std::vector<gmx::RVec>* newx,
1221 char*** newatomname[],
1223 int* newvsite_type[],
1227 gmx::ArrayRef<const gmx::RVec> x,
1231 gmx::ArrayRef<InteractionsOfType> plist,
1235 gmx::ArrayRef<const VirtualSiteTopology> vsitetop)
1237 int nvsite, i, i0, j, atM, tpM;
1238 real dCGCE, dCEOH, dCGM, tmp1, a, b;
1239 real bond_cc, bond_ch, bond_co, bond_oh, angle_coh;
1245 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
1263 /* CG, CE1, CE2 (as in general 6-ring) and OH and HH stay,
1264 rest gets virtualized.
1265 Now we have two linked triangles with one improper keeping them flat */
1266 if (atNR != nrfound)
1268 gmx_incons("Number of atom types in gen_vsites_tyr");
1271 /* Aromatic rings have 6-fold symmetry, so we only need one bond length
1272 * for the ring part (angle is always 120 degrees).
1274 bond_cc = get_ddb_bond(vsitetop, "TYR", "CD1", "CE1");
1275 bond_ch = get_ddb_bond(vsitetop, "TYR", "CD1", "HD1");
1276 bond_co = get_ddb_bond(vsitetop, "TYR", "CZ", "OH");
1277 bond_oh = get_ddb_bond(vsitetop, "TYR", "OH", "HH");
1278 angle_coh = DEG2RAD * get_ddb_angle(vsitetop, "TYR", "CZ", "OH", "HH");
1280 xi[atCG] = -bond_cc + bond_cc * std::cos(ANGLE_6RING);
1281 xi[atCD1] = -bond_cc;
1282 xi[atHD1] = xi[atCD1] + bond_ch * std::cos(ANGLE_6RING);
1284 xi[atHE1] = xi[atCE1] - bond_ch * std::cos(ANGLE_6RING);
1285 xi[atCD2] = xi[atCD1];
1286 xi[atHD2] = xi[atHD1];
1287 xi[atCE2] = xi[atCE1];
1288 xi[atHE2] = xi[atHE1];
1289 xi[atCZ] = bond_cc * std::cos(0.5 * ANGLE_6RING);
1290 xi[atOH] = xi[atCZ] + bond_co;
1293 for (i = 0; i < atOH; i++)
1295 xcom += xi[i] * at->atom[ats[i]].m;
1296 mtot += at->atom[ats[i]].m;
1300 /* first do 6 ring as default,
1301 except CZ (we'll do that different) and HZ (we don't have that): */
1302 nvsite = gen_vsites_6ring(at, vsite_type, plist, nrfound, ats, bond_cc, bond_ch, xcom, FALSE);
1304 /* then construct CZ from the 2nd triangle */
1305 /* vsite3 construction: r_d = r_i + a r_ij + b r_ik */
1306 a = b = 0.5 * bond_co / (bond_co - bond_cc * std::cos(ANGLE_6RING));
1307 add_vsite3_param(&plist[F_VSITE3], ats[atCZ], ats[atOH], ats[atCE1], ats[atCE2], a, b);
1308 at->atom[ats[atCZ]].m = at->atom[ats[atCZ]].mB = 0;
1310 /* constraints between CE1, CE2 and OH */
1311 dCGCE = std::sqrt(cosrule(bond_cc, bond_cc, ANGLE_6RING));
1312 dCEOH = std::sqrt(cosrule(bond_cc, bond_co, ANGLE_6RING));
1313 my_add_param(&(plist[F_CONSTRNC]), ats[atCE1], ats[atOH], dCEOH);
1314 my_add_param(&(plist[F_CONSTRNC]), ats[atCE2], ats[atOH], dCEOH);
1316 /* We also want to constrain the angle C-O-H, but since CZ is constructed
1317 * we need to introduce a constraint to CG.
1318 * CG is much further away, so that will lead to instabilities in LINCS
1319 * when we constrain both CG-HH and OH-HH distances. Instead of requiring
1320 * the use of lincs_order=8 we introduce a dummy mass three times further
1321 * away from OH than HH. The mass is accordingly a third, with the remaining
1322 * 2/3 moved to OH. This shouldn't cause any problems since the forces will
1323 * apply to the HH constructed atom and not directly on the virtual mass.
1326 vdist = 2.0 * bond_oh;
1327 mM = at->atom[ats[atHH]].m / 2.0;
1328 at->atom[ats[atOH]].m += mM; /* add 1/2 of original H mass */
1329 at->atom[ats[atOH]].mB += mM; /* add 1/2 of original H mass */
1330 at->atom[ats[atHH]].m = at->atom[ats[atHH]].mB = 0;
1332 /* get dummy mass type */
1333 tpM = vsite_nm2type("MW", atype);
1334 /* make space for 1 mass: shift HH only */
1339 fprintf(stderr, "Inserting 1 dummy mass at %d\n", (*o2n)[i0] + 1);
1342 for (j = i0; j < at->nr; j++)
1344 (*o2n)[j] = j + *nadd;
1346 newx->resize(at->nr + *nadd);
1347 srenew(*newatom, at->nr + *nadd);
1348 srenew(*newatomname, at->nr + *nadd);
1349 srenew(*newvsite_type, at->nr + *nadd);
1350 srenew(*newcgnr, at->nr + *nadd);
1351 (*newatomname)[at->nr + *nadd - 1] = nullptr;
1353 /* Calc the dummy mass initial position */
1354 rvec_sub(x[ats[atHH]], x[ats[atOH]], r1);
1356 rvec_add(r1, x[ats[atHH]], (*newx)[atM]);
1358 strcpy(name, "MW1");
1359 (*newatomname)[atM] = put_symtab(symtab, name);
1360 (*newatom)[atM].m = (*newatom)[atM].mB = mM;
1361 (*newatom)[atM].q = (*newatom)[atM].qB = 0.0;
1362 (*newatom)[atM].type = (*newatom)[atM].typeB = tpM;
1363 (*newatom)[atM].ptype = eptAtom;
1364 (*newatom)[atM].resind = at->atom[i0].resind;
1365 (*newatom)[atM].elem[0] = 'M';
1366 (*newatom)[atM].elem[1] = '\0';
1367 (*newvsite_type)[atM] = NOTSET;
1368 (*newcgnr)[atM] = (*cgnr)[i0];
1369 /* renumber cgnr: */
1370 for (i = i0; i < at->nr; i++)
1375 (*vsite_type)[ats[atHH]] = F_VSITE2;
1377 /* assume we also want the COH angle constrained: */
1378 tmp1 = bond_cc * std::cos(0.5 * ANGLE_6RING) + dCGCE * std::sin(ANGLE_6RING * 0.5) + bond_co;
1379 dCGM = std::sqrt(cosrule(tmp1, vdist, angle_coh));
1380 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], add_shift + atM, dCGM);
1381 my_add_param(&(plist[F_CONSTRNC]), ats[atOH], add_shift + atM, vdist);
1383 add_vsite2_param(&plist[F_VSITE2], ats[atHH], ats[atOH], add_shift + atM, 1.0 / 2.0);
1387 static int gen_vsites_his(t_atoms* at,
1389 gmx::ArrayRef<InteractionsOfType> plist,
1392 gmx::ArrayRef<const VirtualSiteTopology> vsitetop)
1395 real a, b, alpha, dCGCE1, dCGNE2;
1396 real sinalpha, cosalpha;
1397 real xcom, ycom, mtot;
1398 real mG, mrest, mCE1, mNE2;
1399 real b_CG_ND1, b_ND1_CE1, b_CE1_NE2, b_CG_CD2, b_CD2_NE2;
1400 real b_ND1_HD1, b_NE2_HE2, b_CE1_HE1, b_CD2_HD2;
1401 real a_CG_ND1_CE1, a_CG_CD2_NE2, a_ND1_CE1_NE2, a_CE1_NE2_CD2;
1402 real a_NE2_CE1_HE1, a_NE2_CD2_HD2, a_CE1_ND1_HD1, a_CE1_NE2_HE2;
1405 /* these MUST correspond to the atnms array in do_vsite_aromatics! */
1419 real x[atNR], y[atNR];
1421 /* CG, CE1 and NE2 stay, each gets part of the total mass,
1422 rest gets virtualized */
1423 /* check number of atoms, 3 hydrogens may be missing: */
1424 /* assert( nrfound >= atNR-3 || nrfound <= atNR );
1425 * Don't understand the above logic. Shouldn't it be && rather than || ???
1427 if ((nrfound < atNR - 3) || (nrfound > atNR))
1429 gmx_incons("Generating vsites for HIS");
1432 /* avoid warnings about uninitialized variables */
1433 b_ND1_HD1 = b_NE2_HE2 = b_CE1_HE1 = b_CD2_HD2 = a_NE2_CE1_HE1 = a_NE2_CD2_HD2 = a_CE1_ND1_HD1 =
1436 if (ats[atHD1] != NOTSET)
1438 if (ats[atHE2] != NOTSET)
1440 sprintf(resname, "HISH");
1444 sprintf(resname, "HISA");
1449 sprintf(resname, "HISB");
1452 /* Get geometry from database */
1453 b_CG_ND1 = get_ddb_bond(vsitetop, resname, "CG", "ND1");
1454 b_ND1_CE1 = get_ddb_bond(vsitetop, resname, "ND1", "CE1");
1455 b_CE1_NE2 = get_ddb_bond(vsitetop, resname, "CE1", "NE2");
1456 b_CG_CD2 = get_ddb_bond(vsitetop, resname, "CG", "CD2");
1457 b_CD2_NE2 = get_ddb_bond(vsitetop, resname, "CD2", "NE2");
1458 a_CG_ND1_CE1 = DEG2RAD * get_ddb_angle(vsitetop, resname, "CG", "ND1", "CE1");
1459 a_CG_CD2_NE2 = DEG2RAD * get_ddb_angle(vsitetop, resname, "CG", "CD2", "NE2");
1460 a_ND1_CE1_NE2 = DEG2RAD * get_ddb_angle(vsitetop, resname, "ND1", "CE1", "NE2");
1461 a_CE1_NE2_CD2 = DEG2RAD * get_ddb_angle(vsitetop, resname, "CE1", "NE2", "CD2");
1463 if (ats[atHD1] != NOTSET)
1465 b_ND1_HD1 = get_ddb_bond(vsitetop, resname, "ND1", "HD1");
1466 a_CE1_ND1_HD1 = DEG2RAD * get_ddb_angle(vsitetop, resname, "CE1", "ND1", "HD1");
1468 if (ats[atHE2] != NOTSET)
1470 b_NE2_HE2 = get_ddb_bond(vsitetop, resname, "NE2", "HE2");
1471 a_CE1_NE2_HE2 = DEG2RAD * get_ddb_angle(vsitetop, resname, "CE1", "NE2", "HE2");
1473 if (ats[atHD2] != NOTSET)
1475 b_CD2_HD2 = get_ddb_bond(vsitetop, resname, "CD2", "HD2");
1476 a_NE2_CD2_HD2 = DEG2RAD * get_ddb_angle(vsitetop, resname, "NE2", "CD2", "HD2");
1478 if (ats[atHE1] != NOTSET)
1480 b_CE1_HE1 = get_ddb_bond(vsitetop, resname, "CE1", "HE1");
1481 a_NE2_CE1_HE1 = DEG2RAD * get_ddb_angle(vsitetop, resname, "NE2", "CE1", "HE1");
1484 /* constraints between CG, CE1 and NE1 */
1485 dCGCE1 = std::sqrt(cosrule(b_CG_ND1, b_ND1_CE1, a_CG_ND1_CE1));
1486 dCGNE2 = std::sqrt(cosrule(b_CG_CD2, b_CD2_NE2, a_CG_CD2_NE2));
1488 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], ats[atCE1], dCGCE1);
1489 my_add_param(&(plist[F_CONSTRNC]), ats[atCG], ats[atNE2], dCGNE2);
1490 /* we already have a constraint CE1-NE2, so we don't add it again */
1492 /* calculate the positions in a local frame of reference.
1493 * The x-axis is the line from CG that makes a right angle
1494 * with the bond CE1-NE2, and the y-axis the bond CE1-NE2.
1496 /* First calculate the x-axis intersection with y-axis (=yCE1).
1497 * Get cos(angle CG-CE1-NE2) :
1499 cosalpha = acosrule(dCGNE2, dCGCE1, b_CE1_NE2);
1501 y[atCE1] = cosalpha * dCGCE1;
1503 y[atNE2] = y[atCE1] - b_CE1_NE2;
1504 sinalpha = std::sqrt(1 - cosalpha * cosalpha);
1505 x[atCG] = -sinalpha * dCGCE1;
1507 x[atHE1] = x[atHE2] = x[atHD1] = x[atHD2] = 0;
1508 y[atHE1] = y[atHE2] = y[atHD1] = y[atHD2] = 0;
1510 /* calculate ND1 and CD2 positions from CE1 and NE2 */
1512 x[atND1] = -b_ND1_CE1 * std::sin(a_ND1_CE1_NE2);
1513 y[atND1] = y[atCE1] - b_ND1_CE1 * std::cos(a_ND1_CE1_NE2);
1515 x[atCD2] = -b_CD2_NE2 * std::sin(a_CE1_NE2_CD2);
1516 y[atCD2] = y[atNE2] + b_CD2_NE2 * std::cos(a_CE1_NE2_CD2);
1518 /* And finally the hydrogen positions */
1519 if (ats[atHE1] != NOTSET)
1521 x[atHE1] = x[atCE1] + b_CE1_HE1 * std::sin(a_NE2_CE1_HE1);
1522 y[atHE1] = y[atCE1] - b_CE1_HE1 * std::cos(a_NE2_CE1_HE1);
1524 /* HD2 - first get (ccw) angle from (positive) y-axis */
1525 if (ats[atHD2] != NOTSET)
1527 alpha = a_CE1_NE2_CD2 + M_PI - a_NE2_CD2_HD2;
1528 x[atHD2] = x[atCD2] - b_CD2_HD2 * std::sin(alpha);
1529 y[atHD2] = y[atCD2] + b_CD2_HD2 * std::cos(alpha);
1531 if (ats[atHD1] != NOTSET)
1533 /* HD1 - first get (cw) angle from (positive) y-axis */
1534 alpha = a_ND1_CE1_NE2 + M_PI - a_CE1_ND1_HD1;
1535 x[atHD1] = x[atND1] - b_ND1_HD1 * std::sin(alpha);
1536 y[atHD1] = y[atND1] - b_ND1_HD1 * std::cos(alpha);
1538 if (ats[atHE2] != NOTSET)
1540 x[atHE2] = x[atNE2] + b_NE2_HE2 * std::sin(a_CE1_NE2_HE2);
1541 y[atHE2] = y[atNE2] + b_NE2_HE2 * std::cos(a_CE1_NE2_HE2);
1543 /* Have all coordinates now */
1545 /* calc center-of-mass; keep atoms CG, CE1, NE2 and
1546 * set the rest to vsite3
1548 mtot = xcom = ycom = 0;
1550 for (i = 0; i < atNR; i++)
1552 if (ats[i] != NOTSET)
1554 mtot += at->atom[ats[i]].m;
1555 xcom += x[i] * at->atom[ats[i]].m;
1556 ycom += y[i] * at->atom[ats[i]].m;
1557 if (i != atCG && i != atCE1 && i != atNE2)
1559 at->atom[ats[i]].m = at->atom[ats[i]].mB = 0;
1560 (*vsite_type)[ats[i]] = F_VSITE3;
1565 if (nvsite + 3 != nrfound)
1567 gmx_incons("Generating vsites for HIS");
1573 /* distribute mass so that com stays the same */
1574 mG = xcom * mtot / x[atCG];
1576 mCE1 = (ycom - y[atNE2]) * mrest / (y[atCE1] - y[atNE2]);
1577 mNE2 = mrest - mCE1;
1579 at->atom[ats[atCG]].m = at->atom[ats[atCG]].mB = mG;
1580 at->atom[ats[atCE1]].m = at->atom[ats[atCE1]].mB = mCE1;
1581 at->atom[ats[atNE2]].m = at->atom[ats[atNE2]].mB = mNE2;
1584 if (ats[atHE1] != NOTSET)
1586 calc_vsite3_param(x[atHE1], y[atHE1], x[atCE1], y[atCE1], x[atNE2], y[atNE2], x[atCG],
1588 add_vsite3_param(&plist[F_VSITE3], ats[atHE1], ats[atCE1], ats[atNE2], ats[atCG], a, b);
1591 if (ats[atHE2] != NOTSET)
1593 calc_vsite3_param(x[atHE2], y[atHE2], x[atNE2], y[atNE2], x[atCE1], y[atCE1], x[atCG],
1595 add_vsite3_param(&plist[F_VSITE3], ats[atHE2], ats[atNE2], ats[atCE1], ats[atCG], a, b);
1599 calc_vsite3_param(x[atND1], y[atND1], x[atNE2], y[atNE2], x[atCE1], y[atCE1], x[atCG], y[atCG],
1601 add_vsite3_param(&plist[F_VSITE3], ats[atND1], ats[atNE2], ats[atCE1], ats[atCG], a, b);
1604 calc_vsite3_param(x[atCD2], y[atCD2], x[atCE1], y[atCE1], x[atNE2], y[atNE2], x[atCG], y[atCG],
1606 add_vsite3_param(&plist[F_VSITE3], ats[atCD2], ats[atCE1], ats[atNE2], ats[atCG], a, b);
1609 if (ats[atHD1] != NOTSET)
1611 calc_vsite3_param(x[atHD1], y[atHD1], x[atNE2], y[atNE2], x[atCE1], y[atCE1], x[atCG],
1613 add_vsite3_param(&plist[F_VSITE3], ats[atHD1], ats[atNE2], ats[atCE1], ats[atCG], a, b);
1616 if (ats[atHD2] != NOTSET)
1618 calc_vsite3_param(x[atHD2], y[atHD2], x[atCE1], y[atCE1], x[atNE2], y[atNE2], x[atCG],
1620 add_vsite3_param(&plist[F_VSITE3], ats[atHD2], ats[atCE1], ats[atNE2], ats[atCG], a, b);
1625 static bool is_vsite(int vsite_type)
1627 if (vsite_type == NOTSET)
1631 switch (abs(vsite_type))
1638 case F_VSITE4FDN: return TRUE;
1639 default: return FALSE;
1643 static char atomnamesuffix[] = "1234";
1645 void do_vsites(gmx::ArrayRef<const PreprocessResidue> rtpFFDB,
1646 PreprocessingAtomTypes* atype,
1649 std::vector<gmx::RVec>* x,
1650 gmx::ArrayRef<InteractionsOfType> plist,
1654 bool bVsiteAromatics,
1657 #define MAXATOMSPERRESIDUE 16
1658 int k, m, i0, ni0, whatres, add_shift, nvsite, nadd;
1660 int nrfound = 0, needed, nrbonds, nrHatoms, Heavy, nrheavies, tpM, tpHeavy;
1661 int Hatoms[4], heavies[4];
1662 bool bWARNING, bAddVsiteParam, bFirstWater;
1664 real mHtot, mtot, fact, fact2;
1665 rvec rpar, rperp, temp;
1666 char tpname[32], nexttpname[32];
1667 int * o2n, *newvsite_type, *newcgnr, ats[MAXATOMSPERRESIDUE];
1669 char*** newatomname;
1671 bool isN, planarN, bFound;
1673 /* if bVsiteAromatics=TRUE do_vsites will specifically convert atoms in
1674 PHE, TRP, TYR and HIS to a construction of virtual sites */
1683 const char* resnms[resNR] = { "PHE", "TRP", "TYR", "HIS" };
1684 /* Amber03 alternative names for termini */
1685 const char* resnmsN[resNR] = { "NPHE", "NTRP", "NTYR", "NHIS" };
1686 const char* resnmsC[resNR] = { "CPHE", "CTRP", "CTYR", "CHIS" };
1687 /* HIS can be known as HISH, HIS1, HISA, HID, HIE, HIP, etc. too */
1688 bool bPartial[resNR] = { FALSE, FALSE, FALSE, TRUE };
1689 /* the atnms for every residue MUST correspond to the enums in the
1690 gen_vsites_* (one for each residue) routines! */
1691 /* also the atom names in atnms MUST be in the same order as in the .rtp! */
1692 const char* atnms[resNR][MAXATOMSPERRESIDUE + 1] = {
1694 "CD1", "HD1", "CD2", "HD2", "CE1", "HE1", "CE2", "HE2", "CZ", "HZ", nullptr },
1696 "CG", "CD1", "HD1", "CD2", "NE1", "HE1", "CE2", "CE3", "HE3", "CZ2", "HZ2", "CZ3", "HZ3",
1697 "CH2", "HH2", nullptr },
1699 "CD1", "HD1", "CD2", "HD2", "CE1", "HE1", "CE2", "HE2", "CZ", "OH", "HH", nullptr },
1701 "ND1", "HD1", "CD2", "HD2", "CE1", "HE1", "NE2", "HE2", nullptr }
1706 printf("Searching for atoms to make virtual sites ...\n");
1707 fprintf(debug, "# # # VSITES # # #\n");
1710 std::vector<std::string> db = fflib_search_file_end(ffdir, ".vsd", FALSE);
1712 /* Container of CH3/NH3/NH2 configuration entries.
1713 * See comments in read_vsite_database. It isnt beautiful,
1714 * but it had to be fixed, and I dont even want to try to
1715 * maintain this part of the code...
1717 std::vector<VirtualSiteConfiguration> vsiteconflist;
1719 // TODO those have been deprecated and should be removed completely.
1720 /* Container of geometry (bond/angle) entries for
1721 * residues like PHE, TRP, TYR, HIS, etc., where we need
1722 * to know the geometry to construct vsite aromatics.
1723 * Note that equilibrium geometry isnt necessarily the same
1724 * as the individual bond and angle values given in the
1725 * force field (rings can be strained).
1727 std::vector<VirtualSiteTopology> vsitetop;
1728 for (const auto& filename : db)
1730 read_vsite_database(filename.c_str(), &vsiteconflist, &vsitetop);
1736 /* we need a marker for which atoms should *not* be renumbered afterwards */
1737 add_shift = 10 * at->nr;
1738 /* make arrays where masses can be inserted into */
1739 std::vector<gmx::RVec> newx(at->nr);
1740 snew(newatom, at->nr);
1741 snew(newatomname, at->nr);
1742 snew(newvsite_type, at->nr);
1743 snew(newcgnr, at->nr);
1744 /* make index array to tell where the atoms go to when masses are inserted */
1746 for (int i = 0; i < at->nr; i++)
1750 /* make index to tell which residues were already processed */
1751 std::vector<bool> bResProcessed(at->nres);
1755 /* generate vsite constructions */
1756 /* loop over all atoms */
1758 for (int i = 0; (i < at->nr); i++)
1760 if (at->atom[i].resind != resind)
1762 resind = at->atom[i].resind;
1764 const char* resnm = *(at->resinfo[resind].name);
1765 /* first check for aromatics to virtualize */
1766 /* don't waste our effort on DNA, water etc. */
1767 /* Only do the vsite aromatic stuff when we reach the
1768 * CA atom, since there might be an X2/X3 group on the
1769 * N-terminus that must be treated first.
1771 if (bVsiteAromatics && (strcmp(*(at->atomname[i]), "CA") == 0) && !bResProcessed[resind]
1772 && rt.namedResidueHasType(*(at->resinfo[resind].name), "Protein"))
1774 /* mark this residue */
1775 bResProcessed[resind] = TRUE;
1776 /* find out if this residue needs converting */
1778 for (int j = 0; j < resNR && whatres == NOTSET; j++)
1781 cmplength = bPartial[j] ? strlen(resnm) - 1 : strlen(resnm);
1783 bFound = ((gmx::equalCaseInsensitive(resnm, resnms[j], cmplength))
1784 || (gmx::equalCaseInsensitive(resnm, resnmsN[j], cmplength))
1785 || (gmx::equalCaseInsensitive(resnm, resnmsC[j], cmplength)));
1790 /* get atoms we will be needing for the conversion */
1792 for (k = 0; atnms[j][k]; k++)
1795 for (m = i; m < at->nr && at->atom[m].resind == resind && ats[k] == NOTSET; m++)
1797 if (gmx_strcasecmp(*(at->atomname[m]), atnms[j][k]) == 0)
1805 /* now k is number of atom names in atnms[j] */
1814 if (nrfound < needed)
1817 "not enough atoms found (%d, need %d) in "
1818 "residue %s %d while\n "
1819 "generating aromatics virtual site construction",
1820 nrfound, needed, resnm, at->resinfo[resind].nr);
1822 /* Advance overall atom counter */
1826 /* the enums for every residue MUST correspond to atnms[residue] */
1832 fprintf(stderr, "PHE at %d\n", o2n[ats[0]] + 1);
1834 nvsite += gen_vsites_phe(at, vsite_type, plist, nrfound, ats, vsitetop);
1839 fprintf(stderr, "TRP at %d\n", o2n[ats[0]] + 1);
1841 nvsite += gen_vsites_trp(atype, &newx, &newatom, &newatomname, &o2n,
1842 &newvsite_type, &newcgnr, symtab, &nadd, *x, cgnr, at,
1843 vsite_type, plist, nrfound, ats, add_shift, vsitetop);
1848 fprintf(stderr, "TYR at %d\n", o2n[ats[0]] + 1);
1850 nvsite += gen_vsites_tyr(atype, &newx, &newatom, &newatomname, &o2n,
1851 &newvsite_type, &newcgnr, symtab, &nadd, *x, cgnr, at,
1852 vsite_type, plist, nrfound, ats, add_shift, vsitetop);
1857 fprintf(stderr, "HIS at %d\n", o2n[ats[0]] + 1);
1859 nvsite += gen_vsites_his(at, vsite_type, plist, nrfound, ats, vsitetop);
1862 /* this means this residue won't be processed */
1864 default: gmx_fatal(FARGS, "DEATH HORROR in do_vsites (%s:%d)", __FILE__, __LINE__);
1865 } /* switch whatres */
1866 /* skip back to beginning of residue */
1867 while (i > 0 && at->atom[i - 1].resind == resind)
1871 } /* if bVsiteAromatics & is protein */
1873 /* now process the rest of the hydrogens */
1874 /* only process hydrogen atoms which are not already set */
1875 if (((*vsite_type)[i] == NOTSET) && is_hydrogen(*(at->atomname[i])))
1877 /* find heavy atom, count #bonds from it and #H atoms bound to it
1878 and return H atom numbers (Hatoms) and heavy atom numbers (heavies) */
1879 count_bonds(i, &plist[F_BONDS], at->atomname, &nrbonds, &nrHatoms, Hatoms, &Heavy,
1880 &nrheavies, heavies);
1881 /* get Heavy atom type */
1882 tpHeavy = get_atype(Heavy, at, rtpFFDB, &rt);
1883 strcpy(tpname, atype->atomNameFromAtomType(tpHeavy));
1886 bAddVsiteParam = TRUE;
1887 /* nested if's which check nrHatoms, nrbonds and atomname */
1892 case 2: /* -O-H */ (*vsite_type)[i] = F_BONDS; break;
1893 case 3: /* =CH-, -NH- or =NH+- */ (*vsite_type)[i] = F_VSITE3FD; break;
1894 case 4: /* --CH- (tert) */
1895 /* The old type 4FD had stability issues, so
1896 * all new constructs should use 4FDN
1898 (*vsite_type)[i] = F_VSITE4FDN;
1900 /* Check parity of heavy atoms from coordinates */
1905 rvec_sub((*x)[aj], (*x)[ai], tmpmat[0]);
1906 rvec_sub((*x)[ak], (*x)[ai], tmpmat[1]);
1907 rvec_sub((*x)[al], (*x)[ai], tmpmat[2]);
1909 if (det(tmpmat) > 0)
1917 default: /* nrbonds != 2, 3 or 4 */ bWARNING = TRUE;
1920 else if ((nrHatoms == 2) && (nrbonds == 2) && (at->atom[Heavy].atomnumber == 8))
1922 bAddVsiteParam = FALSE; /* this is water: skip these hydrogens */
1925 bFirstWater = FALSE;
1928 fprintf(debug, "Not converting hydrogens in water to virtual sites\n");
1932 else if ((nrHatoms == 2) && (nrbonds == 4))
1934 /* -CH2- , -NH2+- */
1935 (*vsite_type)[Hatoms[0]] = F_VSITE3OUT;
1936 (*vsite_type)[Hatoms[1]] = -F_VSITE3OUT;
1940 /* 2 or 3 hydrogen atom, with 3 or 4 bonds in total to the heavy atom.
1941 * If it is a nitrogen, first check if it is planar.
1943 isN = planarN = FALSE;
1944 if ((nrHatoms == 2) && ((*at->atomname[Heavy])[0] == 'N'))
1947 int j = nitrogen_is_planar(vsiteconflist, tpname);
1950 gmx_fatal(FARGS, "No vsite database NH2 entry for type %s\n", tpname);
1954 if ((nrHatoms == 2) && (nrbonds == 3) && (!isN || planarN))
1956 /* =CH2 or, if it is a nitrogen NH2, it is a planar one */
1957 (*vsite_type)[Hatoms[0]] = F_VSITE3FAD;
1958 (*vsite_type)[Hatoms[1]] = -F_VSITE3FAD;
1960 else if (((nrHatoms == 2) && (nrbonds == 3) && (isN && !planarN))
1961 || ((nrHatoms == 3) && (nrbonds == 4)))
1963 /* CH3, NH3 or non-planar NH2 group */
1964 int Hat_vsite_type[3] = { F_VSITE3, F_VSITE3OUT, F_VSITE3OUT };
1965 bool Hat_SwapParity[3] = { FALSE, TRUE, FALSE };
1969 fprintf(stderr, "-XH3 or nonplanar NH2 group at %d\n", i + 1);
1971 bAddVsiteParam = FALSE; /* we'll do this ourselves! */
1972 /* -NH2 (umbrella), -NH3+ or -CH3 */
1973 (*vsite_type)[Heavy] = F_VSITE3;
1974 for (int j = 0; j < nrHatoms; j++)
1976 (*vsite_type)[Hatoms[j]] = Hat_vsite_type[j];
1978 /* get dummy mass type from first char of heavy atom type (N or C) */
1981 atype->atomNameFromAtomType(get_atype(heavies[0], at, rtpFFDB, &rt)));
1982 std::string ch = get_dummymass_name(vsiteconflist, tpname, nexttpname);
1990 "Can't find dummy mass for type %s bonded to type %s in the "
1991 "virtual site database (.vsd files). Add it to the database!\n",
1992 tpname, nexttpname);
1997 "A dummy mass for type %s bonded to type %s is required, but "
1998 "no virtual site database (.vsd) files where found.\n",
1999 tpname, nexttpname);
2007 tpM = vsite_nm2type(name.c_str(), atype);
2008 /* make space for 2 masses: shift all atoms starting with 'Heavy' */
2014 fprintf(stderr, "Inserting %d dummy masses at %d\n", NMASS, o2n[i0] + 1);
2017 for (int j = i0; j < at->nr; j++)
2022 newx.resize(at->nr + nadd);
2023 srenew(newatom, at->nr + nadd);
2024 srenew(newatomname, at->nr + nadd);
2025 srenew(newvsite_type, at->nr + nadd);
2026 srenew(newcgnr, at->nr + nadd);
2028 for (int j = 0; j < NMASS; j++)
2030 newatomname[at->nr + nadd - 1 - j] = nullptr;
2033 /* calculate starting position for the masses */
2035 /* get atom masses, and set Heavy and Hatoms mass to zero */
2036 for (int j = 0; j < nrHatoms; j++)
2038 mHtot += get_amass(Hatoms[j], at, rtpFFDB, &rt);
2039 at->atom[Hatoms[j]].m = at->atom[Hatoms[j]].mB = 0;
2041 mtot = mHtot + get_amass(Heavy, at, rtpFFDB, &rt);
2042 at->atom[Heavy].m = at->atom[Heavy].mB = 0;
2047 fact2 = mHtot / mtot;
2048 fact = std::sqrt(fact2);
2049 /* generate vectors parallel and perpendicular to rotational axis:
2050 * rpar = Heavy -> Hcom
2051 * rperp = Hcom -> H1 */
2053 for (int j = 0; j < nrHatoms; j++)
2055 rvec_inc(rpar, (*x)[Hatoms[j]]);
2057 svmul(1.0 / nrHatoms, rpar, rpar); /* rpar = ( H1+H2+H3 ) / 3 */
2058 rvec_dec(rpar, (*x)[Heavy]); /* - Heavy */
2059 rvec_sub((*x)[Hatoms[0]], (*x)[Heavy], rperp);
2060 rvec_dec(rperp, rpar); /* rperp = H1 - Heavy - rpar */
2061 /* calc mass positions */
2062 svmul(fact2, rpar, temp);
2063 for (int j = 0; (j < NMASS); j++) /* xM = xN + fact2 * rpar +/- fact * rperp */
2065 rvec_add((*x)[Heavy], temp, newx[ni0 + j]);
2067 svmul(fact, rperp, temp);
2068 rvec_inc(newx[ni0], temp);
2069 rvec_dec(newx[ni0 + 1], temp);
2070 /* set atom parameters for the masses */
2071 for (int j = 0; (j < NMASS); j++)
2073 /* make name: "M??#" or "M?#" (? is atomname, # is number) */
2076 for (k = 0; (*at->atomname[Heavy])[k] && (k < NMASS); k++)
2078 name[k + 1] = (*at->atomname[Heavy])[k];
2080 name[k + 1] = atomnamesuffix[j];
2082 newatomname[ni0 + j] = put_symtab(symtab, name.c_str());
2083 newatom[ni0 + j].m = newatom[ni0 + j].mB = mtot / NMASS;
2084 newatom[ni0 + j].q = newatom[ni0 + j].qB = 0.0;
2085 newatom[ni0 + j].type = newatom[ni0 + j].typeB = tpM;
2086 newatom[ni0 + j].ptype = eptAtom;
2087 newatom[ni0 + j].resind = at->atom[i0].resind;
2088 newatom[ni0 + j].elem[0] = 'M';
2089 newatom[ni0 + j].elem[1] = '\0';
2090 newvsite_type[ni0 + j] = NOTSET;
2091 newcgnr[ni0 + j] = (*cgnr)[i0];
2093 /* add constraints between dummy masses and to heavies[0] */
2094 /* 'add_shift' says which atoms won't be renumbered afterwards */
2095 my_add_param(&(plist[F_CONSTRNC]), heavies[0], add_shift + ni0, NOTSET);
2096 my_add_param(&(plist[F_CONSTRNC]), heavies[0], add_shift + ni0 + 1, NOTSET);
2097 my_add_param(&(plist[F_CONSTRNC]), add_shift + ni0, add_shift + ni0 + 1, NOTSET);
2099 /* generate Heavy, H1, H2 and H3 from M1, M2 and heavies[0] */
2100 /* note that vsite_type cannot be NOTSET, because we just set it */
2101 add_vsite3_atoms(&plist[(*vsite_type)[Heavy]], Heavy, heavies[0],
2102 add_shift + ni0, add_shift + ni0 + 1, FALSE);
2103 for (int j = 0; j < nrHatoms; j++)
2105 add_vsite3_atoms(&plist[(*vsite_type)[Hatoms[j]]], Hatoms[j], heavies[0],
2106 add_shift + ni0, add_shift + ni0 + 1, Hat_SwapParity[j]);
2118 "Cannot convert atom %d %s (bound to a heavy atom "
2120 " %d bonds and %d bound hydrogens atoms) to virtual site\n",
2121 i + 1, *(at->atomname[i]), tpname, nrbonds, nrHatoms);
2125 /* add vsite parameters to topology,
2126 also get rid of negative vsite_types */
2127 add_vsites(plist, (*vsite_type), Heavy, nrHatoms, Hatoms, nrheavies, heavies);
2128 /* transfer mass of virtual site to Heavy atom */
2129 for (int j = 0; j < nrHatoms; j++)
2131 if (is_vsite((*vsite_type)[Hatoms[j]]))
2133 at->atom[Heavy].m += at->atom[Hatoms[j]].m;
2134 at->atom[Heavy].mB = at->atom[Heavy].m;
2135 at->atom[Hatoms[j]].m = at->atom[Hatoms[j]].mB = 0;
2142 fprintf(debug, "atom %d: ", o2n[i] + 1);
2143 print_bonds(debug, o2n, nrHatoms, Hatoms, Heavy, nrheavies, heavies);
2145 } /* if vsite NOTSET & is hydrogen */
2147 } /* for i < at->nr */
2151 fprintf(debug, "Before inserting new atoms:\n");
2152 for (int i = 0; i < at->nr; i++)
2154 fprintf(debug, "%4d %4d %4s %4d %4s %6d %-10s\n", i + 1, o2n[i] + 1,
2155 at->atomname[i] ? *(at->atomname[i]) : "(NULL)", at->resinfo[at->atom[i].resind].nr,
2156 at->resinfo[at->atom[i].resind].name ? *(at->resinfo[at->atom[i].resind].name) : "(NULL)",
2158 ((*vsite_type)[i] == NOTSET) ? "NOTSET" : interaction_function[(*vsite_type)[i]].name);
2160 fprintf(debug, "new atoms to be inserted:\n");
2161 for (int i = 0; i < at->nr + nadd; i++)
2165 fprintf(debug, "%4d %4s %4d %6d %-10s\n", i + 1,
2166 newatomname[i] ? *(newatomname[i]) : "(NULL)", newatom[i].resind, newcgnr[i],
2167 (newvsite_type[i] == NOTSET) ? "NOTSET"
2168 : interaction_function[newvsite_type[i]].name);
2173 /* add all original atoms to the new arrays, using o2n index array */
2174 for (int i = 0; i < at->nr; i++)
2176 newatomname[o2n[i]] = at->atomname[i];
2177 newatom[o2n[i]] = at->atom[i];
2178 newvsite_type[o2n[i]] = (*vsite_type)[i];
2179 newcgnr[o2n[i]] = (*cgnr)[i];
2180 copy_rvec((*x)[i], newx[o2n[i]]);
2182 /* throw away old atoms */
2184 sfree(at->atomname);
2187 /* put in the new ones */
2190 at->atomname = newatomname;
2191 *vsite_type = newvsite_type;
2194 if (at->nr > add_shift)
2197 "Added impossible amount of dummy masses "
2198 "(%d on a total of %d atoms)\n",
2199 nadd, at->nr - nadd);
2204 fprintf(debug, "After inserting new atoms:\n");
2205 for (int i = 0; i < at->nr; i++)
2207 fprintf(debug, "%4d %4s %4d %4s %6d %-10s\n", i + 1,
2208 at->atomname[i] ? *(at->atomname[i]) : "(NULL)", at->resinfo[at->atom[i].resind].nr,
2209 at->resinfo[at->atom[i].resind].name ? *(at->resinfo[at->atom[i].resind].name) : "(NULL)",
2211 ((*vsite_type)[i] == NOTSET) ? "NOTSET" : interaction_function[(*vsite_type)[i]].name);
2215 /* now renumber all the interactions because of the added atoms */
2216 for (int ftype = 0; ftype < F_NRE; ftype++)
2218 InteractionsOfType* params = &(plist[ftype]);
2221 fprintf(debug, "Renumbering %zu %s\n", params->size(), interaction_function[ftype].longname);
2223 /* Horrible hacks needed here to get this to work */
2224 for (auto parm = params->interactionTypes.begin(); parm != params->interactionTypes.end(); parm++)
2226 gmx::ArrayRef<const int> atomNumbers(parm->atoms());
2227 std::vector<int> newAtomNumber;
2228 for (int j = 0; j < NRAL(ftype); j++)
2230 if (atomNumbers[j] >= add_shift)
2234 fprintf(debug, " [%d -> %d]", atomNumbers[j], atomNumbers[j] - add_shift);
2236 newAtomNumber.emplace_back(atomNumbers[j] - add_shift);
2242 fprintf(debug, " [%d -> %d]", atomNumbers[j], o2n[atomNumbers[j]]);
2244 newAtomNumber.emplace_back(o2n[atomNumbers[j]]);
2247 *parm = InteractionOfType(newAtomNumber, parm->forceParam(), parm->interactionTypeName());
2250 fprintf(debug, "\n");
2254 /* sort constraint parameters */
2255 InteractionsOfType* params = &(plist[F_CONSTRNC]);
2256 for (auto& type : params->interactionTypes)
2264 /* tell the user what we did */
2265 fprintf(stderr, "Marked %d virtual sites\n", nvsite);
2266 fprintf(stderr, "Added %d dummy masses\n", nadd);
2267 fprintf(stderr, "Added %zu new constraints\n", plist[F_CONSTRNC].size());
2270 void do_h_mass(InteractionsOfType* psb, int vsite_type[], t_atoms* at, real mHmult, bool bDeuterate)
2272 /* loop over all atoms */
2273 for (int i = 0; i < at->nr; i++)
2275 /* adjust masses if i is hydrogen and not a virtual site */
2276 if (!is_vsite(vsite_type[i]) && is_hydrogen(*(at->atomname[i])))
2278 /* find bonded heavy atom */
2280 for (auto parm = psb->interactionTypes.begin();
2281 (parm != psb->interactionTypes.end()) && (a == NOTSET); parm++)
2283 /* if other atom is not a virtual site, it is the one we want */
2284 if ((parm->ai() == i) && !is_vsite(vsite_type[parm->aj()]))
2288 else if ((parm->aj() == i) && !is_vsite(vsite_type[parm->ai()]))
2295 gmx_fatal(FARGS, "Unbound hydrogen atom (%d) found while adjusting mass", i + 1);
2298 /* adjust mass of i (hydrogen) with mHmult
2299 and correct mass of a (bonded atom) with same amount */
2302 at->atom[a].m -= (mHmult - 1.0) * at->atom[i].m;
2303 at->atom[a].mB -= (mHmult - 1.0) * at->atom[i].m;
2305 at->atom[i].m *= mHmult;
2306 at->atom[i].mB *= mHmult;