Improve use of gmxpreprocess headers
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / hackblock.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2011,2014,2015,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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 /* This file is completely threadsafe - keep it that way! */
38 #include "gmxpre.h"
39
40 #include "hackblock.h"
41
42 #include <cstring>
43
44 #include "gromacs/gmxpreprocess/notset.h"
45 #include "gromacs/math/vec.h"
46 #include "gromacs/mdtypes/md_enums.h"
47 #include "gromacs/topology/atoms.h"
48 #include "gromacs/utility/cstringutil.h"
49 #include "gromacs/utility/fatalerror.h"
50 #include "gromacs/utility/smalloc.h"
51
52 /* these MUST correspond to the enum in hackblock.h */
53 const char *btsNames[ebtsNR] = { "bonds", "angles", "dihedrals", "impropers", "exclusions", "cmap" };
54 const int   btsNiatoms[ebtsNR] = { 2,       3,        4,           4,           2,             5 };
55
56 static void free_t_bonded(t_rbonded *rb)
57 {
58     int i;
59
60     for (i = 0; i < MAXATOMLIST; i++)
61     {
62         sfree(rb->a[i]);
63     }
64     sfree(rb->s);
65 }
66
67 static void free_t_bondeds(t_rbondeds *rbs)
68 {
69     int i;
70
71     for (i = 0; i < rbs->nb; i++)
72     {
73         free_t_bonded(&rbs->b[i]);
74     }
75     sfree(rbs->b);
76     rbs->b  = nullptr;
77     rbs->nb = 0;
78 }
79
80 void free_t_restp(int nrtp, t_restp **rtp)
81 {
82     int i, j;
83
84     for (i = 0; i < nrtp; i++)
85     {
86         sfree((*rtp)[i].resname);
87         sfree((*rtp)[i].atom);
88         for (j = 0; j < (*rtp)[i].natom; j++)
89         {
90             sfree(*(*rtp)[i].atomname[j]);
91             sfree((*rtp)[i].atomname[j]);
92         }
93         sfree((*rtp)[i].atomname);
94         sfree((*rtp)[i].cgnr);
95         for (j = 0; j < ebtsNR; j++)
96         {
97             free_t_bondeds(&(*rtp)[i].rb[j]);
98         }
99     }
100     sfree(*rtp);
101 }
102
103 void free_t_hack(int nh, t_hack **h)
104 {
105     int i, j;
106
107     for (i = 0; i < nh; i++)
108     {
109         sfree((*h)[i].oname);
110         sfree((*h)[i].nname);
111         sfree((*h)[i].atom);
112         for (j = 0; j < 4; j++)
113         {
114             sfree((*h)[i].a[j]);
115         }
116     }
117     sfree(*h);
118     *h = nullptr;
119 }
120
121 void free_t_hackblock(int nhb, t_hackblock **hb)
122 {
123     int i, j;
124
125     for (i = 0; i < nhb; i++)
126     {
127         sfree((*hb)[i].name);
128         free_t_hack((*hb)[i].nhack, &(*hb)[i].hack);
129         for (j = 0; j < ebtsNR; j++)
130         {
131             free_t_bondeds(&(*hb)[i].rb[j]);
132         }
133     }
134     sfree(*hb);
135 }
136
137 void clear_t_hackblock(t_hackblock *hb)
138 {
139     int i;
140
141     hb->name    = nullptr;
142     hb->nhack   = 0;
143     hb->maxhack = 0;
144     hb->hack    = nullptr;
145     for (i = 0; i < ebtsNR; i++)
146     {
147         hb->rb[i].nb = 0;
148         hb->rb[i].b  = nullptr;
149     }
150 }
151
152 void clear_t_hack(t_hack *hack)
153 {
154     int i;
155
156     hack->nr    = 0;
157     hack->oname = nullptr;
158     hack->nname = nullptr;
159     hack->atom  = nullptr;
160     hack->cgnr  = NOTSET;
161     hack->tp    = 0;
162     hack->nctl  = 0;
163     for (i = 0; i < 4; i++)
164     {
165         hack->a[i]  = nullptr;
166     }
167     for (i = 0; i < DIM; i++)
168     {
169         hack->newx[i] = NOTSET;
170     }
171 }
172
173 #define safe_strdup(str) (((str) != NULL) ? gmx_strdup(str) : NULL)
174
175 static void copy_t_rbonded(t_rbonded *s, t_rbonded *d)
176 {
177     int i;
178
179     for (i = 0; i < MAXATOMLIST; i++)
180     {
181         d->a[i] = safe_strdup(s->a[i]);
182     }
183     d->s     = safe_strdup(s->s);
184     d->match = s->match;
185 }
186
187 static bool contains_char(t_rbonded *s, char c)
188 {
189     int      i;
190     bool     bRet;
191
192     bRet = FALSE;
193     for (i = 0; i < MAXATOMLIST; i++)
194     {
195         if (s->a[i] && s->a[i][0] == c)
196         {
197             bRet = TRUE;
198         }
199     }
200
201     return bRet;
202 }
203
204 static int
205 rbonded_find_atoms_in_list(t_rbonded *b, t_rbonded blist[], int nlist, int natoms)
206 {
207     int      i, k;
208     int      foundPos = -1;
209     bool     atomsMatch;
210
211     for (i = 0; i < nlist && foundPos < 0; i++)
212     {
213         atomsMatch = TRUE;
214         for (k = 0; k < natoms && atomsMatch; k++)
215         {
216             atomsMatch = atomsMatch && (strcmp(b->a[k], blist[i].a[k]) == 0);
217         }
218         /* Try reverse if forward match did not work */
219         if (!atomsMatch)
220         {
221             atomsMatch = TRUE;
222             for (k = 0; k < natoms && atomsMatch; k++)
223             {
224                 atomsMatch = atomsMatch && (strcmp(b->a[k], blist[i].a[natoms-1-k]) == 0);
225             }
226         }
227         if (atomsMatch)
228         {
229             foundPos = i;
230             /* If all the atoms AND all the parameters match, it is likely that
231              * the user made a copy-and-paste mistake (since it would be much cheaper
232              * to just bump the force constant 2x if you really want it twice).
233              * Since we only have the unparsed string here we can only detect
234              * EXACT matches (including identical whitespace).
235              */
236             if (!strcmp(b->s, blist[i].s))
237             {
238                 gmx_warning("Duplicate line found in or between hackblock and rtp entries");
239             }
240         }
241     }
242     return foundPos;
243 }
244
245 bool merge_t_bondeds(t_rbondeds s[], t_rbondeds d[], bool bMin, bool bPlus)
246 {
247     int      i, j;
248     bool     bBondsRemoved;
249     int      nbHackblockStart;
250     int      index;
251
252     bBondsRemoved = FALSE;
253     for (i = 0; i < ebtsNR; i++)
254     {
255         if (s[i].nb > 0)
256         {
257             /* Record how many bonds we have in the destination when we start.
258              *
259              * If an entry is present in the hackblock (destination), we will
260              * not add the one from the main rtp, since the point is for hackblocks
261              * to overwrite it. However, if there is no hackblock entry we do
262              * allow multiple main rtp entries since some forcefield insist on that.
263              *
264              * We accomplish this by checking the position we find an entry in,
265              * rather than merely checking whether it exists at all.
266              * If that index is larger than the original (hackblock) destination
267              * size, it was added from the main rtp, and then we will allow more
268              * such entries. In contrast, if the entry found has a lower index
269              * it is a hackblock entry meant to override the main rtp, and then
270              * we don't add the main rtp one.
271              */
272             nbHackblockStart = d[i].nb;
273
274             /* make space */
275             srenew(d[i].b, d[i].nb + s[i].nb);
276             for (j = 0; j < s[i].nb; j++)
277             {
278                 /* Check if this bonded string already exists before adding.
279                  * We are merging from the main RTP to the hackblocks, so this
280                  * will mean the hackblocks overwrite the man RTP, as intended.
281                  */
282                 index = rbonded_find_atoms_in_list(&s[i].b[j], d[i].b, d[i].nb, btsNiatoms[i]);
283                 /* - If we did not find this interaction at all, the index will be -1,
284                  *   and then we should definitely add it to the merged hackblock and rtp.
285                  *
286                  * Alternatively, if it was found, index will be >=0.
287                  * - In case this index is lower than the original number of entries,
288                  *   it is already present as a *hackblock* entry, and those should
289                  *   always override whatever we have listed in the RTP. Thus, we
290                  *   should just keep that one and not add anything from the RTP.
291                  * - Finally, if it was found, but with an index higher than
292                  *   the original number of entries, it comes from the RTP rather
293                  *   than hackblock, and then we must have added it ourselves
294                  *   in a previous iteration. In that case it is a matter of
295                  *   several entries for the same sequence of atoms, and we allow
296                  *   that in the RTP. In this case we should simply copy all of
297                  *   them, including this one.
298                  */
299                 if (index < 0 || index >= nbHackblockStart)
300                 {
301                     if (!(bMin && contains_char(&s[i].b[j], '-'))
302                         && !(bPlus && contains_char(&s[i].b[j], '+')))
303                     {
304                         copy_t_rbonded(&s[i].b[j], &d[i].b[ d[i].nb ]);
305                         d[i].nb++;
306                     }
307                     else if (i == ebtsBONDS)
308                     {
309                         bBondsRemoved = TRUE;
310                     }
311                 }
312                 else
313                 {
314                     /* This is the common case where a hackblock entry simply
315                      * overrides the RTP, so we cannot warn here.
316                      */
317                 }
318             }
319         }
320     }
321     return bBondsRemoved;
322 }
323
324 void copy_t_restp(t_restp *s, t_restp *d)
325 {
326     int i;
327
328     *d         = *s;
329     d->resname = safe_strdup(s->resname);
330     snew(d->atom, s->natom);
331     for (i = 0; i < s->natom; i++)
332     {
333         d->atom[i] = s->atom[i];
334     }
335     snew(d->atomname, s->natom);
336     for (i = 0; i < s->natom; i++)
337     {
338         snew(d->atomname[i], 1);
339         *d->atomname[i] = safe_strdup(*s->atomname[i]);
340     }
341     snew(d->cgnr, s->natom);
342     for (i = 0; i < s->natom; i++)
343     {
344         d->cgnr[i] = s->cgnr[i];
345     }
346     for (i = 0; i < ebtsNR; i++)
347     {
348         d->rb[i].type = s->rb[i].type;
349         d->rb[i].nb   = 0;
350         d->rb[i].b    = nullptr;
351     }
352     merge_t_bondeds(s->rb, d->rb, FALSE, FALSE);
353 }
354
355 void copy_t_hack(t_hack *s, t_hack *d)
356 {
357     int i;
358
359     *d       = *s;
360     d->oname = safe_strdup(s->oname);
361     d->nname = safe_strdup(s->nname);
362     if (s->atom)
363     {
364         snew(d->atom, 1);
365         *(d->atom) = *(s->atom);
366     }
367     else
368     {
369         d->atom = nullptr;
370     }
371     for (i = 0; i < 4; i++)
372     {
373         d->a[i] = safe_strdup(s->a[i]);
374     }
375     copy_rvec(s->newx, d->newx);
376 }
377
378 void merge_hacks_lo(int ns, t_hack *s, int *nd, t_hack **d)
379 {
380     int i;
381
382     if (ns)
383     {
384         srenew(*d, *nd + ns);
385         for (i = 0; i < ns; i++)
386         {
387             copy_t_hack(&s[i], &(*d)[*nd + i]);
388         }
389         (*nd) += ns;
390     }
391 }
392
393 void merge_hacks(t_hackblock *s, t_hackblock *d)
394 {
395     merge_hacks_lo(s->nhack, s->hack, &d->nhack, &d->hack);
396 }
397
398 void merge_t_hackblock(t_hackblock *s, t_hackblock *d)
399 {
400     merge_hacks(s, d);
401     merge_t_bondeds(s->rb, d->rb, FALSE, FALSE);
402 }
403
404 void copy_t_hackblock(t_hackblock *s, t_hackblock *d)
405 {
406     int i;
407
408     *d       = *s;
409     d->name  = safe_strdup(s->name);
410     d->nhack = 0;
411     d->hack  = nullptr;
412     for (i = 0; i < ebtsNR; i++)
413     {
414         d->rb[i].nb = 0;
415         d->rb[i].b  = nullptr;
416     }
417     merge_t_hackblock(s, d);
418 }
419
420 #undef safe_strdup
421
422 void dump_hb(FILE *out, int nres, t_hackblock hb[])
423 {
424     int i, j, k, l;
425
426 #define SS(s) (s) ? (s) : "-"
427 #define SA(s) (s) ? "+" : ""
428     fprintf(out, "t_hackblock\n");
429     for (i = 0; i < nres; i++)
430     {
431         fprintf(out, "%3d %4s %2d %2d\n",
432                 i, SS(hb[i].name), hb[i].nhack, hb[i].maxhack);
433         if (hb[i].nhack)
434         {
435             for (j = 0; j < hb[i].nhack; j++)
436             {
437                 fprintf(out, "%d: %d %4s %4s %1s %2d %d %4s %4s %4s %4s\n",
438                         j, hb[i].hack[j].nr,
439                         SS(hb[i].hack[j].oname), SS(hb[i].hack[j].nname),
440                         SA(hb[i].hack[j].atom), hb[i].hack[j].tp, hb[i].hack[j].cgnr,
441                         SS(hb[i].hack[j].ai()), SS(hb[i].hack[j].aj()),
442                         SS(hb[i].hack[j].ak()), SS(hb[i].hack[j].al()) );
443             }
444         }
445         for (j = 0; j < ebtsNR; j++)
446         {
447             if (hb[i].rb[j].nb)
448             {
449                 fprintf(out, " %c %d:", btsNames[j][0], hb[i].rb[j].nb);
450                 for (k = 0; k < hb[i].rb[j].nb; k++)
451                 {
452                     fprintf(out, " [");
453                     for (l = 0; l < btsNiatoms[j]; l++)
454                     {
455                         fprintf(out, " %s", hb[i].rb[j].b[k].a[l]);
456                     }
457                     fprintf(out, " %s]", SS(hb[i].rb[j].b[k].s));
458                 }
459                 fprintf(out, " Entry matched: %s\n", yesno_names[hb[i].rb[j].b[k].match]);
460             }
461         }
462         fprintf(out, "\n");
463     }
464 #undef SS
465 #undef SA
466 }