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.",
129 if (ftype
== F_SETTLE
)
131 /* Bond all the atoms in the settle */
132 edgesG
->addEdge(il
.iatoms
[i
+ 1], il
.iatoms
[i
+ 2]);
133 edgesG
->addEdge(il
.iatoms
[i
+ 1], il
.iatoms
[i
+ 3]);
136 else if (part
.empty())
138 /* Simply add this bond */
139 for (j
= 1; j
< np
; j
++)
141 edgesG
->addEdge(il
.iatoms
[i
+ j
], il
.iatoms
[i
+ j
+ 1]);
147 /* Add this bond when it connects two unlinked parts of the graph */
148 for (j
= 1; j
< np
; j
++)
150 if (part
[il
.iatoms
[i
+ j
]] != part
[il
.iatoms
[i
+ j
+ 1]])
152 edgesG
->addEdge(il
.iatoms
[i
+ j
], il
.iatoms
[i
+ j
+ 1]);
165 [[noreturn
]] static void g_error(int line
, const char* file
)
167 gmx_fatal(FARGS
, "Trying to print nonexistent graph (file %s, line %d)", file
, line
);
171 g_error(__LINE__, __FILE__)
173 void p_graph(FILE* log
, const char* title
, const t_graph
* g
)
176 const char* cc
[egcolNR
] = { "W", "G", "B" };
179 fprintf(log
, "graph: %s\n", title
);
180 fprintf(log
, "nnodes: %d\n", g
->numNodes());
181 fprintf(log
, "nbound: %d\n", g
->numConnectedAtoms
);
182 fprintf(log
, "start: %d\n", g
->edgeAtomBegin
);
183 fprintf(log
, "end: %d\n", g
->edgeAtomEnd
);
184 fprintf(log
, " atom shiftx shifty shiftz C nedg e1 e2 etc.\n");
185 for (i
= 0; i
< int(g
->edges
.size()); i
++)
187 if (!g
->edges
[i
].empty())
189 fprintf(log
, "%5d%7d%7d%7d %1s%5zu", g
->edgeAtomBegin
+ i
+ 1,
190 g
->ishift
[g
->edgeAtomBegin
+ i
][XX
], g
->ishift
[g
->edgeAtomBegin
+ i
][YY
],
191 g
->ishift
[g
->edgeAtomBegin
+ i
][ZZ
],
192 (!g
->edgeColor
.empty()) ? cc
[g
->edgeColor
[i
]] : " ", g
->edges
[i
].size());
193 for (const int edge
: g
->edges
[i
])
195 fprintf(log
, " %5d", edge
+ 1);
203 /* Converts the vector of vector of edges to ListOfLists
204 * and removes leading and trailing uncoupled atoms
206 static gmx::ListOfLists
<int> convertGraph(FILE* fplog
,
207 const EdgesGenerator
& edgesG
,
208 int* firstConnectedAtom
,
209 int* numConnectedAtoms
)
211 gmx::ListOfLists
<int> edgesLists
;
213 *firstConnectedAtom
= edgesG
.edges().size();
214 *numConnectedAtoms
= 0;
215 int numEmptyEntriesSkipped
= 0;
217 for (const auto& edges
: edgesG
.edges())
221 numEmptyEntriesSkipped
++;
225 if (edgesLists
.empty())
227 /* We ignore empty entries before the first connected entry */
228 *firstConnectedAtom
= numEmptyEntriesSkipped
;
232 /* Push any empty entries we skipped */
233 for (int i
= 0; i
< numEmptyEntriesSkipped
; i
++)
235 edgesLists
.pushBack({});
238 numEmptyEntriesSkipped
= 0;
240 edgesLists
.pushBack(edges
);
242 (*numConnectedAtoms
)++;
244 max_nedge
= std::max(max_nedge
, int(gmx::ssize(edges
)));
250 fprintf(fplog
, "Max number of graph edges per atom is %d\n", max_nedge
);
251 fprintf(fplog
, "Total number of graph edges is %d\n", edgesLists
.numElements());
257 static gmx_bool
determine_graph_parts(const EdgesGenerator
& edgesG
, ArrayRef
<int> partNr
)
259 /* Initialize the part array with all entries different */
260 const int endAtom
= edgesG
.edges().size();
261 for (int at_i
= 0; at_i
< endAtom
; at_i
++)
266 /* Loop over the graph until the part array is fixed */
267 bool haveMultipleParts
= false;
268 int numAtomsChanged
= 0;
271 haveMultipleParts
= false;
273 for (gmx::index at_i
= 0; at_i
< gmx::ssize(edgesG
.edges()); at_i
++)
275 for (const int at_i2
: edgesG
.edges()[at_i
])
277 /* Set part for both nodes to the minimum */
278 if (partNr
[at_i2
] > partNr
[at_i
])
280 partNr
[at_i2
] = partNr
[at_i
];
283 else if (partNr
[at_i2
] < partNr
[at_i
])
285 partNr
[at_i
] = partNr
[at_i2
];
289 if (partNr
[at_i
] != partNr
[0])
291 haveMultipleParts
= true;
296 fprintf(debug
, "graph partNr[] numAtomsChanged=%d, bMultiPart=%s\n", numAtomsChanged
,
297 gmx::boolToString(haveMultipleParts
));
299 } while (numAtomsChanged
> 0);
301 return haveMultipleParts
;
305 static t_graph
mk_graph_ilist(FILE* fplog
, const T
* ilist
, int at_end
, gmx_bool bShakeOnly
, gmx_bool bSettle
)
307 EdgesGenerator
edgesG(at_end
);
309 t_graph::BondedParts parts
= t_graph::BondedParts::Single
;
315 /* First add all the real bonds: they should determine the molecular
318 for (int i
= 0; (i
< F_NRE
); i
++)
320 if (interaction_function
[i
].flags
& IF_CHEMBOND
)
322 mk_igraph(&edgesG
, i
, ilist
[i
], at_end
, {});
326 /* Determine of which separated parts the IF_CHEMBOND graph consists.
327 * Store the part numbers in the partNr array.
329 std::vector
<int> partNr(at_end
);
330 const bool bMultiPart
= determine_graph_parts(edgesG
, partNr
);
334 /* Then add all the other interactions in fixed lists,
335 * but only when they connect parts of the graph
336 * that are not connected through IF_CHEMBOND interactions.
338 bool addedEdge
= false;
339 for (int i
= 0; (i
< F_NRE
); i
++)
341 if (!(interaction_function
[i
].flags
& IF_CHEMBOND
))
343 bool addedEdgeForType
= mk_igraph(&edgesG
, i
, ilist
[i
], at_end
, partNr
);
344 addedEdge
= (addedEdge
|| addedEdgeForType
);
350 parts
= t_graph::BondedParts::MultipleConnected
;
354 parts
= t_graph::BondedParts::MultipleDisconnected
;
360 /* This is a special thing used in splitter.c to generate shake-blocks */
361 mk_igraph(&edgesG
, F_CONSTR
, ilist
[F_CONSTR
], at_end
, {});
364 mk_igraph(&edgesG
, F_SETTLE
, ilist
[F_SETTLE
], at_end
, {});
370 /* The naming is somewhat confusing, but we need g->shiftAtomEnd
371 * for shifthing coordinates to a new array (not in place) when
372 * some atoms are not connected by the graph, which runs from
373 * g->edgeAtomBegin (>= 0) to g->edgeAtomEnd (<= g->shiftAtomEnd).
375 graph
.shiftAtomEnd
= at_end
;
376 graph
.edgeAtomBegin
= 0;
377 graph
.edgeAtomEnd
= at_end
;
381 /* Convert the vector of vector of edges to ListOfLists */
382 graph
.edges
= convertGraph(fplog
, edgesG
, &graph
.edgeAtomBegin
, &graph
.numConnectedAtoms
);
384 graph
.edgeAtomEnd
= graph
.edgeAtomBegin
+ graph
.edges
.ssize();
387 graph
.edgeColor
.resize(graph
.edges
.size());
388 graph
.ishift
.resize(graph
.shiftAtomEnd
);
392 p_graph(debug
, "graph", &graph
);
398 t_graph
mk_graph_moltype(const gmx_moltype_t
& moltype
)
400 return mk_graph_ilist(nullptr, moltype
.ilist
.data(), moltype
.atoms
.nr
, FALSE
, FALSE
);
403 t_graph
mk_graph(const InteractionDefinitions
& idef
, const int numAtoms
)
405 return mk_graph_ilist(nullptr, idef
.il
.data(), numAtoms
, false, false);
408 t_graph
* mk_graph(FILE* fplog
, const InteractionDefinitions
& idef
, int at_end
, gmx_bool bShakeOnly
, gmx_bool bSettle
)
410 t_graph
* g
= new (t_graph
);
412 *g
= mk_graph_ilist(fplog
, idef
.il
.data(), at_end
, bShakeOnly
, bSettle
);
417 t_graph
* mk_graph(FILE* fplog
, const t_idef
* idef
, int at_end
, gmx_bool bShakeOnly
, gmx_bool bSettle
)
419 t_graph
* g
= new (t_graph
);
421 *g
= mk_graph_ilist(fplog
, idef
->il
, at_end
, bShakeOnly
, bSettle
);
426 void done_graph(t_graph
* g
)
431 /************************************************************
433 * S H I F T C A L C U L A T I O N C O D E
435 ************************************************************/
437 static void mk_1shift_tric(int npbcdim
,
445 /* Calculate periodicity for triclinic box... */
449 rvec_sub(xi
, xj
, dx
);
452 for (m
= npbcdim
- 1; (m
>= 0); m
--)
454 /* If dx < hbox, then xj will be reduced by box, so that
455 * xi - xj will be bigger
457 if (dx
[m
] < -hbox
[m
])
460 for (d
= m
- 1; d
>= 0; d
--)
465 else if (dx
[m
] >= hbox
[m
])
468 for (d
= m
- 1; d
>= 0; d
--)
480 static void mk_1shift(int npbcdim
, const rvec hbox
, const rvec xi
, const rvec xj
, const int* mi
, int* mj
)
482 /* Calculate periodicity for rectangular box... */
486 rvec_sub(xi
, xj
, dx
);
489 for (m
= 0; (m
< npbcdim
); m
++)
491 /* If dx < hbox, then xj will be reduced by box, so that
492 * xi - xj will be bigger
494 if (dx
[m
] < -hbox
[m
])
498 else if (dx
[m
] >= hbox
[m
])
509 static void mk_1shift_screw(const matrix box
, const rvec hbox
, const rvec xi
, const rvec xj
, const int* mi
, int* mj
)
511 /* Calculate periodicity for rectangular box... */
515 if ((mi
[XX
] > 0 && mi
[XX
] % 2 == 1) || (mi
[XX
] < 0 && -mi
[XX
] % 2 == 1))
524 rvec_sub(xi
, xj
, dx
);
526 if (dx
[XX
] < -hbox
[XX
])
530 else if (dx
[XX
] >= hbox
[XX
])
538 if (mj
[XX
] != mi
[XX
])
541 dx
[YY
] = xi
[YY
] - (box
[YY
][YY
] + box
[ZZ
][YY
] - xj
[YY
]);
542 dx
[ZZ
] = xi
[ZZ
] - (box
[ZZ
][ZZ
] - xj
[ZZ
]);
544 for (m
= 1; (m
< DIM
); m
++)
546 /* The signs are taken such that we can first shift x and rotate
547 * and then shift y and z.
549 if (dx
[m
] < -hbox
[m
])
551 mj
[m
] = mi
[m
] - signi
;
553 else if (dx
[m
] >= hbox
[m
])
555 mj
[m
] = mi
[m
] + signi
;
564 static int mk_grey(ArrayRef
<egCol
> edgeColor
,
578 for (m
= 0; (m
< DIM
); m
++)
580 hbox
[m
] = box
[m
][m
] * 0.5;
582 bTriclinic
= TRICLINIC(box
);
584 g0
= g
->edgeAtomBegin
;
588 /* Loop over all the bonds */
589 for (const int aj
: g
->edges
[ai
- g0
])
591 /* If there is a white one, make it grey and set pbc */
594 mk_1shift_screw(box
, hbox
, x
[ai
], x
[aj
], g
->ishift
[ai
], is_aj
);
598 mk_1shift_tric(npbcdim
, box
, hbox
, x
[ai
], x
[aj
], g
->ishift
[ai
], is_aj
);
602 mk_1shift(npbcdim
, hbox
, x
[ai
], x
[aj
], g
->ishift
[ai
], is_aj
);
605 if (edgeColor
[aj
- g0
] == egcolWhite
)
607 if (aj
- g0
< *AtomI
)
611 edgeColor
[aj
- g0
] = egcolGrey
;
613 copy_ivec(is_aj
, g
->ishift
[aj
]);
617 else if ((is_aj
[XX
] != g
->ishift
[aj
][XX
]) || (is_aj
[YY
] != g
->ishift
[aj
][YY
])
618 || (is_aj
[ZZ
] != g
->ishift
[aj
][ZZ
]))
622 set_pbc(&pbc
, PbcType::Unset
, box
);
623 pbc_dx(&pbc
, x
[ai
], x
[aj
], dx
);
625 "mk_grey: shifts for atom %d due to atom %d\n"
626 "are (%d,%d,%d), should be (%d,%d,%d)\n"
628 aj
+ 1, ai
+ 1, is_aj
[XX
], is_aj
[YY
], is_aj
[ZZ
], g
->ishift
[aj
][XX
],
629 g
->ishift
[aj
][YY
], g
->ishift
[aj
][ZZ
], dx
[XX
], dx
[YY
], dx
[ZZ
]);
637 /* Return the first node/atom with colour Col starting at fC.
638 * return -1 if none found.
640 static gmx::index
first_colour(const int fC
, const egCol Col
, const t_graph
* g
, ArrayRef
<const egCol
> edgeColor
)
642 for (gmx::index i
= fC
; i
< gmx::ssize(g
->edges
); i
++)
644 if (!g
->edges
[i
].empty() && edgeColor
[i
] == Col
)
653 /* Returns the maximum length of the graph edges for coordinates x */
654 static real
maxEdgeLength(const t_graph
& g
, PbcType pbcType
, const matrix box
, const rvec x
[])
658 set_pbc(&pbc
, pbcType
, box
);
660 real maxEdgeLength2
= 0;
662 for (int node
= 0; node
< int(g
.edges
.size()); node
++)
664 for (const int nodeJ
: g
.edges
[node
])
667 pbc_dx(&pbc
, x
[node
], x
[nodeJ
], dx
);
668 maxEdgeLength2
= std::max(maxEdgeLength2
, norm2(dx
));
672 return std::sqrt(maxEdgeLength2
);
675 void mk_mshift(FILE* log
, t_graph
* g
, PbcType pbcType
, const matrix box
, const rvec x
[])
677 static int nerror_tot
= 0;
680 int nW
, nG
, nB
; /* Number of Grey, Black, White */
681 int fW
, fG
; /* First of each category */
684 g
->useScrewPbc
= (pbcType
== PbcType::Screw
);
686 if (pbcType
== PbcType::XY
)
696 /* This puts everything in the central box, that is does not move it
697 * at all. If we return without doing this for a system without bonds
698 * (i.e. only settles) all water molecules are moved to the opposite octant
700 for (i
= 0; i
< g
->shiftAtomEnd
; i
++)
702 g
->ishift
[i
][XX
] = g
->ishift
[i
][YY
] = g
->ishift
[i
][ZZ
] = 0;
705 if (!g
->numConnectedAtoms
)
710 std::fill(g
->edgeColor
.begin(), g
->edgeColor
.end(), egcolWhite
);
712 nW
= g
->numConnectedAtoms
;
718 /* We even have a loop invariant:
719 * nW+nG+nB == g->nbound
722 fprintf(log
, "Starting W loop\n");
726 /* Find the first white, this will allways be a larger
727 * number than before, because no nodes are made white
730 if ((fW
= first_colour(fW
, egcolWhite
, g
, g
->edgeColor
)) == -1)
732 gmx_fatal(FARGS
, "No WHITE nodes found while nW=%d\n", nW
);
735 /* Make the first white node grey */
736 g
->edgeColor
[fW
] = egcolGrey
;
740 /* Initial value for the first grey */
743 fprintf(log
, "Starting G loop (nW=%d, nG=%d, nB=%d, total %d)\n", nW
, nG
, nB
, nW
+ nG
+ nB
);
747 if ((fG
= first_colour(fG
, egcolGrey
, g
, g
->edgeColor
)) == -1)
749 gmx_fatal(FARGS
, "No GREY nodes found while nG=%d\n", nG
);
752 /* Make the first grey node black */
753 g
->edgeColor
[fG
] = egcolBlack
;
757 /* Make all the neighbours of this black node grey
758 * and set their periodicity
760 ng
= mk_grey(g
->edgeColor
, g
, &fG
, npbcdim
, box
, x
, &nerror
);
761 /* ng is the number of white nodes made grey */
768 /* We use a threshold of 0.25*boxSize for generating a fatal error
769 * to allow removing PBC for systems with periodic molecules.
771 * TODO: Consider a better solution for systems with periodic
772 * molecules. Ideally analysis tools should only ask to make
773 * non-periodic molecules whole in a system with periodic mols.
775 constexpr real c_relativeDistanceThreshold
= 0.25;
777 int npbcdim
= numPbcDimensions(pbcType
);
778 GMX_RELEASE_ASSERT(npbcdim
> 0, "Expect PBC with graph");
779 real minBoxSize
= norm(box
[XX
]);
780 for (int d
= 1; d
< npbcdim
; d
++)
782 minBoxSize
= std::min(minBoxSize
, norm(box
[d
]));
784 real maxDistance
= maxEdgeLength(*g
, pbcType
, box
, x
);
785 if (maxDistance
>= c_relativeDistanceThreshold
* minBoxSize
)
787 std::string mesg
= gmx::formatString(
788 "There are inconsistent shifts over periodic boundaries in a molecule type "
789 "consisting of %d atoms. The longest distance involved in such interactions is "
790 "%.3f nm which is %s half the box length.",
791 g
->shiftAtomEnd
, maxDistance
, maxDistance
>= 0.5 * minBoxSize
? "above" : "close to");
795 case t_graph::BondedParts::MultipleConnected
:
796 /* Ideally we should check if the long distances are
797 * actually between the parts, but that would require
798 * a lot of extra code.
800 mesg
+= " This molecule type consists of muliple parts, e.g. monomers, that "
801 "are connected by interactions that are not chemical bonds, e.g. "
802 "restraints. Such systems can not be treated. The only solution is "
803 "increasing the box size.";
806 mesg
+= " Either you have excessively large distances between atoms in bonded "
807 "interactions or your system is exploding.";
809 gmx_fatal(FARGS
, "%s", mesg
.c_str());
812 /* The most likely reason for arriving here is a periodic molecule. */
815 if (nerror_tot
<= 100)
817 fprintf(stderr
, "There were %d inconsistent shifts. Check your topology\n", nerror
);
820 fprintf(log
, "There were %d inconsistent shifts. Check your topology\n", nerror
);
823 if (nerror_tot
== 100)
825 fprintf(stderr
, "Will stop reporting inconsistent shifts\n");
828 fprintf(log
, "Will stop reporting inconsistent shifts\n");
834 /************************************************************
836 * A C T U A L S H I F T C O D E
838 ************************************************************/
840 void shift_x(const t_graph
* g
, const matrix box
, const rvec x
[], rvec x_s
[])
845 const int g0
= g
->edgeAtomBegin
;
846 const int g1
= g
->edgeAtomEnd
;
847 ArrayRef
<const IVec
> is
= g
->ishift
;
849 for (j
= 0; j
< g0
; j
++)
851 copy_rvec(x
[j
], x_s
[j
]);
856 for (j
= g0
; (j
< g1
); j
++)
862 if ((tx
> 0 && tx
% 2 == 1) || (tx
< 0 && -tx
% 2 == 1))
864 x_s
[j
][XX
] = x
[j
][XX
] + tx
* box
[XX
][XX
];
865 x_s
[j
][YY
] = box
[YY
][YY
] + box
[ZZ
][YY
] - x
[j
][YY
];
866 x_s
[j
][ZZ
] = box
[ZZ
][ZZ
] - x
[j
][ZZ
];
870 x_s
[j
][XX
] = x
[j
][XX
];
872 x_s
[j
][YY
] = x
[j
][YY
] + ty
* box
[YY
][YY
] + tz
* box
[ZZ
][YY
];
873 x_s
[j
][ZZ
] = x
[j
][ZZ
] + tz
* box
[ZZ
][ZZ
];
876 else if (TRICLINIC(box
))
878 for (j
= g0
; (j
< g1
); j
++)
884 x_s
[j
][XX
] = x
[j
][XX
] + tx
* box
[XX
][XX
] + ty
* box
[YY
][XX
] + tz
* box
[ZZ
][XX
];
885 x_s
[j
][YY
] = x
[j
][YY
] + ty
* box
[YY
][YY
] + tz
* box
[ZZ
][YY
];
886 x_s
[j
][ZZ
] = x
[j
][ZZ
] + tz
* box
[ZZ
][ZZ
];
891 for (j
= g0
; (j
< g1
); j
++)
897 x_s
[j
][XX
] = x
[j
][XX
] + tx
* box
[XX
][XX
];
898 x_s
[j
][YY
] = x
[j
][YY
] + ty
* box
[YY
][YY
];
899 x_s
[j
][ZZ
] = x
[j
][ZZ
] + tz
* box
[ZZ
][ZZ
];
903 for (j
= g1
; j
< g
->shiftAtomEnd
; j
++)
905 copy_rvec(x
[j
], x_s
[j
]);
909 void shift_self(const t_graph
& g
, const matrix box
, rvec x
[])
913 GMX_RELEASE_ASSERT(!g
.useScrewPbc
, "screw pbc not implemented for shift_self");
915 const int g0
= g
.edgeAtomBegin
;
916 const int g1
= g
.edgeAtomEnd
;
917 ArrayRef
<const IVec
> is
= g
.ishift
;
920 fprintf(stderr
, "Shifting atoms %d to %d\n", g0
, g0
+ gn
);
924 for (j
= g0
; (j
< g1
); j
++)
930 x
[j
][XX
] = x
[j
][XX
] + tx
* box
[XX
][XX
] + ty
* box
[YY
][XX
] + tz
* box
[ZZ
][XX
];
931 x
[j
][YY
] = x
[j
][YY
] + ty
* box
[YY
][YY
] + tz
* box
[ZZ
][YY
];
932 x
[j
][ZZ
] = x
[j
][ZZ
] + tz
* box
[ZZ
][ZZ
];
937 for (j
= g0
; (j
< g1
); j
++)
943 x
[j
][XX
] = x
[j
][XX
] + tx
* box
[XX
][XX
];
944 x
[j
][YY
] = x
[j
][YY
] + ty
* box
[YY
][YY
];
945 x
[j
][ZZ
] = x
[j
][ZZ
] + tz
* box
[ZZ
][ZZ
];
950 void shift_self(const t_graph
* g
, const matrix box
, rvec x
[])
952 shift_self(*g
, box
, x
);
955 void unshift_x(const t_graph
* g
, const matrix box
, rvec x
[], const rvec x_s
[])
961 gmx_incons("screw pbc not implemented (yet) for unshift_x");
964 const int g0
= g
->edgeAtomBegin
;
965 const int g1
= g
->edgeAtomEnd
;
966 ArrayRef
<const IVec
> is
= g
->ishift
;
968 for (j
= 0; j
< g0
; j
++)
970 copy_rvec(x_s
[j
], x
[j
]);
975 for (j
= g0
; (j
< g1
); j
++)
981 x
[j
][XX
] = x_s
[j
][XX
] - tx
* box
[XX
][XX
] - ty
* box
[YY
][XX
] - tz
* box
[ZZ
][XX
];
982 x
[j
][YY
] = x_s
[j
][YY
] - ty
* box
[YY
][YY
] - tz
* box
[ZZ
][YY
];
983 x
[j
][ZZ
] = x_s
[j
][ZZ
] - tz
* box
[ZZ
][ZZ
];
988 for (j
= g0
; (j
< g1
); j
++)
994 x
[j
][XX
] = x_s
[j
][XX
] - tx
* box
[XX
][XX
];
995 x
[j
][YY
] = x_s
[j
][YY
] - ty
* box
[YY
][YY
];
996 x
[j
][ZZ
] = x_s
[j
][ZZ
] - tz
* box
[ZZ
][ZZ
];
1000 for (j
= g1
; j
< g
->shiftAtomEnd
; j
++)
1002 copy_rvec(x_s
[j
], x
[j
]);
1006 void unshift_self(const t_graph
* g
, const matrix box
, rvec x
[])
1012 gmx_incons("screw pbc not implemented for unshift_self");
1015 const int g0
= g
->edgeAtomBegin
;
1016 const int g1
= g
->edgeAtomEnd
;
1017 ArrayRef
<const IVec
> is
= g
->ishift
;
1021 for (j
= g0
; (j
< g1
); j
++)
1027 x
[j
][XX
] = x
[j
][XX
] - tx
* box
[XX
][XX
] - ty
* box
[YY
][XX
] - tz
* box
[ZZ
][XX
];
1028 x
[j
][YY
] = x
[j
][YY
] - ty
* box
[YY
][YY
] - tz
* box
[ZZ
][YY
];
1029 x
[j
][ZZ
] = x
[j
][ZZ
] - tz
* box
[ZZ
][ZZ
];
1034 for (j
= g0
; (j
< g1
); j
++)
1040 x
[j
][XX
] = x
[j
][XX
] - tx
* box
[XX
][XX
];
1041 x
[j
][YY
] = x
[j
][YY
] - ty
* box
[YY
][YY
];
1042 x
[j
][ZZ
] = x
[j
][ZZ
] - tz
* box
[ZZ
][ZZ
];