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 by the GROMACS development team.
7 * Copyright (c) 2018,2019,2020, 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.
38 /* This file is completely threadsafe - keep it that way! */
51 #include "gromacs/fileio/confio.h"
52 #include "gromacs/gmxpreprocess/gpp_nextnb.h"
53 #include "gromacs/gmxpreprocess/grompp_impl.h"
54 #include "gromacs/gmxpreprocess/notset.h"
55 #include "gromacs/gmxpreprocess/pgutil.h"
56 #include "gromacs/gmxpreprocess/topio.h"
57 #include "gromacs/gmxpreprocess/toputil.h"
58 #include "gromacs/math/vec.h"
59 #include "gromacs/topology/ifunc.h"
60 #include "gromacs/utility/cstringutil.h"
61 #include "gromacs/utility/fatalerror.h"
62 #include "gromacs/utility/smalloc.h"
64 #include "hackblock.h"
67 #define DIHEDRAL_WAS_SET_IN_RTP 0
68 static bool was_dihedral_set_in_rtp(const InteractionOfType
& dih
)
70 // This is a bad way to check this, but I don't know how to make this better now.
71 gmx::ArrayRef
<const real
> forceParam
= dih
.forceParam();
72 return forceParam
[MAXFORCEPARAM
- 1] == DIHEDRAL_WAS_SET_IN_RTP
;
75 typedef bool (*peq
)(const InteractionOfType
& p1
, const InteractionOfType
& p2
);
77 static bool acomp(const InteractionOfType
& a1
, const InteractionOfType
& a2
)
81 if (((ac
= (a1
.aj() - a2
.aj())) != 0) || ((ac
= (a1
.ai() - a2
.ai())) != 0))
87 return (a1
.ak() < a2
.ak());
91 static bool pcomp(const InteractionOfType
& a1
, const InteractionOfType
& a2
)
95 if ((pc
= (a1
.ai() - a2
.ai())) != 0)
101 return (a1
.aj() < a2
.aj());
105 static bool dcomp(const InteractionOfType
& d1
, const InteractionOfType
& d2
)
109 /* First sort by J & K (the two central) atoms */
110 if (((dc
= (d1
.aj() - d2
.aj())) != 0) || ((dc
= (d1
.ak() - d2
.ak())) != 0))
111 { // NOLINT bugprone-branch-clone
114 /* Then make sure to put rtp dihedrals before generated ones */
115 else if (was_dihedral_set_in_rtp(d1
) && !was_dihedral_set_in_rtp(d2
))
119 else if (!was_dihedral_set_in_rtp(d1
) && was_dihedral_set_in_rtp(d2
))
123 /* Then sort by I and J (two outer) atoms */
124 else if (((dc
= (d1
.ai() - d2
.ai())) != 0) || ((dc
= (d1
.al() - d2
.al())) != 0))
130 // AMBER force fields with type 9 dihedrals can reach here, where we sort on
131 // the contents of the string that names the macro for the parameters.
132 return std::lexicographical_compare(
133 d1
.interactionTypeName().begin(), d1
.interactionTypeName().end(),
134 d2
.interactionTypeName().begin(), d2
.interactionTypeName().end());
139 static bool is_dihedral_on_same_bond(const InteractionOfType
& p1
, const InteractionOfType
& p2
)
141 return ((p1
.aj() == p2
.aj()) && (p1
.ak() == p2
.ak()))
142 || ((p1
.aj() == p2
.ak()) && (p1
.ak() == p2
.aj()));
146 static bool preq(const InteractionOfType
& p1
, const InteractionOfType
& p2
)
148 return (p1
.ai() == p2
.ai()) && (p1
.aj() == p2
.aj());
151 static void rm2par(std::vector
<InteractionOfType
>* p
, peq eq
)
158 for (auto param
= p
->begin() + 1; param
!= p
->end();)
160 auto prev
= param
- 1;
161 if (eq(*param
, *prev
))
163 param
= p
->erase(param
);
172 static void cppar(gmx::ArrayRef
<const InteractionOfType
> types
, gmx::ArrayRef
<InteractionsOfType
> plist
, int ftype
)
175 for (const auto& type
: types
)
177 plist
[ftype
].interactionTypes
.push_back(type
);
181 static bool idcomp(const InteractionOfType
& a
, const InteractionOfType
& b
)
185 if (((d
= (a
.ai() - b
.ai())) != 0) || ((d
= (a
.al() - b
.al())) != 0) || ((d
= (a
.aj() - b
.aj())) != 0))
191 return (a
.ak() < b
.ak());
195 static void sort_id(gmx::ArrayRef
<InteractionOfType
> ps
)
199 for (auto& parm
: ps
)
203 std::sort(ps
.begin(), ps
.end(), idcomp
);
207 static int n_hydro(gmx::ArrayRef
<const int> a
, char*** atomname
)
211 for (auto atom
= a
.begin(); atom
< a
.end(); atom
+= 3)
213 const char* aname
= *atomname
[*atom
];
214 char c0
= toupper(aname
[0]);
219 else if ((static_cast<int>(strlen(aname
)) > 1) && (c0
>= '0') && (c0
<= '9'))
221 char c1
= toupper(aname
[1]);
231 /* Clean up the dihedrals (both generated and read from the .rtp
233 static std::vector
<InteractionOfType
> clean_dih(gmx::ArrayRef
<const InteractionOfType
> dih
,
234 gmx::ArrayRef
<const InteractionOfType
> improper
,
236 bool bKeepAllGeneratedDihedrals
,
237 bool bRemoveDihedralIfWithImproper
)
239 /* Construct the list of the indices of the dihedrals
240 * (i.e. generated or read) that might be kept. */
241 std::vector
<std::pair
<InteractionOfType
, int>> newDihedrals
;
242 if (bKeepAllGeneratedDihedrals
)
244 fprintf(stderr
, "Keeping all generated dihedrals\n");
246 for (const auto& dihedral
: dih
)
248 newDihedrals
.emplace_back(std::pair
<InteractionOfType
, int>(dihedral
, i
++));
253 /* Check if generated dihedral i should be removed. The
254 * dihedrals have been sorted by dcomp() above, so all those
255 * on the same two central atoms are together, with those from
256 * the .rtp file preceding those that were automatically
257 * generated. We remove the latter if the former exist. */
259 for (auto dihedral
= dih
.begin(); dihedral
!= dih
.end(); dihedral
++)
261 /* Keep the dihedrals that were defined in the .rtp file,
262 * and the dihedrals that were generated and different
263 * from the last one (whether it was generated or not). */
264 if (was_dihedral_set_in_rtp(*dihedral
) || dihedral
== dih
.begin()
265 || !is_dihedral_on_same_bond(*dihedral
, *(dihedral
- 1)))
267 newDihedrals
.emplace_back(std::pair
<InteractionOfType
, int>(*dihedral
, i
++));
272 for (auto dihedral
= newDihedrals
.begin(); dihedral
!= newDihedrals
.end();)
274 bool bWasSetInRTP
= was_dihedral_set_in_rtp(dihedral
->first
);
276 if (!bWasSetInRTP
&& bRemoveDihedralIfWithImproper
)
278 /* Remove the dihedral if there is an improper on the same
280 for (auto imp
= improper
.begin(); imp
!= improper
.end() && bKeep
; ++imp
)
282 bKeep
= !is_dihedral_on_same_bond(dihedral
->first
, *imp
);
288 /* If we don't want all dihedrals, we want to select the
289 * ones with the fewest hydrogens. Note that any generated
290 * dihedrals on the same bond as an .rtp dihedral may have
291 * been already pruned above in the construction of
292 * index[]. However, their parameters are still present,
293 * and l is looping over this dihedral and all of its
294 * pruned siblings. */
295 int bestl
= dihedral
->second
;
296 if (!bKeepAllGeneratedDihedrals
&& !bWasSetInRTP
)
298 /* Minimum number of hydrogens for i and l atoms */
300 int next
= dihedral
->second
+ 1;
301 for (int l
= dihedral
->second
;
302 (l
< next
&& is_dihedral_on_same_bond(dihedral
->first
, dih
[l
])); l
++)
304 int nh
= n_hydro(dih
[l
].atoms(), atoms
->atomname
);
315 dihedral
->first
= dih
[bestl
];
325 dihedral
= newDihedrals
.erase(dihedral
);
328 std::vector
<InteractionOfType
> finalDihedrals
;
329 finalDihedrals
.reserve(newDihedrals
.size());
330 for (const auto& param
: newDihedrals
)
332 finalDihedrals
.emplace_back(param
.first
);
334 return finalDihedrals
;
337 static std::vector
<InteractionOfType
> get_impropers(t_atoms
* atoms
,
338 gmx::ArrayRef
<MoleculePatchDatabase
> globalPatches
,
340 gmx::ArrayRef
<const int> cyclicBondsIndex
)
342 std::vector
<InteractionOfType
> improper
;
344 /* Add all the impropers from the residue database to the list. */
346 if (!globalPatches
.empty())
348 for (int i
= 0; (i
< atoms
->nres
); i
++)
350 BondedInteractionList
* impropers
= &globalPatches
[i
].rb
[ebtsIDIHS
];
351 for (const auto& bondeds
: impropers
->b
)
355 for (int k
= 0; (k
< 4) && !bStop
; k
++)
357 const int entry
= search_atom(bondeds
.a
[k
].c_str(), start
, atoms
, "improper",
358 bAllowMissing
, cyclicBondsIndex
);
362 ai
.emplace_back(entry
);
372 improper
.emplace_back(InteractionOfType(ai
, {}, bondeds
.s
));
375 while ((start
< atoms
->nr
) && (atoms
->atom
[start
].resind
== i
))
385 static int nb_dist(t_nextnb
* nnb
, int ai
, int aj
)
397 nrexcl
= nnb
->nrexcl
[ai
];
398 for (nre
= 1; (nre
< nnb
->nrex
); nre
++)
401 for (nrx
= 0; (nrx
< nrexcl
[nre
]); nrx
++)
403 if ((aj
== a
[nrx
]) && (NRE
== -1))
412 static bool is_hydro(t_atoms
* atoms
, int ai
)
414 return ((*(atoms
->atomname
[ai
]))[0] == 'H');
417 static void get_atomnames_min(int n
,
418 gmx::ArrayRef
<std::string
> anm
,
421 gmx::ArrayRef
<const int> a
)
423 /* Assume ascending residue numbering */
424 for (int m
= 0; m
< n
; m
++)
426 if (atoms
->atom
[a
[m
]].resind
< resind
)
430 else if (atoms
->atom
[a
[m
]].resind
> resind
)
438 anm
[m
].append(*(atoms
->atomname
[a
[m
]]));
442 static void gen_excls(t_atoms
* atoms
,
444 gmx::ArrayRef
<MoleculePatchDatabase
> globalPatches
,
446 gmx::ArrayRef
<const int> cyclicBondsIndex
)
449 for (int a
= 0; a
< atoms
->nr
; a
++)
451 int r
= atoms
->atom
[a
].resind
;
452 if (a
== atoms
->nr
- 1 || atoms
->atom
[a
+ 1].resind
!= r
)
454 BondedInteractionList
* hbexcl
= &globalPatches
[r
].rb
[ebtsEXCLS
];
456 for (const auto& bondeds
: hbexcl
->b
)
458 const char* anm
= bondeds
.a
[0].c_str();
459 int i1
= search_atom(anm
, astart
, atoms
, "exclusion", bAllowMissing
, cyclicBondsIndex
);
460 anm
= bondeds
.a
[1].c_str();
461 int i2
= search_atom(anm
, astart
, atoms
, "exclusion", bAllowMissing
, cyclicBondsIndex
);
462 if (i1
!= -1 && i2
!= -1)
470 srenew(excls
[i1
].e
, excls
[i1
].nr
+ 1);
471 excls
[i1
].e
[excls
[i1
].nr
] = i2
;
480 for (int a
= 0; a
< atoms
->nr
; a
++)
484 std::sort(excls
[a
].e
, excls
[a
].e
+ excls
[a
].nr
);
489 static void remove_excl(t_excls
* excls
, int remove
)
493 for (i
= remove
+ 1; i
< excls
->nr
; i
++)
495 excls
->e
[i
- 1] = excls
->e
[i
];
501 static void clean_excls(t_nextnb
* nnb
, int nrexcl
, t_excls excls
[])
503 int i
, j
, j1
, k
, k1
, l
, l1
, e
;
508 /* extract all i-j-k-l neighbours from nnb struct */
509 for (i
= 0; (i
< nnb
->nr
); i
++)
511 /* For all particles */
514 for (j
= 0; (j
< nnb
->nrexcl
[i
][1]); j
++)
516 /* For all first neighbours */
517 j1
= nnb
->a
[i
][1][j
];
519 for (e
= 0; e
< excl
->nr
; e
++)
521 if (excl
->e
[e
] == j1
)
523 remove_excl(excl
, e
);
529 for (k
= 0; (k
< nnb
->nrexcl
[j1
][1]); k
++)
531 /* For all first neighbours of j1 */
532 k1
= nnb
->a
[j1
][1][k
];
534 for (e
= 0; e
< excl
->nr
; e
++)
536 if (excl
->e
[e
] == k1
)
538 remove_excl(excl
, e
);
544 for (l
= 0; (l
< nnb
->nrexcl
[k1
][1]); l
++)
546 /* For all first neighbours of k1 */
547 l1
= nnb
->a
[k1
][1][l
];
549 for (e
= 0; e
< excl
->nr
; e
++)
551 if (excl
->e
[e
] == l1
)
553 remove_excl(excl
, e
);
566 * Generate pairs, angles and dihedrals from .rtp settings
568 * \param[in,out] atoms Global information about atoms in topology.
569 * \param[in] rtpFFDB Residue type database from force field.
570 * \param[in,out] plist Information about listed interactions.
571 * \param[in,out] excls Pair interaction exclusions.
572 * \param[in,out] globalPatches Information about possible residue modifications.
573 * \param[in] bAllowMissing True if missing interaction information is allowed.
574 * AKA allow cartoon physics
575 * \param[in] cyclicBondsIndex Information about bonds creating cyclic molecules.
576 * Empty if no such bonds exist.
578 void gen_pad(t_atoms
* atoms
,
579 gmx::ArrayRef
<const PreprocessResidue
> rtpFFDB
,
580 gmx::ArrayRef
<InteractionsOfType
> plist
,
582 gmx::ArrayRef
<MoleculePatchDatabase
> globalPatches
,
584 gmx::ArrayRef
<const int> cyclicBondsIndex
)
587 init_nnb(&nnb
, atoms
->nr
, 4);
588 gen_nnb(&nnb
, plist
);
589 print_nnb(&nnb
, "NNB");
591 /* These are the angles, dihedrals and pairs that we generate
592 * from the bonds. The ones that are already there from the rtp file
595 std::vector
<InteractionOfType
> ang
;
596 std::vector
<InteractionOfType
> dih
;
597 std::vector
<InteractionOfType
> pai
;
598 std::vector
<InteractionOfType
> improper
;
600 std::array
<std::string
, 4> anm
;
602 if (!globalPatches
.empty())
604 gen_excls(atoms
, excls
, globalPatches
, bAllowMissing
, cyclicBondsIndex
);
605 /* mark all entries as not matched yet */
606 for (int i
= 0; i
< atoms
->nres
; i
++)
608 for (int j
= 0; j
< ebtsNR
; j
++)
610 for (auto& bondeds
: globalPatches
[i
].rb
[j
].b
)
612 bondeds
.match
= false;
618 /* Extract all i-j-k-l neighbours from nnb struct to generate all
619 * angles and dihedrals. */
620 for (int i
= 0; (i
< nnb
.nr
); i
++)
622 /* For all particles */
623 for (int j
= 0; (j
< nnb
.nrexcl
[i
][1]); j
++)
625 /* For all first neighbours */
626 int j1
= nnb
.a
[i
][1][j
];
627 for (int k
= 0; (k
< nnb
.nrexcl
[j1
][1]); k
++)
629 /* For all first neighbours of j1 */
630 int k1
= nnb
.a
[j1
][1][k
];
633 /* Generate every angle only once */
636 std::vector
<int> atomNumbers
= { i
, j1
, k1
};
638 if (!globalPatches
.empty())
640 int minres
= atoms
->atom
[i
].resind
;
642 for (int m
= 1; m
< 3; m
++)
644 minres
= std::min(minres
, atoms
->atom
[atomNumbers
[m
]].resind
);
645 maxres
= std::max(maxres
, atoms
->atom
[atomNumbers
[m
]].resind
);
647 int res
= 2 * minres
- maxres
;
650 res
+= maxres
- minres
;
651 get_atomnames_min(3, anm
, res
, atoms
, atomNumbers
);
652 BondedInteractionList
* hbang
= &globalPatches
[res
].rb
[ebtsANGLES
];
653 for (auto& bondeds
: hbang
->b
)
655 if (anm
[1] == bondeds
.aj())
658 for (int m
= 0; m
< 3; m
+= 2)
661 || ((anm
[m
] == bondeds
.ai())
662 && (anm
[2 - m
] == bondeds
.ak())));
667 /* Mark that we found a match for this entry */
668 bondeds
.match
= true;
672 } while (res
< maxres
);
674 ang
.push_back(InteractionOfType(atomNumbers
, {}, name
));
676 /* Generate every dihedral, 1-4 exclusion and 1-4 interaction
680 for (int l
= 0; (l
< nnb
.nrexcl
[k1
][1]); l
++)
682 /* For all first neighbours of k1 */
683 int l1
= nnb
.a
[k1
][1][l
];
684 if ((l1
!= i
) && (l1
!= j1
))
686 std::vector
<int> atomNumbers
= { i
, j1
, k1
, l1
};
689 if (!globalPatches
.empty())
691 int minres
= atoms
->atom
[i
].resind
;
693 for (int m
= 1; m
< 4; m
++)
695 minres
= std::min(minres
, atoms
->atom
[atomNumbers
[m
]].resind
);
696 maxres
= std::max(maxres
, atoms
->atom
[atomNumbers
[m
]].resind
);
698 int res
= 2 * minres
- maxres
;
701 res
+= maxres
- minres
;
702 get_atomnames_min(4, anm
, res
, atoms
, atomNumbers
);
703 BondedInteractionList
* hbdih
= &globalPatches
[res
].rb
[ebtsPDIHS
];
704 for (auto& bondeds
: hbdih
->b
)
707 for (int m
= 0; m
< 2; m
++)
710 || ((anm
[3 * m
] == bondeds
.ai())
711 && (anm
[1 + m
] == bondeds
.aj())
712 && (anm
[2 - m
] == bondeds
.ak())
713 && (anm
[3 - 3 * m
] == bondeds
.al())));
718 /* Mark that we found a match for this entry */
719 bondeds
.match
= true;
721 /* Set the last parameter to be able to see
722 if the dihedral was in the rtp list.
725 dih
.push_back(InteractionOfType(atomNumbers
, {}, name
));
726 dih
.back().setForceParameter(
727 MAXFORCEPARAM
- 1, DIHEDRAL_WAS_SET_IN_RTP
);
730 } while (res
< maxres
);
734 std::vector
<int> atoms
= { i
, j1
, k1
, l1
};
735 dih
.push_back(InteractionOfType(atoms
, {}, ""));
738 int nbd
= nb_dist(&nnb
, i
, l1
);
741 int i1
= std::min(i
, l1
);
742 int i2
= std::max(i
, l1
);
744 for (int m
= 0; m
< excls
[i1
].nr
; m
++)
746 bExcl
= bExcl
|| excls
[i1
].e
[m
] == i2
;
750 if (rtpFFDB
[0].bGenerateHH14Interactions
751 || !(is_hydro(atoms
, i1
) && is_hydro(atoms
, i2
)))
753 std::vector
<int> atoms
= { i1
, i2
};
754 pai
.push_back(InteractionOfType(atoms
, {}, ""));
766 if (!globalPatches
.empty())
768 /* The above approach is great in that we double-check that e.g. an angle
769 * really corresponds to three atoms connected by bonds, but this is not
770 * generally true. Go through the angle and dihedral hackblocks to add
771 * entries that we have not yet marked as matched when going through bonds.
773 for (int i
= 0; i
< atoms
->nres
; i
++)
775 /* Add remaining angles from hackblock */
776 BondedInteractionList
* hbang
= &globalPatches
[i
].rb
[ebtsANGLES
];
777 for (auto& bondeds
: hbang
->b
)
781 /* We already used this entry, continue to the next */
784 /* Hm - entry not used, let's see if we can find all atoms */
785 std::vector
<int> atomNumbers
;
787 gmx::ArrayRef
<const int>::iterator cyclicBondsIterator
;
788 for (int k
= 0; k
< 3 && bFound
; k
++)
790 const char* p
= bondeds
.a
[k
].c_str();
795 cyclicBondsIterator
=
796 std::find(cyclicBondsIndex
.begin(), cyclicBondsIndex
.end(), res
--);
797 if (cyclicBondsIterator
!= cyclicBondsIndex
.end()
798 && !((cyclicBondsIterator
- cyclicBondsIndex
.begin()) & 1))
800 res
= *(++cyclicBondsIterator
);
803 else if (p
[0] == '+')
806 cyclicBondsIterator
=
807 std::find(cyclicBondsIndex
.begin(), cyclicBondsIndex
.end(), res
++);
808 if (cyclicBondsIterator
!= cyclicBondsIndex
.end()
809 && ((cyclicBondsIterator
- cyclicBondsIndex
.begin()) & 1))
811 res
= *(--cyclicBondsIterator
);
814 atomNumbers
.emplace_back(search_res_atom(p
, res
, atoms
, "angle", TRUE
));
815 bFound
= (atomNumbers
.back() != -1);
820 bondeds
.match
= true;
821 /* Incrementing nang means we save this angle */
822 ang
.push_back(InteractionOfType(atomNumbers
, {}, bondeds
.s
));
826 /* Add remaining dihedrals from hackblock */
827 BondedInteractionList
* hbdih
= &globalPatches
[i
].rb
[ebtsPDIHS
];
828 for (auto& bondeds
: hbdih
->b
)
832 /* We already used this entry, continue to the next */
835 /* Hm - entry not used, let's see if we can find all atoms */
836 std::vector
<int> atomNumbers
;
838 gmx::ArrayRef
<const int>::iterator cyclicBondsIterator
;
839 for (int k
= 0; k
< 4 && bFound
; k
++)
841 const char* p
= bondeds
.a
[k
].c_str();
846 cyclicBondsIterator
=
847 std::find(cyclicBondsIndex
.begin(), cyclicBondsIndex
.end(), res
);
849 if (cyclicBondsIterator
!= cyclicBondsIndex
.end()
850 && !((cyclicBondsIterator
- cyclicBondsIndex
.begin()) & 1))
852 res
= *(++cyclicBondsIterator
);
855 else if (p
[0] == '+')
858 cyclicBondsIterator
=
859 std::find(cyclicBondsIndex
.begin(), cyclicBondsIndex
.end(), res
);
861 if (cyclicBondsIterator
!= cyclicBondsIndex
.end()
862 && ((cyclicBondsIterator
- cyclicBondsIndex
.begin()) & 1))
864 res
= *(--cyclicBondsIterator
);
867 atomNumbers
.emplace_back(search_res_atom(p
, res
, atoms
, "dihedral", TRUE
));
868 bFound
= (atomNumbers
.back() != -1);
873 bondeds
.match
= true;
874 /* Incrementing ndih means we save this dihedral */
875 dih
.push_back(InteractionOfType(atomNumbers
, {}, bondeds
.s
));
881 /* Sort angles with respect to j-i-k (middle atom first) */
884 std::sort(ang
.begin(), ang
.end(), acomp
);
887 /* Sort dihedrals with respect to j-k-i-l (middle atoms first) */
890 std::sort(dih
.begin(), dih
.end(), dcomp
);
896 std::sort(pai
.begin(), pai
.end(), pcomp
);
900 /* Remove doubles, could occur in 6-rings, such as phenyls,
901 maybe one does not want this when fudgeQQ < 1.
903 fprintf(stderr
, "Before cleaning: %zu pairs\n", pai
.size());
907 /* Get the impropers from the database */
908 improper
= get_impropers(atoms
, globalPatches
, bAllowMissing
, cyclicBondsIndex
);
910 /* Sort the impropers */
915 fprintf(stderr
, "Before cleaning: %zu dihedrals\n", dih
.size());
916 dih
= clean_dih(dih
, improper
, atoms
, rtpFFDB
[0].bKeepAllGeneratedDihedrals
,
917 rtpFFDB
[0].bRemoveDihedralIfWithImproper
);
920 /* Now we have unique lists of angles and dihedrals
921 * Copy them into the destination struct
923 cppar(ang
, plist
, F_ANGLES
);
924 cppar(dih
, plist
, F_PDIHS
);
925 cppar(improper
, plist
, F_IDIHS
);
926 cppar(pai
, plist
, F_LJ14
);
928 /* Remove all exclusions which are within nrexcl */
929 clean_excls(&nnb
, rtpFFDB
[0].nrexcl
, excls
);