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.
46 #include "gromacs/math/vec.h"
47 #include "gromacs/pbcutil/pbc.h"
48 #include "gromacs/topology/idef.h"
49 #include "gromacs/topology/ifunc.h"
50 #include "gromacs/topology/topology.h"
51 #include "gromacs/utility/fatalerror.h"
52 #include "gromacs/utility/strconvert.h"
53 #include "gromacs/utility/stringutil.h"
55 /************************************************************
57 * S H I F T U T I L I T I E S
59 ************************************************************/
62 /************************************************************
64 * G R A P H G E N E R A T I O N C O D E
66 ************************************************************/
71 // Class for generating the edges of the graph
75 EdgesGenerator(const int endAtom) : edges_(endAtom) {}
77 // Adds an edge, bi-directional
78 void addEdge(int a0, int a1);
81 const std::vector<std::vector<int>>& edges() const { return edges_; }
84 // The edges stored as list (for each atom starting at \p startAtom_) of lists of atoms
85 std::vector<std::vector<int>> edges_;
88 void EdgesGenerator::addEdge(const int a0, const int a1)
90 const auto& edges = edges_[a0];
91 if (std::find(edges.begin(), edges.end(), a1) == edges.end())
93 edges_[a0].push_back(a1);
94 edges_[a1].push_back(a0);
98 /* Make the actual graph for an ilist, returns whether an edge was added.
100 * When a non-null part array is supplied with part indices for each atom,
101 * edges are only added when atoms have a different part index.
104 static bool mk_igraph(EdgesGenerator* edgesG, int ftype, const T& il, int at_end, ArrayRef<const int> part)
108 bool addedEdge = false;
115 np = interaction_function[ftype].nratoms;
117 if (np > 1 && il.iatoms[i + 1] < at_end)
119 if (il.iatoms[i + np] >= at_end)
122 "Molecule in topology has atom numbers below and "
123 "above natoms (%d).\n"
124 "You are probably trying to use a trajectory which does "
125 "not match the first %d atoms of the run input file.\n"
126 "You can make a matching run input file with gmx convert-tpr.",
130 if (ftype == F_SETTLE)
132 /* Bond all the atoms in the settle */
133 edgesG->addEdge(il.iatoms[i + 1], il.iatoms[i + 2]);
134 edgesG->addEdge(il.iatoms[i + 1], il.iatoms[i + 3]);
137 else if (part.empty())
139 /* Simply add this bond */
140 for (j = 1; j < np; j++)
142 edgesG->addEdge(il.iatoms[i + j], il.iatoms[i + j + 1]);
148 /* Add this bond when it connects two unlinked parts of the graph */
149 for (j = 1; j < np; j++)
151 if (part[il.iatoms[i + j]] != part[il.iatoms[i + j + 1]])
153 edgesG->addEdge(il.iatoms[i + j], il.iatoms[i + j + 1]);
166 [[noreturn]] static void g_error(int line, const char* file)
168 gmx_fatal(FARGS, "Trying to print nonexistent graph (file %s, line %d)", file, line);
172 g_error(__LINE__, __FILE__)
174 void p_graph(FILE* log, const char* title, const t_graph* g)
177 const char* cc[egcolNR] = { "W", "G", "B" };
180 fprintf(log, "graph: %s\n", title);
181 fprintf(log, "nnodes: %d\n", g->numNodes());
182 fprintf(log, "nbound: %d\n", g->numConnectedAtoms);
183 fprintf(log, "start: %d\n", g->edgeAtomBegin);
184 fprintf(log, "end: %d\n", g->edgeAtomEnd);
185 fprintf(log, " atom shiftx shifty shiftz C nedg e1 e2 etc.\n");
186 for (i = 0; i < int(g->edges.size()); i++)
188 if (!g->edges[i].empty())
191 "%5d%7d%7d%7d %1s%5zu",
192 g->edgeAtomBegin + i + 1,
193 g->ishift[g->edgeAtomBegin + i][XX],
194 g->ishift[g->edgeAtomBegin + i][YY],
195 g->ishift[g->edgeAtomBegin + i][ZZ],
196 (!g->edgeColor.empty()) ? cc[g->edgeColor[i]] : " ",
198 for (const int edge : g->edges[i])
200 fprintf(log, " %5d", edge + 1);
208 /* Converts the vector of vector of edges to ListOfLists
209 * and removes leading and trailing uncoupled atoms
211 static gmx::ListOfLists<int> convertGraph(FILE* fplog,
212 const EdgesGenerator& edgesG,
213 int* firstConnectedAtom,
214 int* numConnectedAtoms)
216 gmx::ListOfLists<int> edgesLists;
218 *firstConnectedAtom = edgesG.edges().size();
219 *numConnectedAtoms = 0;
220 int numEmptyEntriesSkipped = 0;
222 for (const auto& edges : edgesG.edges())
226 numEmptyEntriesSkipped++;
230 if (edgesLists.empty())
232 /* We ignore empty entries before the first connected entry */
233 *firstConnectedAtom = numEmptyEntriesSkipped;
237 /* Push any empty entries we skipped */
238 for (int i = 0; i < numEmptyEntriesSkipped; i++)
240 edgesLists.pushBack({});
243 numEmptyEntriesSkipped = 0;
245 edgesLists.pushBack(edges);
247 (*numConnectedAtoms)++;
249 max_nedge = std::max(max_nedge, int(gmx::ssize(edges)));
255 fprintf(fplog, "Max number of graph edges per atom is %d\n", max_nedge);
256 fprintf(fplog, "Total number of graph edges is %d\n", edgesLists.numElements());
262 static gmx_bool determine_graph_parts(const EdgesGenerator& edgesG, ArrayRef<int> partNr)
264 /* Initialize the part array with all entries different */
265 const int endAtom = edgesG.edges().size();
266 for (int at_i = 0; at_i < endAtom; at_i++)
271 /* Loop over the graph until the part array is fixed */
272 bool haveMultipleParts = false;
273 int numAtomsChanged = 0;
276 haveMultipleParts = false;
278 for (gmx::index at_i = 0; at_i < gmx::ssize(edgesG.edges()); at_i++)
280 for (const int at_i2 : edgesG.edges()[at_i])
282 /* Set part for both nodes to the minimum */
283 if (partNr[at_i2] > partNr[at_i])
285 partNr[at_i2] = partNr[at_i];
288 else if (partNr[at_i2] < partNr[at_i])
290 partNr[at_i] = partNr[at_i2];
294 if (partNr[at_i] != partNr[0])
296 haveMultipleParts = true;
302 "graph partNr[] numAtomsChanged=%d, bMultiPart=%s\n",
304 gmx::boolToString(haveMultipleParts));
306 } while (numAtomsChanged > 0);
308 return haveMultipleParts;
312 static t_graph mk_graph_ilist(FILE* fplog, const T* ilist, int at_end, gmx_bool bShakeOnly, gmx_bool bSettle)
314 EdgesGenerator edgesG(at_end);
316 t_graph::BondedParts parts = t_graph::BondedParts::Single;
322 /* First add all the real bonds: they should determine the molecular
325 for (int i = 0; (i < F_NRE); i++)
327 if (interaction_function[i].flags & IF_CHEMBOND)
329 mk_igraph(&edgesG, i, ilist[i], at_end, {});
333 /* Determine of which separated parts the IF_CHEMBOND graph consists.
334 * Store the part numbers in the partNr array.
336 std::vector<int> partNr(at_end);
337 const bool bMultiPart = determine_graph_parts(edgesG, partNr);
341 /* Then add all the other interactions in fixed lists,
342 * but only when they connect parts of the graph
343 * that are not connected through IF_CHEMBOND interactions.
345 bool addedEdge = false;
346 for (int i = 0; (i < F_NRE); i++)
348 if (!(interaction_function[i].flags & IF_CHEMBOND))
350 bool addedEdgeForType = mk_igraph(&edgesG, i, ilist[i], at_end, partNr);
351 addedEdge = (addedEdge || addedEdgeForType);
357 parts = t_graph::BondedParts::MultipleConnected;
361 parts = t_graph::BondedParts::MultipleDisconnected;
367 /* This is a special thing used in splitter.c to generate shake-blocks */
368 mk_igraph(&edgesG, F_CONSTR, ilist[F_CONSTR], at_end, {});
371 mk_igraph(&edgesG, F_SETTLE, ilist[F_SETTLE], at_end, {});
377 /* The naming is somewhat confusing, but we need g->shiftAtomEnd
378 * for shifthing coordinates to a new array (not in place) when
379 * some atoms are not connected by the graph, which runs from
380 * g->edgeAtomBegin (>= 0) to g->edgeAtomEnd (<= g->shiftAtomEnd).
382 graph.shiftAtomEnd = at_end;
383 graph.edgeAtomBegin = 0;
384 graph.edgeAtomEnd = at_end;
388 /* Convert the vector of vector of edges to ListOfLists */
389 graph.edges = convertGraph(fplog, edgesG, &graph.edgeAtomBegin, &graph.numConnectedAtoms);
391 graph.edgeAtomEnd = graph.edgeAtomBegin + graph.edges.ssize();
394 graph.edgeColor.resize(graph.edges.size());
395 graph.ishift.resize(graph.shiftAtomEnd);
399 p_graph(debug, "graph", &graph);
405 t_graph mk_graph_moltype(const gmx_moltype_t& moltype)
407 return mk_graph_ilist(nullptr, moltype.ilist.data(), moltype.atoms.nr, FALSE, FALSE);
410 t_graph mk_graph(const InteractionDefinitions& idef, const int numAtoms)
412 return mk_graph_ilist(nullptr, idef.il.data(), numAtoms, false, false);
415 t_graph* mk_graph(FILE* fplog, const InteractionDefinitions& idef, int at_end, gmx_bool bShakeOnly, gmx_bool bSettle)
417 t_graph* g = new (t_graph);
419 *g = mk_graph_ilist(fplog, idef.il.data(), at_end, bShakeOnly, bSettle);
424 t_graph* mk_graph(FILE* fplog, const t_idef* idef, int at_end, gmx_bool bShakeOnly, gmx_bool bSettle)
426 t_graph* g = new (t_graph);
428 *g = mk_graph_ilist(fplog, idef->il, at_end, bShakeOnly, bSettle);
433 void done_graph(t_graph* g)
438 /************************************************************
440 * S H I F T C A L C U L A T I O N C O D E
442 ************************************************************/
444 static void mk_1shift_tric(int npbcdim,
452 /* Calculate periodicity for triclinic box... */
456 rvec_sub(xi, xj, dx);
459 for (m = npbcdim - 1; (m >= 0); m--)
461 /* If dx < hbox, then xj will be reduced by box, so that
462 * xi - xj will be bigger
464 if (dx[m] < -hbox[m])
467 for (d = m - 1; d >= 0; d--)
472 else if (dx[m] >= hbox[m])
475 for (d = m - 1; d >= 0; d--)
487 static void mk_1shift(int npbcdim, const rvec hbox, const rvec xi, const rvec xj, const int* mi, int* mj)
489 /* Calculate periodicity for rectangular box... */
493 rvec_sub(xi, xj, dx);
496 for (m = 0; (m < npbcdim); m++)
498 /* If dx < hbox, then xj will be reduced by box, so that
499 * xi - xj will be bigger
501 if (dx[m] < -hbox[m])
505 else if (dx[m] >= hbox[m])
516 static void mk_1shift_screw(const matrix box, const rvec hbox, const rvec xi, const rvec xj, const int* mi, int* mj)
518 /* Calculate periodicity for rectangular box... */
522 if ((mi[XX] > 0 && mi[XX] % 2 == 1) || (mi[XX] < 0 && -mi[XX] % 2 == 1))
531 rvec_sub(xi, xj, dx);
533 if (dx[XX] < -hbox[XX])
537 else if (dx[XX] >= hbox[XX])
545 if (mj[XX] != mi[XX])
548 dx[YY] = xi[YY] - (box[YY][YY] + box[ZZ][YY] - xj[YY]);
549 dx[ZZ] = xi[ZZ] - (box[ZZ][ZZ] - xj[ZZ]);
551 for (m = 1; (m < DIM); m++)
553 /* The signs are taken such that we can first shift x and rotate
554 * and then shift y and z.
556 if (dx[m] < -hbox[m])
558 mj[m] = mi[m] - signi;
560 else if (dx[m] >= hbox[m])
562 mj[m] = mi[m] + signi;
571 static int mk_grey(ArrayRef<egCol> edgeColor,
585 for (m = 0; (m < DIM); m++)
587 hbox[m] = box[m][m] * 0.5;
589 bTriclinic = TRICLINIC(box);
591 g0 = g->edgeAtomBegin;
595 /* Loop over all the bonds */
596 for (const int aj : g->edges[ai - g0])
598 /* If there is a white one, make it grey and set pbc */
601 mk_1shift_screw(box, hbox, x[ai], x[aj], g->ishift[ai], is_aj);
605 mk_1shift_tric(npbcdim, box, hbox, x[ai], x[aj], g->ishift[ai], is_aj);
609 mk_1shift(npbcdim, hbox, x[ai], x[aj], g->ishift[ai], is_aj);
612 if (edgeColor[aj - g0] == egcolWhite)
614 if (aj - g0 < *AtomI)
618 edgeColor[aj - g0] = egcolGrey;
620 copy_ivec(is_aj, g->ishift[aj]);
624 else if ((is_aj[XX] != g->ishift[aj][XX]) || (is_aj[YY] != g->ishift[aj][YY])
625 || (is_aj[ZZ] != g->ishift[aj][ZZ]))
629 set_pbc(&pbc, PbcType::Unset, box);
630 pbc_dx(&pbc, x[ai], x[aj], dx);
632 "mk_grey: shifts for atom %d due to atom %d\n"
633 "are (%d,%d,%d), should be (%d,%d,%d)\n"
653 /* Return the first node/atom with colour Col starting at fC.
654 * return -1 if none found.
656 static gmx::index first_colour(const int fC, const egCol Col, const t_graph* g, ArrayRef<const egCol> edgeColor)
658 for (gmx::index i = fC; i < gmx::ssize(g->edges); i++)
660 if (!g->edges[i].empty() && edgeColor[i] == Col)
669 /* Returns the maximum length of the graph edges for coordinates x */
670 static real maxEdgeLength(const t_graph& g, PbcType pbcType, const matrix box, const rvec x[])
674 set_pbc(&pbc, pbcType, box);
676 real maxEdgeLength2 = 0;
678 for (int node = 0; node < int(g.edges.size()); node++)
680 for (const int nodeJ : g.edges[node])
683 pbc_dx(&pbc, x[node], x[nodeJ], dx);
684 maxEdgeLength2 = std::max(maxEdgeLength2, norm2(dx));
688 return std::sqrt(maxEdgeLength2);
691 void mk_mshift(FILE* log, t_graph* g, PbcType pbcType, const matrix box, const rvec x[])
693 static int nerror_tot = 0;
696 int nW, nG, nB; /* Number of Grey, Black, White */
697 int fW, fG; /* First of each category */
700 g->useScrewPbc = (pbcType == PbcType::Screw);
702 if (pbcType == PbcType::XY)
712 /* This puts everything in the central box, that is does not move it
713 * at all. If we return without doing this for a system without bonds
714 * (i.e. only settles) all water molecules are moved to the opposite octant
716 for (i = 0; i < g->shiftAtomEnd; i++)
718 g->ishift[i][XX] = g->ishift[i][YY] = g->ishift[i][ZZ] = 0;
721 if (!g->numConnectedAtoms)
726 std::fill(g->edgeColor.begin(), g->edgeColor.end(), egcolWhite);
728 nW = g->numConnectedAtoms;
734 /* We even have a loop invariant:
735 * nW+nG+nB == g->nbound
738 fprintf(log, "Starting W loop\n");
742 /* Find the first white, this will allways be a larger
743 * number than before, because no nodes are made white
746 if ((fW = first_colour(fW, egcolWhite, g, g->edgeColor)) == -1)
748 gmx_fatal(FARGS, "No WHITE nodes found while nW=%d\n", nW);
751 /* Make the first white node grey */
752 g->edgeColor[fW] = egcolGrey;
756 /* Initial value for the first grey */
759 fprintf(log, "Starting G loop (nW=%d, nG=%d, nB=%d, total %d)\n", nW, nG, nB, nW + nG + nB);
763 if ((fG = first_colour(fG, egcolGrey, g, g->edgeColor)) == -1)
765 gmx_fatal(FARGS, "No GREY nodes found while nG=%d\n", nG);
768 /* Make the first grey node black */
769 g->edgeColor[fG] = egcolBlack;
773 /* Make all the neighbours of this black node grey
774 * and set their periodicity
776 ng = mk_grey(g->edgeColor, g, &fG, npbcdim, box, x, &nerror);
777 /* ng is the number of white nodes made grey */
784 /* We use a threshold of 0.25*boxSize for generating a fatal error
785 * to allow removing PBC for systems with periodic molecules.
787 * TODO: Consider a better solution for systems with periodic
788 * molecules. Ideally analysis tools should only ask to make
789 * non-periodic molecules whole in a system with periodic mols.
791 constexpr real c_relativeDistanceThreshold = 0.25;
793 int npbcdim = numPbcDimensions(pbcType);
794 GMX_RELEASE_ASSERT(npbcdim > 0, "Expect PBC with graph");
795 real minBoxSize = norm(box[XX]);
796 for (int d = 1; d < npbcdim; d++)
798 minBoxSize = std::min(minBoxSize, norm(box[d]));
800 real maxDistance = maxEdgeLength(*g, pbcType, box, x);
801 if (maxDistance >= c_relativeDistanceThreshold * minBoxSize)
803 std::string mesg = gmx::formatString(
804 "There are inconsistent shifts over periodic boundaries in a molecule type "
805 "consisting of %d atoms. The longest distance involved in such interactions is "
806 "%.3f nm which is %s half the box length.",
809 maxDistance >= 0.5 * minBoxSize ? "above" : "close to");
813 case t_graph::BondedParts::MultipleConnected:
814 /* Ideally we should check if the long distances are
815 * actually between the parts, but that would require
816 * a lot of extra code.
818 mesg += " This molecule type consists of muliple parts, e.g. monomers, that "
819 "are connected by interactions that are not chemical bonds, e.g. "
820 "restraints. Such systems can not be treated. The only solution is "
821 "increasing the box size.";
824 mesg += " Either you have excessively large distances between atoms in bonded "
825 "interactions or your system is exploding.";
827 gmx_fatal(FARGS, "%s", mesg.c_str());
830 /* The most likely reason for arriving here is a periodic molecule. */
833 if (nerror_tot <= 100)
835 fprintf(stderr, "There were %d inconsistent shifts. Check your topology\n", nerror);
838 fprintf(log, "There were %d inconsistent shifts. Check your topology\n", nerror);
841 if (nerror_tot == 100)
843 fprintf(stderr, "Will stop reporting inconsistent shifts\n");
846 fprintf(log, "Will stop reporting inconsistent shifts\n");
852 /************************************************************
854 * A C T U A L S H I F T C O D E
856 ************************************************************/
858 void shift_x(const t_graph* g, const matrix box, const rvec x[], rvec x_s[])
863 const int g0 = g->edgeAtomBegin;
864 const int g1 = g->edgeAtomEnd;
865 ArrayRef<const IVec> is = g->ishift;
867 for (j = 0; j < g0; j++)
869 copy_rvec(x[j], x_s[j]);
874 for (j = g0; (j < g1); j++)
880 if ((tx > 0 && tx % 2 == 1) || (tx < 0 && -tx % 2 == 1))
882 x_s[j][XX] = x[j][XX] + tx * box[XX][XX];
883 x_s[j][YY] = box[YY][YY] + box[ZZ][YY] - x[j][YY];
884 x_s[j][ZZ] = box[ZZ][ZZ] - x[j][ZZ];
888 x_s[j][XX] = x[j][XX];
890 x_s[j][YY] = x[j][YY] + ty * box[YY][YY] + tz * box[ZZ][YY];
891 x_s[j][ZZ] = x[j][ZZ] + tz * box[ZZ][ZZ];
894 else if (TRICLINIC(box))
896 for (j = g0; (j < g1); j++)
902 x_s[j][XX] = x[j][XX] + tx * box[XX][XX] + ty * box[YY][XX] + tz * box[ZZ][XX];
903 x_s[j][YY] = x[j][YY] + ty * box[YY][YY] + tz * box[ZZ][YY];
904 x_s[j][ZZ] = x[j][ZZ] + tz * box[ZZ][ZZ];
909 for (j = g0; (j < g1); j++)
915 x_s[j][XX] = x[j][XX] + tx * box[XX][XX];
916 x_s[j][YY] = x[j][YY] + ty * box[YY][YY];
917 x_s[j][ZZ] = x[j][ZZ] + tz * box[ZZ][ZZ];
921 for (j = g1; j < g->shiftAtomEnd; j++)
923 copy_rvec(x[j], x_s[j]);
927 void shift_self(const t_graph& g, const matrix box, rvec x[])
931 GMX_RELEASE_ASSERT(!g.useScrewPbc, "screw pbc not implemented for shift_self");
933 const int g0 = g.edgeAtomBegin;
934 const int g1 = g.edgeAtomEnd;
935 ArrayRef<const IVec> is = g.ishift;
938 fprintf(stderr, "Shifting atoms %d to %d\n", g0, g0 + gn);
942 for (j = g0; (j < g1); j++)
948 x[j][XX] = x[j][XX] + tx * box[XX][XX] + ty * box[YY][XX] + tz * box[ZZ][XX];
949 x[j][YY] = x[j][YY] + ty * box[YY][YY] + tz * box[ZZ][YY];
950 x[j][ZZ] = x[j][ZZ] + tz * box[ZZ][ZZ];
955 for (j = g0; (j < g1); j++)
961 x[j][XX] = x[j][XX] + tx * box[XX][XX];
962 x[j][YY] = x[j][YY] + ty * box[YY][YY];
963 x[j][ZZ] = x[j][ZZ] + tz * box[ZZ][ZZ];
968 void shift_self(const t_graph* g, const matrix box, rvec x[])
970 shift_self(*g, box, x);
973 void unshift_x(const t_graph* g, const matrix box, rvec x[], const rvec x_s[])
979 gmx_incons("screw pbc not implemented (yet) for unshift_x");
982 const int g0 = g->edgeAtomBegin;
983 const int g1 = g->edgeAtomEnd;
984 ArrayRef<const IVec> is = g->ishift;
986 for (j = 0; j < g0; j++)
988 copy_rvec(x_s[j], x[j]);
993 for (j = g0; (j < g1); j++)
999 x[j][XX] = x_s[j][XX] - tx * box[XX][XX] - ty * box[YY][XX] - tz * box[ZZ][XX];
1000 x[j][YY] = x_s[j][YY] - ty * box[YY][YY] - tz * box[ZZ][YY];
1001 x[j][ZZ] = x_s[j][ZZ] - tz * box[ZZ][ZZ];
1006 for (j = g0; (j < g1); j++)
1012 x[j][XX] = x_s[j][XX] - tx * box[XX][XX];
1013 x[j][YY] = x_s[j][YY] - ty * box[YY][YY];
1014 x[j][ZZ] = x_s[j][ZZ] - tz * box[ZZ][ZZ];
1018 for (j = g1; j < g->shiftAtomEnd; j++)
1020 copy_rvec(x_s[j], x[j]);
1024 void unshift_self(const t_graph* g, const matrix box, rvec x[])
1030 gmx_incons("screw pbc not implemented for unshift_self");
1033 const int g0 = g->edgeAtomBegin;
1034 const int g1 = g->edgeAtomEnd;
1035 ArrayRef<const IVec> is = g->ishift;
1039 for (j = g0; (j < g1); j++)
1045 x[j][XX] = x[j][XX] - tx * box[XX][XX] - ty * box[YY][XX] - tz * box[ZZ][XX];
1046 x[j][YY] = x[j][YY] - ty * box[YY][YY] - tz * box[ZZ][YY];
1047 x[j][ZZ] = x[j][ZZ] - tz * box[ZZ][ZZ];
1052 for (j = g0; (j < g1); j++)
1058 x[j][XX] = x[j][XX] - tx * box[XX][XX];
1059 x[j][YY] = x[j][YY] - ty * box[YY][YY];
1060 x[j][ZZ] = x[j][ZZ] - tz * box[ZZ][ZZ];