Separate job script files for gmxapi package versions.
[gromacs.git] / src / gromacs / gmxpreprocess / gen_ad.cpp
blob8c3a4a399808b1d90a9406de745cc172da0d8c71
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017 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! */
39 #include "gmxpre.h"
41 #include "gen_ad.h"
43 #include <cctype>
44 #include <cmath>
45 #include <cstdlib>
46 #include <cstring>
48 #include <algorithm>
49 #include <numeric>
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"
65 #include "resall.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)
79 int ac;
81 if (((ac = (a1.aj() - a2.aj())) != 0) || ((ac = (a1.ai() - a2.ai())) != 0))
83 return ac < 0;
85 else
87 return (a1.ak() < a2.ak());
91 static bool pcomp(const InteractionOfType& a1, const InteractionOfType& a2)
93 int pc;
95 if ((pc = (a1.ai() - a2.ai())) != 0)
97 return pc < 0;
99 else
101 return (a1.aj() < a2.aj());
105 static bool dcomp(const InteractionOfType& d1, const InteractionOfType& d2)
107 int dc;
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
112 return dc < 0;
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))
117 return true;
119 else if (!was_dihedral_set_in_rtp(d1) && was_dihedral_set_in_rtp(d2))
121 return false;
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))
126 return dc < 0;
128 else
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)
153 if (p->empty())
155 return;
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);
165 else
167 ++param;
172 static void cppar(gmx::ArrayRef<const InteractionOfType> types, gmx::ArrayRef<InteractionsOfType> plist, int ftype)
174 /* Keep old stuff */
175 for (const auto& type : types)
177 plist[ftype].interactionTypes.push_back(type);
181 static bool idcomp(const InteractionOfType& a, const InteractionOfType& b)
183 int d;
185 if (((d = (a.ai() - b.ai())) != 0) || ((d = (a.al() - b.al())) != 0) || ((d = (a.aj() - b.aj())) != 0))
187 return d < 0;
189 else
191 return (a.ak() < b.ak());
195 static void sort_id(gmx::ArrayRef<InteractionOfType> ps)
197 if (ps.size() > 1)
199 for (auto& parm : ps)
201 parm.sortAtomIds();
203 std::sort(ps.begin(), ps.end(), idcomp);
207 static int n_hydro(gmx::ArrayRef<const int> a, char*** atomname)
209 int nh = 0;
211 for (auto atom = a.begin(); atom < a.end(); atom += 3)
213 const char* aname = *atomname[*atom];
214 char c0 = toupper(aname[0]);
215 if (c0 == 'H')
217 nh++;
219 else if ((static_cast<int>(strlen(aname)) > 1) && (c0 >= '0') && (c0 <= '9'))
221 char c1 = toupper(aname[1]);
222 if (c1 == 'H')
224 nh++;
228 return nh;
231 /* Clean up the dihedrals (both generated and read from the .rtp
232 * file). */
233 static std::vector<InteractionOfType> clean_dih(gmx::ArrayRef<const InteractionOfType> dih,
234 gmx::ArrayRef<const InteractionOfType> improper,
235 t_atoms* atoms,
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");
245 int i = 0;
246 for (const auto& dihedral : dih)
248 newDihedrals.emplace_back(std::pair<InteractionOfType, int>(dihedral, i++));
251 else
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. */
258 int i = 0;
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++));
271 int k = 0;
272 for (auto dihedral = newDihedrals.begin(); dihedral != newDihedrals.end();)
274 bool bWasSetInRTP = was_dihedral_set_in_rtp(dihedral->first);
275 bool bKeep = true;
276 if (!bWasSetInRTP && bRemoveDihedralIfWithImproper)
278 /* Remove the dihedral if there is an improper on the same
279 * bond. */
280 for (auto imp = improper.begin(); imp != improper.end() && bKeep; ++imp)
282 bKeep = !is_dihedral_on_same_bond(dihedral->first, *imp);
286 if (bKeep)
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 */
299 int minh = 2;
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);
305 if (nh < minh)
307 minh = nh;
308 bestl = l;
310 if (0 == minh)
312 break;
315 dihedral->first = dih[bestl];
317 if (k == bestl)
319 ++dihedral;
321 k++;
323 else
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,
339 bool bAllowMissing,
340 gmx::ArrayRef<const int> cyclicBondsIndex)
342 std::vector<InteractionOfType> improper;
344 /* Add all the impropers from the residue database to the list. */
345 int start = 0;
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)
353 bool bStop = false;
354 std::vector<int> ai;
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);
360 if (entry != -1)
362 ai.emplace_back(entry);
364 else
366 bStop = true;
369 if (!bStop)
371 /* Not broken out */
372 improper.emplace_back(InteractionOfType(ai, {}, bondeds.s));
375 while ((start < atoms->nr) && (atoms->atom[start].resind == i))
377 start++;
382 return improper;
385 static int nb_dist(t_nextnb* nnb, int ai, int aj)
387 int nre, nrx, NRE;
388 int* nrexcl;
389 int* a;
391 if (ai == aj)
393 return 0;
396 NRE = -1;
397 nrexcl = nnb->nrexcl[ai];
398 for (nre = 1; (nre < nnb->nrex); nre++)
400 a = nnb->a[ai][nre];
401 for (nrx = 0; (nrx < nrexcl[nre]); nrx++)
403 if ((aj == a[nrx]) && (NRE == -1))
405 NRE = nre;
409 return NRE;
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,
419 int resind,
420 t_atoms* atoms,
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)
428 anm[m] = "-";
430 else if (atoms->atom[a[m]].resind > resind)
432 anm[m] = "+";
434 else
436 anm[m] = "";
438 anm[m].append(*(atoms->atomname[a[m]]));
442 static void gen_excls(t_atoms* atoms,
443 t_excls* excls,
444 gmx::ArrayRef<MoleculePatchDatabase> globalPatches,
445 bool bAllowMissing,
446 gmx::ArrayRef<const int> cyclicBondsIndex)
448 int astart = 0;
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)
464 if (i1 > i2)
466 int itmp = i1;
467 i1 = i2;
468 i2 = itmp;
470 srenew(excls[i1].e, excls[i1].nr + 1);
471 excls[i1].e[excls[i1].nr] = i2;
472 excls[i1].nr++;
476 astart = a + 1;
480 for (int a = 0; a < atoms->nr; a++)
482 if (excls[a].nr > 1)
484 std::sort(excls[a].e, excls[a].e + excls[a].nr);
489 static void remove_excl(t_excls* excls, int remove)
491 int i;
493 for (i = remove + 1; i < excls->nr; i++)
495 excls->e[i - 1] = excls->e[i];
498 excls->nr--;
501 static void clean_excls(t_nextnb* nnb, int nrexcl, t_excls excls[])
503 int i, j, j1, k, k1, l, l1, e;
504 t_excls* excl;
506 if (nrexcl >= 1)
508 /* extract all i-j-k-l neighbours from nnb struct */
509 for (i = 0; (i < nnb->nr); i++)
511 /* For all particles */
512 excl = &excls[i];
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);
527 if (nrexcl >= 2)
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);
542 if (nrexcl >= 3)
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);
565 /*! \brief
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,
581 t_excls excls[],
582 gmx::ArrayRef<MoleculePatchDatabase> globalPatches,
583 bool bAllowMissing,
584 gmx::ArrayRef<const int> cyclicBondsIndex)
586 t_nextnb nnb;
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
593 * will be retained.
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];
631 if (k1 != i)
633 /* Generate every angle only once */
634 if (i < k1)
636 std::vector<int> atomNumbers = { i, j1, k1 };
637 std::string name;
638 if (!globalPatches.empty())
640 int minres = atoms->atom[i].resind;
641 int maxres = minres;
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())
657 bool bFound = false;
658 for (int m = 0; m < 3; m += 2)
660 bFound = (bFound
661 || ((anm[m] == bondeds.ai())
662 && (anm[2 - m] == bondeds.ak())));
664 if (bFound)
666 name = bondeds.s;
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
677 only once */
678 if (j1 < k1)
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 };
687 std::string name;
688 int nFound = 0;
689 if (!globalPatches.empty())
691 int minres = atoms->atom[i].resind;
692 int maxres = minres;
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)
706 bool bFound = false;
707 for (int m = 0; m < 2; m++)
709 bFound = (bFound
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())));
715 if (bFound)
717 name = bondeds.s;
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.
724 nFound++;
725 dih.push_back(InteractionOfType(atomNumbers, {}, name));
726 dih.back().setForceParameter(
727 MAXFORCEPARAM - 1, DIHEDRAL_WAS_SET_IN_RTP);
730 } while (res < maxres);
732 if (nFound == 0)
734 std::vector<int> atoms = { i, j1, k1, l1 };
735 dih.push_back(InteractionOfType(atoms, {}, ""));
738 int nbd = nb_dist(&nnb, i, l1);
739 if (nbd == 3)
741 int i1 = std::min(i, l1);
742 int i2 = std::max(i, l1);
743 bool bExcl = false;
744 for (int m = 0; m < excls[i1].nr; m++)
746 bExcl = bExcl || excls[i1].e[m] == i2;
748 if (!bExcl)
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)
779 if (bondeds.match)
781 /* We already used this entry, continue to the next */
782 continue;
784 /* Hm - entry not used, let's see if we can find all atoms */
785 std::vector<int> atomNumbers;
786 bool bFound = true;
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();
791 int res = i;
792 if (p[0] == '-')
794 p++;
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] == '+')
805 p++;
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);
818 if (bFound)
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)
830 if (bondeds.match)
832 /* We already used this entry, continue to the next */
833 continue;
835 /* Hm - entry not used, let's see if we can find all atoms */
836 std::vector<int> atomNumbers;
837 bool bFound = true;
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();
842 int res = i;
843 if (p[0] == '-')
845 p++;
846 cyclicBondsIterator =
847 std::find(cyclicBondsIndex.begin(), cyclicBondsIndex.end(), res);
848 res--;
849 if (cyclicBondsIterator != cyclicBondsIndex.end()
850 && !((cyclicBondsIterator - cyclicBondsIndex.begin()) & 1))
852 res = *(++cyclicBondsIterator);
855 else if (p[0] == '+')
857 p++;
858 cyclicBondsIterator =
859 std::find(cyclicBondsIndex.begin(), cyclicBondsIndex.end(), res);
860 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);
871 if (bFound)
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) */
882 if (ang.size() > 1)
884 std::sort(ang.begin(), ang.end(), acomp);
887 /* Sort dihedrals with respect to j-k-i-l (middle atoms first) */
888 if (dih.size() > 1)
890 std::sort(dih.begin(), dih.end(), dcomp);
893 /* Sort the pairs */
894 if (pai.size() > 1)
896 std::sort(pai.begin(), pai.end(), pcomp);
898 if (!pai.empty())
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());
904 rm2par(&pai, preq);
907 /* Get the impropers from the database */
908 improper = get_impropers(atoms, globalPatches, bAllowMissing, cyclicBondsIndex);
910 /* Sort the impropers */
911 sort_id(improper);
913 if (!dih.empty())
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);
930 done_nnb(&nnb);