OCL: Make variables const
[gromacs.git] / src / gromacs / mdlib / splitter.cpp
blob3f53746ab67610061a8aeea9f810c0f681ce50f0
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,2017,2018, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
37 #include "gmxpre.h"
39 #include "splitter.h"
41 #include <cstdlib>
42 #include <cstring>
44 #include <algorithm>
46 #include "gromacs/pbcutil/mshift.h"
47 #include "gromacs/topology/block.h"
48 #include "gromacs/topology/idef.h"
49 #include "gromacs/utility/fatalerror.h"
50 #include "gromacs/utility/gmxassert.h"
51 #include "gromacs/utility/smalloc.h"
53 typedef struct {
54 int atom, sid;
55 } t_sid;
57 static bool sid_comp(const t_sid &sa, const t_sid &sb)
59 if (sa.sid == sb.sid)
61 return sa.atom < sb.atom;
63 else
65 return sa.sid < sb.sid;
69 static int mk_grey(egCol egc[], t_graph *g, int *AtomI,
70 int maxsid, t_sid sid[])
72 int j, ng, ai, aj, g0;
74 ng = 0;
75 ai = *AtomI;
77 g0 = g->at_start;
78 /* Loop over all the bonds */
79 for (j = 0; (j < g->nedge[ai]); j++)
81 aj = g->edge[ai][j]-g0;
82 /* If there is a white one, make it gray and set pbc */
83 if (egc[aj] == egcolWhite)
85 if (aj < *AtomI)
87 *AtomI = aj;
89 egc[aj] = egcolGrey;
91 /* Check whether this one has been set before... */
92 range_check(aj+g0, 0, maxsid);
93 range_check(ai+g0, 0, maxsid);
94 if (sid[aj+g0].sid != -1)
96 gmx_fatal(FARGS, "sid[%d]=%d, sid[%d]=%d, file %s, line %d",
97 ai, sid[ai+g0].sid, aj, sid[aj+g0].sid, __FILE__, __LINE__);
99 else
101 sid[aj+g0].sid = sid[ai+g0].sid;
102 sid[aj+g0].atom = aj+g0;
104 ng++;
107 return ng;
110 static int first_colour(int fC, egCol Col, t_graph *g, const egCol egc[])
111 /* Return the first node with colour Col starting at fC.
112 * return -1 if none found.
115 int i;
117 for (i = fC; (i < g->nnodes); i++)
119 if ((g->nedge[i] > 0) && (egc[i] == Col))
121 return i;
125 return -1;
128 static int mk_sblocks(FILE *fp, t_graph *g, int maxsid, t_sid sid[])
130 int ng, nnodes;
131 int nW, nG, nB; /* Number of Grey, Black, White */
132 int fW, fG; /* First of each category */
133 egCol *egc = nullptr; /* The colour of each node */
134 int g0, nblock;
136 if (!g->nbound)
138 return 0;
141 nblock = 0;
143 nnodes = g->nnodes;
144 snew(egc, nnodes);
146 g0 = g->at_start;
147 nW = g->nbound;
148 nG = 0;
149 nB = 0;
151 fW = 0;
153 /* We even have a loop invariant:
154 * nW+nG+nB == g->nbound
157 if (fp)
159 fprintf(fp, "Walking down the molecule graph to make constraint-blocks\n");
162 while (nW > 0)
164 /* Find the first white, this will allways be a larger
165 * number than before, because no nodes are made white
166 * in the loop
168 if ((fW = first_colour(fW, egcolWhite, g, egc)) == -1)
170 gmx_fatal(FARGS, "No WHITE nodes found while nW=%d\n", nW);
173 /* Make the first white node grey, and set the block number */
174 egc[fW] = egcolGrey;
175 range_check(fW+g0, 0, maxsid);
176 sid[fW+g0].sid = nblock++;
177 nG++;
178 nW--;
180 /* Initial value for the first grey */
181 fG = fW;
183 if (debug)
185 fprintf(debug, "Starting G loop (nW=%d, nG=%d, nB=%d, total %d)\n",
186 nW, nG, nB, nW+nG+nB);
189 while (nG > 0)
191 if ((fG = first_colour(fG, egcolGrey, g, egc)) == -1)
193 gmx_fatal(FARGS, "No GREY nodes found while nG=%d\n", nG);
196 /* Make the first grey node black */
197 egc[fG] = egcolBlack;
198 nB++;
199 nG--;
201 /* Make all the neighbours of this black node grey
202 * and set their block number
204 ng = mk_grey(egc, g, &fG, maxsid, sid);
205 /* ng is the number of white nodes made grey */
206 nG += ng;
207 nW -= ng;
210 sfree(egc);
212 if (debug)
214 fprintf(debug, "Found %d shake blocks\n", nblock);
217 return nblock;
221 typedef struct {
222 int first, last, sid;
223 } t_merge_sid;
225 static int ms_comp(const void *a, const void *b)
227 const t_merge_sid *ma = reinterpret_cast<const t_merge_sid *>(a);
228 const t_merge_sid *mb = reinterpret_cast<const t_merge_sid *>(b);
229 int d;
231 d = ma->first-mb->first;
232 if (d == 0)
234 return ma->last-mb->last;
236 else
238 return d;
242 static int merge_sid(int at_start, int at_end, int nsid, t_sid sid[],
243 t_blocka *sblock)
245 int i, j, k, n, isid, ndel;
246 t_merge_sid *ms;
248 /* We try to remdy the following problem:
249 * Atom: 1 2 3 4 5 6 7 8 9 10
250 * Sid: 0 -1 1 0 -1 1 1 1 1 1
253 /* Determine first and last atom in each shake ID */
254 snew(ms, nsid);
256 for (k = 0; (k < nsid); k++)
258 ms[k].first = at_end+1;
259 ms[k].last = -1;
260 ms[k].sid = k;
262 for (i = at_start; (i < at_end); i++)
264 isid = sid[i].sid;
265 range_check(isid, -1, nsid);
266 if (isid >= 0)
268 ms[isid].first = std::min(ms[isid].first, sid[i].atom);
269 ms[isid].last = std::max(ms[isid].last, sid[i].atom);
272 qsort(ms, nsid, sizeof(ms[0]), ms_comp);
274 /* Now merge the overlapping ones */
275 ndel = 0;
276 for (k = 0; (k < nsid); )
278 for (j = k+1; (j < nsid); )
280 if (ms[j].first <= ms[k].last)
282 ms[k].last = std::max(ms[k].last, ms[j].last);
283 ms[k].first = std::min(ms[k].first, ms[j].first);
284 ms[j].sid = -1;
285 ndel++;
286 j++;
288 else
290 k = j;
291 j = k+1;
294 if (j == nsid)
296 k++;
299 for (k = 0; (k < nsid); k++)
301 while ((k < nsid-1) && (ms[k].sid == -1))
303 for (j = k+1; (j < nsid); j++)
305 std::memcpy(&(ms[j-1]), &(ms[j]), sizeof(ms[0]));
307 nsid--;
311 for (k = at_start; (k < at_end); k++)
313 sid[k].atom = k;
314 sid[k].sid = -1;
316 sblock->nr = nsid;
317 sblock->nalloc_index = sblock->nr+1;
318 snew(sblock->index, sblock->nalloc_index);
319 sblock->nra = at_end - at_start;
320 sblock->nalloc_a = sblock->nra;
321 snew(sblock->a, sblock->nalloc_a);
322 sblock->index[0] = 0;
323 for (k = n = 0; (k < nsid); k++)
325 sblock->index[k+1] = sblock->index[k] + ms[k].last - ms[k].first+1;
326 for (j = ms[k].first; (j <= ms[k].last); j++)
328 range_check(n, 0, sblock->nra);
329 sblock->a[n++] = j;
330 range_check(j, 0, at_end);
331 if (sid[j].sid == -1)
333 sid[j].sid = k;
335 else
337 fprintf(stderr, "Double sids (%d, %d) for atom %d\n", sid[j].sid, k, j);
342 sblock->nra = n;
343 GMX_RELEASE_ASSERT(sblock->index[k] == sblock->nra, "Internal inconsistency; sid merge failed");
345 sfree(ms);
347 return nsid;
350 void gen_sblocks(FILE *fp, int at_start, int at_end,
351 const t_idef *idef, t_blocka *sblock,
352 gmx_bool bSettle)
354 t_graph *g;
355 int i, i0;
356 t_sid *sid;
357 int nsid;
359 g = mk_graph(nullptr, idef, at_start, at_end, TRUE, bSettle);
360 if (debug)
362 p_graph(debug, "Graaf Dracula", g);
364 snew(sid, at_end);
365 for (i = at_start; (i < at_end); i++)
367 sid[i].atom = i;
368 sid[i].sid = -1;
370 nsid = mk_sblocks(fp, g, at_end, sid);
372 if (!nsid)
374 return;
377 /* Now sort the shake blocks... */
378 std::sort(sid+at_start, sid+at_end, sid_comp);
380 if (debug)
382 fprintf(debug, "Sorted shake block\n");
383 for (i = at_start; (i < at_end); i++)
385 fprintf(debug, "sid[%5d] = atom:%5d sid:%5d\n", i, sid[i].atom, sid[i].sid);
388 /* Now check how many are NOT -1, i.e. how many have to be shaken */
389 for (i0 = at_start; (i0 < at_end); i0++)
391 if (sid[i0].sid > -1)
393 break;
397 /* Now we have the sids that have to be shaken. We'll check the min and
398 * max atom numbers and this determines the shake block. DvdS 2007-07-19.
399 * For the purpose of making boundaries all atoms in between need to be
400 * part of the shake block too. There may be cases where blocks overlap
401 * and they will have to be merged.
403 merge_sid(at_start, at_end, nsid, sid, sblock);
404 sfree(sid);
405 /* Due to unknown reason this free generates a problem sometimes */
406 done_graph(g);
407 sfree(g);
408 if (debug)
410 fprintf(debug, "Done gen_sblocks\n");