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.
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"
57 static bool sid_comp(const t_sid
&sa
, const t_sid
&sb
)
61 return sa
.atom
< sb
.atom
;
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
;
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
)
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__
);
101 sid
[aj
+g0
].sid
= sid
[ai
+g0
].sid
;
102 sid
[aj
+g0
].atom
= aj
+g0
;
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.
117 for (i
= fC
; (i
< g
->nnodes
); i
++)
119 if ((g
->nedge
[i
] > 0) && (egc
[i
] == Col
))
128 static int mk_sblocks(FILE *fp
, t_graph
*g
, int maxsid
, t_sid sid
[])
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 */
153 /* We even have a loop invariant:
154 * nW+nG+nB == g->nbound
159 fprintf(fp
, "Walking down the molecule graph to make constraint-blocks\n");
164 /* Find the first white, this will allways be a larger
165 * number than before, because no nodes are made white
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 */
175 range_check(fW
+g0
, 0, maxsid
);
176 sid
[fW
+g0
].sid
= nblock
++;
180 /* Initial value for the first grey */
185 fprintf(debug
, "Starting G loop (nW=%d, nG=%d, nB=%d, total %d)\n",
186 nW
, nG
, nB
, nW
+nG
+nB
);
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
;
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 */
214 fprintf(debug
, "Found %d shake blocks\n", nblock
);
222 int first
, last
, 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
);
231 d
= ma
->first
-mb
->first
;
234 return ma
->last
-mb
->last
;
242 static int merge_sid(int at_start
, int at_end
, int nsid
, t_sid sid
[],
245 int i
, j
, k
, n
, isid
, ndel
;
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 */
256 for (k
= 0; (k
< nsid
); k
++)
258 ms
[k
].first
= at_end
+1;
262 for (i
= at_start
; (i
< at_end
); i
++)
265 range_check(isid
, -1, nsid
);
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 */
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
);
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]));
311 for (k
= at_start
; (k
< at_end
); k
++)
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
);
330 range_check(j
, 0, at_end
);
331 if (sid
[j
].sid
== -1)
337 fprintf(stderr
, "Double sids (%d, %d) for atom %d\n", sid
[j
].sid
, k
, j
);
343 GMX_RELEASE_ASSERT(sblock
->index
[k
] == sblock
->nra
, "Internal inconsistency; sid merge failed");
350 void gen_sblocks(FILE *fp
, int at_start
, int at_end
,
351 const t_idef
*idef
, t_blocka
*sblock
,
359 g
= mk_graph(nullptr, idef
, at_start
, at_end
, TRUE
, bSettle
);
362 p_graph(debug
, "Graaf Dracula", g
);
365 for (i
= at_start
; (i
< at_end
); i
++)
370 nsid
= mk_sblocks(fp
, g
, at_end
, sid
);
377 /* Now sort the shake blocks... */
378 std::sort(sid
+at_start
, sid
+at_end
, sid_comp
);
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)
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
);
405 /* Due to unknown reason this free generates a problem sometimes */
410 fprintf(debug
, "Done gen_sblocks\n");