2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
7 * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
40 #include "vsite_parm.h"
49 #include "gromacs/gmxpreprocess/add_par.h"
50 #include "gromacs/gmxpreprocess/gpp_atomtype.h"
51 #include "gromacs/gmxpreprocess/grompp_impl.h"
52 #include "gromacs/gmxpreprocess/notset.h"
53 #include "gromacs/gmxpreprocess/toputil.h"
54 #include "gromacs/math/functions.h"
55 #include "gromacs/math/units.h"
56 #include "gromacs/math/utilities.h"
57 #include "gromacs/math/vec.h"
58 #include "gromacs/mdtypes/md_enums.h"
59 #include "gromacs/topology/ifunc.h"
60 #include "gromacs/topology/topology.h"
61 #include "gromacs/utility/arrayref.h"
62 #include "gromacs/utility/cstringutil.h"
63 #include "gromacs/utility/fatalerror.h"
64 #include "gromacs/utility/gmxassert.h"
65 #include "gromacs/utility/logger.h"
66 #include "gromacs/utility/smalloc.h"
67 #include "gromacs/utility/strconvert.h"
69 #include "hackblock.h"
73 * Data type to store information about bonded interactions for virtual sites.
75 class VsiteBondedInteraction
78 //! Constructor initializes datastructure.
79 VsiteBondedInteraction(gmx::ArrayRef<const int> atomIndex, real parameterValue) :
80 parameterValue_(parameterValue)
82 GMX_RELEASE_ASSERT(atomIndex.size() <= atomIndex_.size(),
83 "Cannot add more atom indices than maximum number");
84 std::array<int, 4>::iterator atomIndexIt = atomIndex_.begin();
85 for (const auto index : atomIndex)
87 *atomIndexIt++ = index;
91 //! Access the individual elements set for the vsite bonded interaction.
92 const int& ai() const { return atomIndex_[0]; }
93 const int& aj() const { return atomIndex_[1]; }
94 const int& ak() const { return atomIndex_[2]; }
95 const int& al() const { return atomIndex_[3]; }
97 const real& parameterValue() const { return parameterValue_; }
101 //! The distance value for this bonded interaction.
102 real parameterValue_;
103 //! Array of atom indices
104 std::array<int, 4> atomIndex_;
108 * Stores information about single virtual site bonded parameter.
110 struct VsiteBondParameter
112 //! Constructor initializes datastructure.
113 VsiteBondParameter(int ftype, const InteractionOfType& vsiteInteraction) :
115 vsiteInteraction_(vsiteInteraction)
118 //! Function type for virtual site.
121 * Interaction type data.
123 * The datastructure should never be used in a case where the InteractionType
124 * used to construct it might go out of scope before this object, as it would cause
125 * the reference to type_ to dangle.
127 const InteractionOfType& vsiteInteraction_;
131 * Helper type for conversion of bonded parameters to virtual sites.
133 struct Atom2VsiteBond
135 //! Function type for conversion.
137 //! The vsite parameters in a list.
138 std::vector<VsiteBondParameter> vSiteBondedParameters;
141 //! Convenience type def for virtual site connections.
142 using Atom2VsiteConnection = std::vector<int>;
144 static int vsite_bond_nrcheck(int ftype)
148 if ((interaction_function[ftype].flags & (IF_BTYPE | IF_CONSTRAINT | IF_ATYPE)) || (ftype == F_IDIHS))
150 nrcheck = NRAL(ftype);
160 static void enter_bonded(int nratoms, std::vector<VsiteBondedInteraction>* bondeds, const InteractionOfType& type)
162 GMX_RELEASE_ASSERT(nratoms == type.atoms().ssize(), "Size of atom array must match");
163 bondeds->emplace_back(VsiteBondedInteraction(type.atoms(), type.c0()));
167 * Wraps the datastructures for the different vsite bondeds.
169 struct AllVsiteBondedInteractions
172 std::vector<VsiteBondedInteraction> bonds;
174 std::vector<VsiteBondedInteraction> angles;
176 std::vector<VsiteBondedInteraction> dihedrals;
179 static AllVsiteBondedInteractions createVsiteBondedInformation(int nrat,
180 gmx::ArrayRef<const int> atoms,
181 gmx::ArrayRef<const Atom2VsiteBond> at2vb)
183 AllVsiteBondedInteractions allVsiteBondeds;
184 for (int k = 0; k < nrat; k++)
186 for (const auto& vsite : at2vb[atoms[k]].vSiteBondedParameters)
188 int ftype = vsite.ftype_;
189 const InteractionOfType& type = vsite.vsiteInteraction_;
190 int nrcheck = vsite_bond_nrcheck(ftype);
191 /* abuse nrcheck to see if we're adding bond, angle or idih */
194 case 2: enter_bonded(nrcheck, &allVsiteBondeds.bonds, type); break;
195 case 3: enter_bonded(nrcheck, &allVsiteBondeds.angles, type); break;
196 case 4: enter_bonded(nrcheck, &allVsiteBondeds.dihedrals, type); break;
200 return allVsiteBondeds;
203 static std::vector<Atom2VsiteBond> make_at2vsitebond(int natoms, gmx::ArrayRef<InteractionsOfType> plist)
207 std::vector<Atom2VsiteBond> at2vb(natoms);
210 for (int ftype = 0; (ftype < F_NRE); ftype++)
212 if ((interaction_function[ftype].flags & IF_VSITE) && ftype != F_VSITEN)
214 for (int i = 0; (i < gmx::ssize(plist[ftype])); i++)
216 gmx::ArrayRef<const int> atoms = plist[ftype].interactionTypes[i].atoms();
217 for (int j = 0; j < NRAL(ftype); j++)
219 bVSI[atoms[j]] = TRUE;
225 for (int ftype = 0; (ftype < F_NRE); ftype++)
227 int nrcheck = vsite_bond_nrcheck(ftype);
230 for (int i = 0; (i < gmx::ssize(plist[ftype])); i++)
232 gmx::ArrayRef<const int> aa = plist[ftype].interactionTypes[i].atoms();
233 for (int j = 0; j < nrcheck; j++)
237 at2vb[aa[j]].vSiteBondedParameters.emplace_back(
238 ftype, plist[ftype].interactionTypes[i]);
249 static std::vector<Atom2VsiteConnection> make_at2vsitecon(int natoms, gmx::ArrayRef<InteractionsOfType> plist)
251 std::vector<bool> bVSI(natoms);
252 std::vector<Atom2VsiteConnection> at2vc(natoms);
254 for (int ftype = 0; (ftype < F_NRE); ftype++)
256 if ((interaction_function[ftype].flags & IF_VSITE) && ftype != F_VSITEN)
258 for (int i = 0; (i < gmx::ssize(plist[ftype])); i++)
260 gmx::ArrayRef<const int> atoms = plist[ftype].interactionTypes[i].atoms();
261 for (int j = 0; j < NRAL(ftype); j++)
263 bVSI[atoms[j]] = TRUE;
269 for (int ftype = 0; (ftype < F_NRE); ftype++)
271 if (interaction_function[ftype].flags & IF_CONSTRAINT)
273 for (int i = 0; (i < gmx::ssize(plist[ftype])); i++)
275 int ai = plist[ftype].interactionTypes[i].ai();
276 int aj = plist[ftype].interactionTypes[i].aj();
277 if (bVSI[ai] && bVSI[aj])
279 /* Store forward direction */
280 at2vc[ai].emplace_back(aj);
281 /* Store backward direction */
282 at2vc[aj].emplace_back(ai);
291 static void print_bad(FILE* fp,
292 gmx::ArrayRef<const VsiteBondedInteraction> bonds,
293 gmx::ArrayRef<const VsiteBondedInteraction> angles,
294 gmx::ArrayRef<const VsiteBondedInteraction> idihs)
298 fprintf(fp, "bonds:");
299 for (const auto& bond : bonds)
301 fprintf(fp, " %d-%d (%g)", bond.ai() + 1, bond.aj() + 1, bond.parameterValue());
307 fprintf(fp, "angles:");
308 for (const auto& angle : angles)
310 fprintf(fp, " %d-%d-%d (%g)", angle.ai() + 1, angle.aj() + 1, angle.ak() + 1, angle.parameterValue());
316 fprintf(fp, "idihs:");
317 for (const auto& idih : idihs)
325 idih.parameterValue());
331 static void printInteractionOfType(FILE* fp, int ftype, int i, const InteractionOfType& type)
334 static int prev_ftype = NOTSET;
335 static int prev_i = NOTSET;
337 if ((ftype != prev_ftype) || (i != prev_i))
343 fprintf(fp, "(%d) plist[%s].param[%d]", pass, interaction_function[ftype].name, i);
344 gmx::ArrayRef<const real> forceParam = type.forceParam();
345 for (int j = 0; j < NRFP(ftype); j++)
347 fprintf(fp, ".c[%d]=%g ", j, forceParam[j]);
353 static real get_bond_length(gmx::ArrayRef<const VsiteBondedInteraction> bonds, t_iatom ai, t_iatom aj)
358 for (const auto& bond : bonds)
360 /* check both ways */
361 if (((ai == bond.ai()) && (aj == bond.aj())) || ((ai == bond.aj()) && (aj == bond.ai())))
363 bondlen = bond.parameterValue(); /* note: parameterValue might be NOTSET */
370 static real get_angle(gmx::ArrayRef<const VsiteBondedInteraction> angles, t_iatom ai, t_iatom aj, t_iatom ak)
375 for (const auto& ang : angles)
377 /* check both ways */
378 if (((ai == ang.ai()) && (aj == ang.aj()) && (ak == ang.ak()))
379 || ((ai == ang.ak()) && (aj == ang.aj()) && (ak == ang.ai())))
381 angle = gmx::c_deg2Rad * ang.parameterValue();
388 static const char* get_atomtype_name_AB(t_atom* atom, PreprocessingAtomTypes* atypes)
390 const char* name = *atypes->atomNameFromAtomType(atom->type);
392 /* When using the decoupling option, atom types are changed
393 * to decoupled for the non-bonded interactions, but the virtual
394 * sites constructions should be based on the "normal" interactions.
395 * So we return the state B atom type name if the state A atom
396 * type is the decoupled one. We should actually check for the atom
397 * type number, but that's not passed here. So we check for
398 * the decoupled atom type name. This should not cause trouble
399 * as this code is only used for topologies with v-sites without
400 * parameters generated by pdb2gmx.
402 if (strcmp(name, "decoupled") == 0)
404 name = *atypes->atomNameFromAtomType(atom->typeB);
410 static bool calc_vsite3_param(PreprocessingAtomTypes* atypes,
411 InteractionOfType* vsite,
413 gmx::ArrayRef<const VsiteBondedInteraction> bonds,
414 gmx::ArrayRef<const VsiteBondedInteraction> angles)
416 /* i = virtual site | ,k
417 * j = 1st bonded heavy atom | i-j
418 * k,l = 2nd bonded atoms | `l
422 real bjk, bjl, a = -1, b = -1;
423 /* check if this is part of a NH3 , NH2-umbrella or CH3 group,
424 * i.e. if atom k and l are dummy masses (MNH* or MCH3*) */
425 bXH3 = ((gmx::equalCaseInsensitive(get_atomtype_name_AB(&at->atom[vsite->ak()], atypes), "MNH", 3))
426 && (gmx::equalCaseInsensitive(
427 get_atomtype_name_AB(&at->atom[vsite->al()], atypes), "MNH", 3)))
428 || ((gmx::equalCaseInsensitive(
429 get_atomtype_name_AB(&at->atom[vsite->ak()], atypes), "MCH3", 4))
430 && (gmx::equalCaseInsensitive(
431 get_atomtype_name_AB(&at->atom[vsite->al()], atypes), "MCH3", 4)));
433 bjk = get_bond_length(bonds, vsite->aj(), vsite->ak());
434 bjl = get_bond_length(bonds, vsite->aj(), vsite->al());
435 bError = (bjk == NOTSET) || (bjl == NOTSET);
438 /* now we get some XH2/XH3 group specific construction */
439 /* note: we call the heavy atom 'C' and the X atom 'N' */
440 real bMM, bCM, bCN, bNH, aCNH, dH, rH, dM, rM;
443 /* check if bonds from heavy atom (j) to dummy masses (k,l) are equal: */
444 bError = bError || (bjk != bjl);
446 /* the X atom (C or N) in the XH2/XH3 group is the first after the masses: */
447 aN = std::max(vsite->ak(), vsite->al()) + 1;
449 /* get common bonds */
450 bMM = get_bond_length(bonds, vsite->ak(), vsite->al());
452 bCN = get_bond_length(bonds, vsite->aj(), aN);
453 bError = bError || (bMM == NOTSET) || (bCN == NOTSET);
455 /* calculate common things */
457 dM = std::sqrt(gmx::square(bCM) - gmx::square(rM));
459 /* are we dealing with the X atom? */
460 if (vsite->ai() == aN)
462 /* this is trivial */
463 a = b = 0.5 * bCN / dM;
467 /* get other bondlengths and angles: */
468 bNH = get_bond_length(bonds, aN, vsite->ai());
469 aCNH = get_angle(angles, vsite->aj(), aN, vsite->ai());
470 bError = bError || (bNH == NOTSET) || (aCNH == NOTSET);
473 dH = bCN - bNH * std::cos(aCNH);
474 rH = bNH * std::sin(aCNH);
476 a = 0.5 * (dH / dM + rH / rM);
477 b = 0.5 * (dH / dM - rH / rM);
483 "calc_vsite3_param not implemented for the general case "
487 vsite->setForceParameter(0, a);
488 vsite->setForceParameter(1, b);
493 static bool calc_vsite3fd_param(InteractionOfType* vsite,
494 gmx::ArrayRef<const VsiteBondedInteraction> bonds,
495 gmx::ArrayRef<const VsiteBondedInteraction> angles)
497 /* i = virtual site | ,k
498 * j = 1st bonded heavy atom | i-j
499 * k,l = 2nd bonded atoms | `l
503 real bij, bjk, bjl, aijk, aijl, rk, rl;
505 bij = get_bond_length(bonds, vsite->ai(), vsite->aj());
506 bjk = get_bond_length(bonds, vsite->aj(), vsite->ak());
507 bjl = get_bond_length(bonds, vsite->aj(), vsite->al());
508 aijk = get_angle(angles, vsite->ai(), vsite->aj(), vsite->ak());
509 aijl = get_angle(angles, vsite->ai(), vsite->aj(), vsite->al());
510 bError = (bij == NOTSET) || (bjk == NOTSET) || (bjl == NOTSET) || (aijk == NOTSET) || (aijl == NOTSET);
512 rk = bjk * std::sin(aijk);
513 rl = bjl * std::sin(aijl);
514 vsite->setForceParameter(0, rk / (rk + rl));
515 vsite->setForceParameter(1, -bij);
520 static bool calc_vsite3fad_param(InteractionOfType* vsite,
521 gmx::ArrayRef<const VsiteBondedInteraction> bonds,
522 gmx::ArrayRef<const VsiteBondedInteraction> angles)
524 /* i = virtual site |
525 * j = 1st bonded heavy atom | i-j
526 * k = 2nd bonded heavy atom | `k-l
527 * l = 3d bonded heavy atom |
530 bool bSwapParity, bError;
533 bSwapParity = (vsite->c1() == -1);
535 bij = get_bond_length(bonds, vsite->ai(), vsite->aj());
536 aijk = get_angle(angles, vsite->ai(), vsite->aj(), vsite->ak());
537 bError = (bij == NOTSET) || (aijk == NOTSET);
539 vsite->setForceParameter(1, bij);
540 vsite->setForceParameter(0, gmx::c_rad2Deg * aijk);
544 vsite->setForceParameter(0, 360 - vsite->c0());
550 static bool calc_vsite3out_param(PreprocessingAtomTypes* atypes,
551 InteractionOfType* vsite,
553 gmx::ArrayRef<const VsiteBondedInteraction> bonds,
554 gmx::ArrayRef<const VsiteBondedInteraction> angles)
556 /* i = virtual site | ,k
557 * j = 1st bonded heavy atom | i-j
558 * k,l = 2nd bonded atoms | `l
559 * NOTE: i is out of the j-k-l plane!
562 bool bXH3, bError, bSwapParity;
563 real bij, bjk, bjl, aijk, aijl, akjl, pijk, pijl, a, b, c;
565 /* check if this is part of a NH2-umbrella, NH3 or CH3 group,
566 * i.e. if atom k and l are dummy masses (MNH* or MCH3*) */
567 bXH3 = ((gmx::equalCaseInsensitive(get_atomtype_name_AB(&at->atom[vsite->ak()], atypes), "MNH", 3))
568 && (gmx::equalCaseInsensitive(
569 get_atomtype_name_AB(&at->atom[vsite->al()], atypes), "MNH", 3)))
570 || ((gmx::equalCaseInsensitive(
571 get_atomtype_name_AB(&at->atom[vsite->ak()], atypes), "MCH3", 4))
572 && (gmx::equalCaseInsensitive(
573 get_atomtype_name_AB(&at->atom[vsite->al()], atypes), "MCH3", 4)));
575 /* check if construction parity must be swapped */
576 bSwapParity = (vsite->c1() == -1);
578 bjk = get_bond_length(bonds, vsite->aj(), vsite->ak());
579 bjl = get_bond_length(bonds, vsite->aj(), vsite->al());
580 bError = (bjk == NOTSET) || (bjl == NOTSET);
583 /* now we get some XH3 group specific construction */
584 /* note: we call the heavy atom 'C' and the X atom 'N' */
585 real bMM, bCM, bCN, bNH, aCNH, dH, rH, rHx, rHy, dM, rM;
588 /* check if bonds from heavy atom (j) to dummy masses (k,l) are equal: */
589 bError = bError || (bjk != bjl);
591 /* the X atom (C or N) in the XH3 group is the first after the masses: */
592 aN = std::max(vsite->ak(), vsite->al()) + 1;
594 /* get all bondlengths and angles: */
595 bMM = get_bond_length(bonds, vsite->ak(), vsite->al());
597 bCN = get_bond_length(bonds, vsite->aj(), aN);
598 bNH = get_bond_length(bonds, aN, vsite->ai());
599 aCNH = get_angle(angles, vsite->aj(), aN, vsite->ai());
600 bError = bError || (bMM == NOTSET) || (bCN == NOTSET) || (bNH == NOTSET) || (aCNH == NOTSET);
603 dH = bCN - bNH * std::cos(aCNH);
604 rH = bNH * std::sin(aCNH);
605 /* we assume the H's are symmetrically distributed */
606 rHx = rH * std::cos(gmx::c_deg2Rad * 30);
607 rHy = rH * std::sin(gmx::c_deg2Rad * 30);
609 dM = std::sqrt(gmx::square(bCM) - gmx::square(rM));
610 a = 0.5 * ((dH / dM) - (rHy / rM));
611 b = 0.5 * ((dH / dM) + (rHy / rM));
612 c = rHx / (2 * dM * rM);
616 /* this is the general construction */
618 bij = get_bond_length(bonds, vsite->ai(), vsite->aj());
619 aijk = get_angle(angles, vsite->ai(), vsite->aj(), vsite->ak());
620 aijl = get_angle(angles, vsite->ai(), vsite->aj(), vsite->al());
621 akjl = get_angle(angles, vsite->ak(), vsite->aj(), vsite->al());
622 bError = bError || (bij == NOTSET) || (aijk == NOTSET) || (aijl == NOTSET) || (akjl == NOTSET);
624 pijk = std::cos(aijk) * bij;
625 pijl = std::cos(aijl) * bij;
626 a = (pijk + (pijk * std::cos(akjl) - pijl) * std::cos(akjl) / gmx::square(std::sin(akjl))) / bjk;
627 b = (pijl + (pijl * std::cos(akjl) - pijk) * std::cos(akjl) / gmx::square(std::sin(akjl))) / bjl;
628 c = -std::sqrt(gmx::square(bij)
629 - (gmx::square(pijk) - 2 * pijk * pijl * std::cos(akjl) + gmx::square(pijl))
630 / gmx::square(std::sin(akjl)))
631 / (bjk * bjl * std::sin(akjl));
634 vsite->setForceParameter(0, a);
635 vsite->setForceParameter(1, b);
638 vsite->setForceParameter(2, -c);
642 vsite->setForceParameter(2, c);
647 static bool calc_vsite4fd_param(InteractionOfType* vsite,
648 gmx::ArrayRef<const VsiteBondedInteraction> bonds,
649 gmx::ArrayRef<const VsiteBondedInteraction> angles,
650 const gmx::MDLogger& logger)
652 /* i = virtual site | ,k
653 * j = 1st bonded heavy atom | i-j-m
654 * k,l,m = 2nd bonded atoms | `l
658 real bij, bjk, bjl, bjm, aijk, aijl, aijm, akjm, akjl;
659 real pk, pl, pm, cosakl, cosakm, sinakl, sinakm, cl, cm;
661 bij = get_bond_length(bonds, vsite->ai(), vsite->aj());
662 bjk = get_bond_length(bonds, vsite->aj(), vsite->ak());
663 bjl = get_bond_length(bonds, vsite->aj(), vsite->al());
664 bjm = get_bond_length(bonds, vsite->aj(), vsite->am());
665 aijk = get_angle(angles, vsite->ai(), vsite->aj(), vsite->ak());
666 aijl = get_angle(angles, vsite->ai(), vsite->aj(), vsite->al());
667 aijm = get_angle(angles, vsite->ai(), vsite->aj(), vsite->am());
668 akjm = get_angle(angles, vsite->ak(), vsite->aj(), vsite->am());
669 akjl = get_angle(angles, vsite->ak(), vsite->aj(), vsite->al());
670 bError = (bij == NOTSET) || (bjk == NOTSET) || (bjl == NOTSET) || (bjm == NOTSET) || (aijk == NOTSET)
671 || (aijl == NOTSET) || (aijm == NOTSET) || (akjm == NOTSET) || (akjl == NOTSET);
675 pk = bjk * std::sin(aijk);
676 pl = bjl * std::sin(aijl);
677 pm = bjm * std::sin(aijm);
678 cosakl = (std::cos(akjl) - std::cos(aijk) * std::cos(aijl)) / (std::sin(aijk) * std::sin(aijl));
679 cosakm = (std::cos(akjm) - std::cos(aijk) * std::cos(aijm)) / (std::sin(aijk) * std::sin(aijm));
680 if (cosakl < -1 || cosakl > 1 || cosakm < -1 || cosakm > 1)
682 GMX_LOG(logger.warning)
684 .appendTextFormatted(
685 "virtual site %d: angle ijk = %f, angle ijl = %f, angle ijm = %f",
687 gmx::c_rad2Deg * aijk,
688 gmx::c_rad2Deg * aijl,
689 gmx::c_rad2Deg * aijm);
691 "invalid construction in calc_vsite4fd for atom %d: "
692 "cosakl=%f, cosakm=%f\n",
697 sinakl = std::sqrt(1 - gmx::square(cosakl));
698 sinakm = std::sqrt(1 - gmx::square(cosakm));
700 /* note: there is a '+' because of the way the sines are calculated */
701 cl = -pk / (pl * cosakl - pk + pl * sinakl * (pm * cosakm - pk) / (pm * sinakm));
702 cm = -pk / (pm * cosakm - pk + pm * sinakm * (pl * cosakl - pk) / (pl * sinakl));
704 vsite->setForceParameter(0, cl);
705 vsite->setForceParameter(1, cm);
706 vsite->setForceParameter(2, -bij);
713 static bool calc_vsite4fdn_param(InteractionOfType* vsite,
714 gmx::ArrayRef<const VsiteBondedInteraction> bonds,
715 gmx::ArrayRef<const VsiteBondedInteraction> angles,
716 const gmx::MDLogger& logger)
718 /* i = virtual site | ,k
719 * j = 1st bonded heavy atom | i-j-m
720 * k,l,m = 2nd bonded atoms | `l
724 real bij, bjk, bjl, bjm, aijk, aijl, aijm;
725 real pk, pl, pm, a, b;
727 bij = get_bond_length(bonds, vsite->ai(), vsite->aj());
728 bjk = get_bond_length(bonds, vsite->aj(), vsite->ak());
729 bjl = get_bond_length(bonds, vsite->aj(), vsite->al());
730 bjm = get_bond_length(bonds, vsite->aj(), vsite->am());
731 aijk = get_angle(angles, vsite->ai(), vsite->aj(), vsite->ak());
732 aijl = get_angle(angles, vsite->ai(), vsite->aj(), vsite->al());
733 aijm = get_angle(angles, vsite->ai(), vsite->aj(), vsite->am());
735 bError = (bij == NOTSET) || (bjk == NOTSET) || (bjl == NOTSET) || (bjm == NOTSET)
736 || (aijk == NOTSET) || (aijl == NOTSET) || (aijm == NOTSET);
741 /* Calculate component of bond j-k along the direction i-j */
742 pk = -bjk * std::cos(aijk);
744 /* Calculate component of bond j-l along the direction i-j */
745 pl = -bjl * std::cos(aijl);
747 /* Calculate component of bond j-m along the direction i-j */
748 pm = -bjm * std::cos(aijm);
750 if (fabs(pl) < 1000 * GMX_REAL_MIN || fabs(pm) < 1000 * GMX_REAL_MIN)
752 GMX_LOG(logger.warning)
754 .appendTextFormatted(
755 "virtual site %d: angle ijk = %f, angle ijl = %f, angle ijm = %f",
757 gmx::c_rad2Deg * aijk,
758 gmx::c_rad2Deg * aijl,
759 gmx::c_rad2Deg * aijm);
761 "invalid construction in calc_vsite4fdn for atom %d: "
771 vsite->setForceParameter(0, a);
772 vsite->setForceParameter(1, b);
773 vsite->setForceParameter(2, bij);
780 int set_vsites(bool bVerbose,
782 PreprocessingAtomTypes* atypes,
783 gmx::ArrayRef<InteractionsOfType> plist,
784 const gmx::MDLogger& logger)
793 /* Make a reverse list to avoid ninteractions^2 operations */
794 std::vector<Atom2VsiteBond> at2vb = make_at2vsitebond(atoms->nr, plist);
796 for (ftype = 0; (ftype < F_NRE); ftype++)
798 if (interaction_function[ftype].flags & IF_VSITE)
800 nvsite += plist[ftype].size();
802 if (ftype == F_VSITEN)
804 /* We don't calculate parameters for VSITEN */
810 for (auto& param : plist[ftype].interactionTypes)
812 /* check if all parameters are set */
814 gmx::ArrayRef<const real> forceParam = param.forceParam();
815 for (int j = 0; (j < NRFP(ftype)) && bSet; j++)
817 bSet = forceParam[j] != NOTSET;
822 fprintf(debug, "bSet=%s ", gmx::boolToString(bSet));
823 printInteractionOfType(debug, ftype, i, plist[ftype].interactionTypes[i]);
827 if (bVerbose && bFirst)
831 .appendTextFormatted("Calculating parameters for virtual sites");
836 /* now set the vsite parameters: */
837 AllVsiteBondedInteractions allVsiteBondeds =
838 createVsiteBondedInformation(NRAL(ftype), param.atoms(), at2vb);
842 "Found %zu bonds, %zu angles and %zu idihs "
843 "for virtual site %d (%s)\n",
844 allVsiteBondeds.bonds.size(),
845 allVsiteBondeds.angles.size(),
846 allVsiteBondeds.dihedrals.size(),
848 interaction_function[ftype].longname);
850 allVsiteBondeds.bonds,
851 allVsiteBondeds.angles,
852 allVsiteBondeds.dihedrals);
857 bERROR = calc_vsite3_param(
858 atypes, ¶m, atoms, allVsiteBondeds.bonds, allVsiteBondeds.angles);
861 bERROR = calc_vsite3fd_param(
862 ¶m, allVsiteBondeds.bonds, allVsiteBondeds.angles);
865 bERROR = calc_vsite3fad_param(
866 ¶m, allVsiteBondeds.bonds, allVsiteBondeds.angles);
869 bERROR = calc_vsite3out_param(
870 atypes, ¶m, atoms, allVsiteBondeds.bonds, allVsiteBondeds.angles);
873 bERROR = calc_vsite4fd_param(
874 ¶m, allVsiteBondeds.bonds, allVsiteBondeds.angles, logger);
877 bERROR = calc_vsite4fdn_param(
878 ¶m, allVsiteBondeds.bonds, allVsiteBondeds.angles, logger);
882 "Automatic parameter generation not supported "
884 interaction_function[ftype].longname,
891 "Automatic parameter generation not supported "
892 "for %s atom %d for this bonding configuration",
893 interaction_function[ftype].longname,
904 void set_vsites_ptype(bool bVerbose, gmx_moltype_t* molt, const gmx::MDLogger& logger)
912 .appendTextFormatted("Setting particle type to V for virtual sites");
914 for (ftype = 0; ftype < F_NRE; ftype++)
916 InteractionList* il = &molt->ilist[ftype];
917 if (interaction_function[ftype].flags & IF_VSITE)
919 const int nra = interaction_function[ftype].nratoms;
920 const int nrd = il->size();
921 gmx::ArrayRef<const int> ia = il->iatoms;
927 .appendTextFormatted("doing %d %s virtual sites",
929 interaction_function[ftype].longname);
932 for (i = 0; (i < nrd);)
934 /* The virtual site */
935 int avsite = ia[i + 1];
936 molt->atoms.atom[avsite].ptype = ParticleType::VSite;
945 * Convenience typedef for linking function type to parameter numbers.
947 * The entries in this datastructure are valid if the particle participates in
948 * a virtual site interaction and has a valid vsite function type other than VSITEN.
949 * \todo Change to remove empty constructor when gmx::compat::optional is available.
951 class VsiteAtomMapping
954 //! Only construct with all information in place or nothing
955 VsiteAtomMapping(int functionType, int interactionIndex) :
956 functionType_(functionType),
957 interactionIndex_(interactionIndex)
960 VsiteAtomMapping() : functionType_(-1), interactionIndex_(-1) {}
961 //! Get function type.
962 const int& functionType() const { return functionType_; }
963 //! Get parameter number.
964 const int& interactionIndex() const { return interactionIndex_; }
967 //! Function type for the linked parameter.
969 //! The linked parameter.
970 int interactionIndex_;
973 static void check_vsite_constraints(gmx::ArrayRef<InteractionsOfType> plist,
975 const int vsite_type[],
976 const gmx::MDLogger& logger)
979 for (const auto& param : plist[cftype].interactionTypes)
981 gmx::ArrayRef<const int> atoms = param.atoms();
982 for (int k = 0; k < 2; k++)
985 if (vsite_type[atom] != NOTSET)
989 .appendTextFormatted(
990 "ERROR: Cannot have constraint (%d-%d) with virtual site (%d)",
1000 gmx_fatal(FARGS, "There were %d virtual sites involved in constraints", n);
1004 static void clean_vsite_bonds(gmx::ArrayRef<InteractionsOfType> plist,
1005 gmx::ArrayRef<const VsiteAtomMapping> pindex,
1007 const int vsite_type[],
1008 const gmx::MDLogger& logger)
1011 int nconverted, nremoved;
1012 int oatom, at1, at2;
1013 bool bKeep, bRemove, bAllFD;
1014 InteractionsOfType* ps;
1016 if (cftype == F_CONNBONDS)
1021 ps = &(plist[cftype]);
1025 for (auto bond = ps->interactionTypes.begin(); bond != ps->interactionTypes.end();)
1028 const int* first_atoms = nullptr;
1033 /* check if all virtual sites are constructed from the same atoms */
1035 gmx::ArrayRef<const int> atoms = bond->atoms();
1036 for (int k = 0; (k < 2) && !bKeep && !bRemove; k++)
1038 /* for all atoms in the bond */
1039 int atom = atoms[k];
1040 if (vsite_type[atom] != NOTSET && vsite_type[atom] != F_VSITEN)
1043 bool bThisFD = ((pindex[atom].functionType() == F_VSITE3FD)
1044 || (pindex[atom].functionType() == F_VSITE3FAD)
1045 || (pindex[atom].functionType() == F_VSITE4FD)
1046 || (pindex[atom].functionType() == F_VSITE4FDN));
1047 bool bThisOUT = ((pindex[atom].functionType() == F_VSITE3OUT)
1048 && ((interaction_function[cftype].flags & IF_CONSTRAINT) != 0U));
1049 bAllFD = bAllFD && bThisFD;
1050 if (bThisFD || bThisOUT)
1052 oatom = atoms[1 - k]; /* the other atom */
1053 if (vsite_type[oatom] == NOTSET
1055 == plist[pindex[atom].functionType()]
1056 .interactionTypes[pindex[atom].interactionIndex()]
1059 /* if the other atom isn't a vsite, and it is AI */
1069 /* TODO This fragment, and corresponding logic in
1070 clean_vsite_angles and clean_vsite_dihs should
1071 be refactored into a common function */
1074 /* if this is the first vsite we encounter then
1075 store construction atoms */
1076 /* TODO This would be nicer to implement with
1077 a C++ "vector view" class" with an
1078 STL-container-like interface. */
1079 vsnral = NRAL(pindex[atom].functionType()) - 1;
1080 first_atoms = plist[pindex[atom].functionType()]
1081 .interactionTypes[pindex[atom].interactionIndex()]
1088 GMX_ASSERT(vsnral != 0, "nvsite > 1 must have vsnral != 0");
1089 GMX_ASSERT(first_atoms != nullptr,
1090 "nvsite > 1 must have first_atoms != NULL");
1091 /* if it is not the first then
1092 check if this vsite is constructed from the same atoms */
1093 if (vsnral == NRAL(pindex[atom].functionType()) - 1)
1095 for (int m = 0; (m < vsnral) && !bKeep; m++)
1099 bool bPresent = false;
1100 atoms = plist[pindex[atom].functionType()]
1101 .interactionTypes[pindex[atom].interactionIndex()]
1105 for (int n = 0; (n < vsnral) && !bPresent; n++)
1107 if (atoms[m] == first_atoms[n])
1133 /* if we have no virtual sites in this bond, keep it */
1139 /* TODO This loop and the corresponding loop in
1140 check_vsite_angles should be refactored into a common
1142 /* check if all non-vsite atoms are used in construction: */
1143 bool bFirstTwo = true;
1144 for (int k = 0; (k < 2) && !bKeep; k++) /* for all atoms in the bond */
1146 int atom = atoms[k];
1147 if (vsite_type[atom] == NOTSET)
1150 for (int m = 0; (m < vsnral) && !bUsed; m++)
1152 GMX_ASSERT(first_atoms != nullptr,
1153 "If we've seen a vsite before, we know what its first atom "
1156 if (atom == first_atoms[m])
1159 bFirstTwo = bFirstTwo && m < 2;
1169 if (!(bAllFD && bFirstTwo))
1171 /* Two atom bonded interactions include constraints.
1172 * We need to remove constraints between vsite pairs that have
1173 * a fixed distance due to being constructed from the same
1174 * atoms, since this can be numerically unstable.
1176 for (int m = 0; m < vsnral && !bKeep; m++) /* all constr. atoms */
1178 at1 = first_atoms[m];
1179 at2 = first_atoms[(m + 1) % vsnral];
1180 bool bPresent = false;
1181 for (ftype = 0; ftype < F_NRE; ftype++)
1183 if (interaction_function[ftype].flags & IF_CONSTRAINT)
1185 for (auto entry = plist[ftype].interactionTypes.begin();
1186 (entry != plist[ftype].interactionTypes.end()) && !bPresent;
1189 /* all constraints until one matches */
1190 bPresent = (((entry->ai() == at1) && (entry->aj() == at2))
1191 || ((entry->ai() == at2) && (entry->aj() == at1)));
1207 else if (IS_CHEMBOND(cftype))
1209 plist[F_CONNBONDS].interactionTypes.emplace_back(*bond);
1210 bond = ps->interactionTypes.erase(bond);
1215 bond = ps->interactionTypes.erase(bond);
1222 GMX_LOG(logger.info)
1224 .appendTextFormatted("Removed %4d %15ss with virtual sites, %zu left",
1226 interaction_function[cftype].longname,
1231 GMX_LOG(logger.info)
1233 .appendTextFormatted(
1234 "Converted %4d %15ss with virtual sites to connections, %zu left",
1236 interaction_function[cftype].longname,
1241 GMX_LOG(logger.info)
1243 .appendTextFormatted(
1244 "Warning: removed %d %ss with vsite with %s construction\n"
1245 " This vsite construction does not guarantee constant "
1247 " If the constructions were generated by pdb2gmx ignore "
1250 interaction_function[cftype].longname,
1251 interaction_function[F_VSITE3OUT].longname);
1255 static void clean_vsite_angles(gmx::ArrayRef<InteractionsOfType> plist,
1256 gmx::ArrayRef<VsiteAtomMapping> pindex,
1258 const int vsite_type[],
1259 gmx::ArrayRef<const Atom2VsiteConnection> at2vc,
1260 const gmx::MDLogger& logger)
1263 InteractionsOfType* ps;
1265 ps = &(plist[cftype]);
1266 int oldSize = ps->size();
1267 for (auto angle = ps->interactionTypes.begin(); angle != ps->interactionTypes.end();)
1270 const int* first_atoms = nullptr;
1273 bool bAll3FAD = true;
1274 /* check if all virtual sites are constructed from the same atoms */
1276 gmx::ArrayRef<const int> atoms = angle->atoms();
1277 for (int k = 0; (k < 3) && !bKeep; k++) /* for all atoms in the angle */
1279 int atom = atoms[k];
1280 if (vsite_type[atom] != NOTSET && vsite_type[atom] != F_VSITEN)
1283 bAll3FAD = bAll3FAD && (pindex[atom].functionType() == F_VSITE3FAD);
1286 /* store construction atoms of first vsite */
1287 vsnral = NRAL(pindex[atom].functionType()) - 1;
1288 first_atoms = plist[pindex[atom].functionType()]
1289 .interactionTypes[pindex[atom].interactionIndex()]
1296 GMX_ASSERT(vsnral != 0,
1297 "If we've seen a vsite before, we know how many constructing atoms "
1300 first_atoms != nullptr,
1301 "If we've seen a vsite before, we know what its first atom index was");
1302 /* check if this vsite is constructed from the same atoms */
1303 if (vsnral == NRAL(pindex[atom].functionType()) - 1)
1305 for (int m = 0; (m < vsnral) && !bKeep; m++)
1307 const int* subAtoms;
1309 bool bPresent = false;
1310 subAtoms = plist[pindex[atom].functionType()]
1311 .interactionTypes[pindex[atom].interactionIndex()]
1315 for (int n = 0; (n < vsnral) && !bPresent; n++)
1317 if (subAtoms[m] == first_atoms[n])
1336 /* keep all angles with no virtual sites in them or
1337 with virtual sites with more than 3 constr. atoms */
1338 if (nvsite == 0 && vsnral > 3)
1343 /* check if all non-vsite atoms are used in construction: */
1344 bool bFirstTwo = true;
1345 for (int k = 0; (k < 3) && !bKeep; k++) /* for all atoms in the angle */
1348 if (vsite_type[atom] == NOTSET)
1351 for (int m = 0; (m < vsnral) && !bUsed; m++)
1354 first_atoms != nullptr,
1355 "If we've seen a vsite before, we know what its first atom index was");
1357 if (atom == first_atoms[m])
1360 bFirstTwo = bFirstTwo && m < 2;
1370 if (!(bAll3FAD && bFirstTwo))
1372 /* check if all constructing atoms are constrained together */
1373 for (int m = 0; m < vsnral && !bKeep; m++) /* all constr. atoms */
1375 at1 = first_atoms[m];
1376 at2 = first_atoms[(m + 1) % vsnral];
1377 bool bPresent = false;
1378 auto found = std::find(at2vc[at1].begin(), at2vc[at1].end(), at2);
1379 if (found != at2vc[at1].end())
1396 angle = ps->interactionTypes.erase(angle);
1400 if (oldSize != gmx::ssize(*ps))
1402 GMX_LOG(logger.info)
1404 .appendTextFormatted("Removed %4zu %15ss with virtual sites, %zu left",
1405 oldSize - ps->size(),
1406 interaction_function[cftype].longname,
1411 static void clean_vsite_dihs(gmx::ArrayRef<InteractionsOfType> plist,
1412 gmx::ArrayRef<const VsiteAtomMapping> pindex,
1414 const int vsite_type[],
1415 const gmx::MDLogger& logger)
1417 InteractionsOfType* ps;
1419 ps = &(plist[cftype]);
1421 int oldSize = ps->size();
1422 for (auto dih = ps->interactionTypes.begin(); dih != ps->interactionTypes.end();)
1425 const int* first_atoms = nullptr;
1428 gmx::ArrayRef<const int> atoms = dih->atoms();
1430 /* check if all virtual sites are constructed from the same atoms */
1432 for (int k = 0; (k < 4) && !bKeep; k++) /* for all atoms in the dihedral */
1435 if (vsite_type[atom] != NOTSET && vsite_type[atom] != F_VSITEN)
1439 /* store construction atoms of first vsite */
1440 vsnral = NRAL(pindex[atom].functionType()) - 1;
1441 first_atoms = plist[pindex[atom].functionType()]
1442 .interactionTypes[pindex[atom].interactionIndex()]
1449 GMX_ASSERT(vsnral != 0,
1450 "If we've seen a vsite before, we know how many constructing atoms "
1453 first_atoms != nullptr,
1454 "If we've seen a vsite before, we know what its first atom index was");
1455 /* check if this vsite is constructed from the same atoms */
1456 if (vsnral == NRAL(pindex[atom].functionType()) - 1)
1458 for (int m = 0; (m < vsnral) && !bKeep; m++)
1460 const int* subAtoms;
1462 bool bPresent = false;
1463 subAtoms = plist[pindex[atom].functionType()]
1464 .interactionTypes[pindex[atom].interactionIndex()]
1468 for (int n = 0; (n < vsnral) && !bPresent; n++)
1470 if (subAtoms[m] == first_atoms[n])
1482 /* TODO clean_site_bonds and _angles do this increment
1483 at the top of the loop. Refactor this for
1489 /* keep all dihedrals with no virtual sites in them */
1495 /* check if all atoms in dihedral are either virtual sites, or used in
1496 construction of virtual sites. If so, keep it, if not throw away: */
1497 for (int k = 0; (k < 4) && !bKeep; k++) /* for all atoms in the dihedral */
1499 GMX_ASSERT(vsnral != 0,
1500 "If we've seen a vsite before, we know how many constructing atoms it had");
1501 GMX_ASSERT(first_atoms != nullptr,
1502 "If we've seen a vsite before, we know what its first atom index was");
1504 if (vsite_type[atom] == NOTSET)
1506 /* vsnral will be set here, we don't get here with nvsite==0 */
1508 for (int m = 0; (m < vsnral) && !bUsed; m++)
1510 if (atom == first_atoms[m])
1528 dih = ps->interactionTypes.erase(dih);
1532 if (oldSize != gmx::ssize(*ps))
1534 GMX_LOG(logger.info)
1536 .appendTextFormatted("Removed %4zu %15ss with virtual sites, %zu left",
1537 oldSize - ps->size(),
1538 interaction_function[cftype].longname,
1543 // TODO use gmx::compat::optional for pindex.
1544 void clean_vsite_bondeds(gmx::ArrayRef<InteractionsOfType> plist,
1547 const gmx::MDLogger& logger)
1551 std::vector<VsiteAtomMapping> pindex;
1552 std::vector<Atom2VsiteConnection> at2vc;
1554 /* make vsite_type array */
1555 snew(vsite_type, natoms);
1556 for (int i = 0; i < natoms; i++)
1558 vsite_type[i] = NOTSET;
1561 for (int ftype = 0; ftype < F_NRE; ftype++)
1563 if (interaction_function[ftype].flags & IF_VSITE)
1565 nvsite += plist[ftype].size();
1567 while (i < gmx::ssize(plist[ftype]))
1569 vsite = plist[ftype].interactionTypes[i].ai();
1570 if (vsite_type[vsite] == NOTSET)
1572 vsite_type[vsite] = ftype;
1576 gmx_fatal(FARGS, "multiple vsite constructions for atom %d", vsite + 1);
1578 if (ftype == F_VSITEN)
1580 while (i < gmx::ssize(plist[ftype]) && plist[ftype].interactionTypes[i].ai() == vsite)
1593 /* the rest only if we have virtual sites: */
1596 GMX_LOG(logger.info)
1598 .appendTextFormatted("Cleaning up constraints %swith virtual sites",
1599 bRmVSiteBds ? "and constant bonded interactions " : "");
1601 /* Make a reverse list to avoid ninteractions^2 operations */
1602 at2vc = make_at2vsitecon(natoms, plist);
1604 pindex.resize(natoms);
1605 for (int ftype = 0; ftype < F_NRE; ftype++)
1607 /* Here we skip VSITEN. In neary all practical use cases this
1608 * is not an issue, since VSITEN is intended for constructing
1609 * additional interaction sites, not for replacing normal atoms
1610 * with bonded interactions. Thus we do not expect constant
1611 * bonded interactions. If VSITEN does get used with constant
1612 * bonded interactions, these are not removed which only leads
1613 * to very minor extra computation and constant energy.
1614 * The only problematic case is onstraints between VSITEN
1615 * constructions with fixed distance (which is anyhow useless).
1616 * This will generate a fatal error in check_vsite_constraints.
1618 if ((interaction_function[ftype].flags & IF_VSITE) && ftype != F_VSITEN)
1620 for (gmx::index interactionIndex = 0; interactionIndex < gmx::ssize(plist[ftype]);
1623 int k = plist[ftype].interactionTypes[interactionIndex].ai();
1624 pindex[k] = VsiteAtomMapping(ftype, interactionIndex);
1629 /* remove interactions that include virtual sites */
1630 for (int ftype = 0; ftype < F_NRE; ftype++)
1632 if (((interaction_function[ftype].flags & IF_BOND) && bRmVSiteBds)
1633 || (interaction_function[ftype].flags & IF_CONSTRAINT))
1635 if (interaction_function[ftype].flags & (IF_BTYPE | IF_CONSTRAINT))
1637 clean_vsite_bonds(plist, pindex, ftype, vsite_type, logger);
1639 else if (interaction_function[ftype].flags & IF_ATYPE)
1641 clean_vsite_angles(plist, pindex, ftype, vsite_type, at2vc, logger);
1643 else if ((ftype == F_PDIHS) || (ftype == F_IDIHS))
1645 clean_vsite_dihs(plist, pindex, ftype, vsite_type, logger);
1649 /* check that no remaining constraints include virtual sites */
1650 for (int ftype = 0; ftype < F_NRE; ftype++)
1652 if (interaction_function[ftype].flags & IF_CONSTRAINT)
1654 check_vsite_constraints(plist, ftype, vsite_type, logger);