Update instructions in containers.rst
[gromacs.git] / src / gromacs / pbcutil / mshift.cpp
blob99824fc17274af270eb60b82cdb891e9cd2ccdb4
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,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.
38 #include "gmxpre.h"
40 #include "mshift.h"
42 #include <cstring>
44 #include <algorithm>
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 ************************************************************/
68 using gmx::ArrayRef;
69 using gmx::IVec;
71 // Class for generating the edges of the graph
72 class EdgesGenerator
74 public:
75 EdgesGenerator(const int endAtom) : edges_(endAtom) {}
77 // Adds an edge, bi-directional
78 void addEdge(int a0, int a1);
80 // Returns the edges
81 const std::vector<std::vector<int>>& edges() const { return edges_; }
83 private:
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.
103 template<typename T>
104 static bool mk_igraph(EdgesGenerator* edgesG, int ftype, const T& il, int at_end, ArrayRef<const int> part)
106 int i, j, np;
107 int end;
108 bool addedEdge = false;
110 end = il.size();
112 i = 0;
113 while (i < end)
115 np = interaction_function[ftype].nratoms;
117 if (np > 1 && il.iatoms[i + 1] < at_end)
119 if (il.iatoms[i + np] >= at_end)
121 gmx_fatal(FARGS,
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.",
127 at_end, at_end);
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]);
134 addedEdge = true;
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]);
143 addedEdge = true;
145 else
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]);
153 addedEdge = true;
159 i += np + 1;
162 return addedEdge;
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);
169 #define GCHECK(g) \
170 if ((g) == NULL) \
171 g_error(__LINE__, __FILE__)
173 void p_graph(FILE* log, const char* title, const t_graph* g)
175 int i;
176 const char* cc[egcolNR] = { "W", "G", "B" };
178 GCHECK(g);
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);
197 fprintf(log, "\n");
200 fflush(log);
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;
216 int max_nedge = 0;
217 for (const auto& edges : edgesG.edges())
219 if (edges.empty())
221 numEmptyEntriesSkipped++;
223 else
225 if (edgesLists.empty())
227 /* We ignore empty entries before the first connected entry */
228 *firstConnectedAtom = numEmptyEntriesSkipped;
230 else
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)));
248 if (fplog)
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());
254 return edgesLists;
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++)
263 partNr[at_i] = at_i;
266 /* Loop over the graph until the part array is fixed */
267 bool haveMultipleParts = false;
268 int numAtomsChanged = 0;
271 haveMultipleParts = false;
272 numAtomsChanged = 0;
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];
281 numAtomsChanged++;
283 else if (partNr[at_i2] < partNr[at_i])
285 partNr[at_i] = partNr[at_i2];
286 numAtomsChanged++;
289 if (partNr[at_i] != partNr[0])
291 haveMultipleParts = true;
294 if (debug)
296 fprintf(debug, "graph partNr[] numAtomsChanged=%d, bMultiPart=%s\n", numAtomsChanged,
297 gmx::boolToString(haveMultipleParts));
299 } while (numAtomsChanged > 0);
301 return haveMultipleParts;
304 template<typename T>
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;
311 if (at_end > 0)
313 if (!bShakeOnly)
315 /* First add all the real bonds: they should determine the molecular
316 * graph.
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);
332 if (bMultiPart)
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);
348 if (addedEdge)
350 parts = t_graph::BondedParts::MultipleConnected;
352 else
354 parts = t_graph::BondedParts::MultipleDisconnected;
358 else
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, {});
362 if (bSettle)
364 mk_igraph(&edgesG, F_SETTLE, ilist[F_SETTLE], at_end, {});
369 t_graph graph;
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;
378 graph.parts = parts;
379 if (at_end > 0)
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);
390 if (gmx_debug_at)
392 p_graph(debug, "graph", &graph);
395 return 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);
414 return g;
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);
423 return g;
426 void done_graph(t_graph* g)
428 delete 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,
438 const matrix box,
439 const rvec hbox,
440 const rvec xi,
441 const rvec xj,
442 const int* mi,
443 int* mj)
445 /* Calculate periodicity for triclinic box... */
446 int m, d;
447 rvec dx;
449 rvec_sub(xi, xj, dx);
451 mj[ZZ] = 0;
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])
459 mj[m] = mi[m] - 1;
460 for (d = m - 1; d >= 0; d--)
462 dx[d] += box[m][d];
465 else if (dx[m] >= hbox[m])
467 mj[m] = mi[m] + 1;
468 for (d = m - 1; d >= 0; d--)
470 dx[d] -= box[m][d];
473 else
475 mj[m] = mi[m];
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... */
483 int m;
484 rvec dx;
486 rvec_sub(xi, xj, dx);
488 mj[ZZ] = 0;
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])
496 mj[m] = mi[m] - 1;
498 else if (dx[m] >= hbox[m])
500 mj[m] = mi[m] + 1;
502 else
504 mj[m] = mi[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... */
512 int signi, m;
513 rvec dx;
515 if ((mi[XX] > 0 && mi[XX] % 2 == 1) || (mi[XX] < 0 && -mi[XX] % 2 == 1))
517 signi = -1;
519 else
521 signi = 1;
524 rvec_sub(xi, xj, dx);
526 if (dx[XX] < -hbox[XX])
528 mj[XX] = mi[XX] - 1;
530 else if (dx[XX] >= hbox[XX])
532 mj[XX] = mi[XX] + 1;
534 else
536 mj[XX] = mi[XX];
538 if (mj[XX] != mi[XX])
540 /* Rotate */
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;
557 else
559 mj[m] = mi[m];
564 static int mk_grey(ArrayRef<egCol> edgeColor,
565 t_graph* g,
566 int* AtomI,
567 int npbcdim,
568 const matrix box,
569 const rvec x[],
570 int* nerror)
572 int m, ng, ai, g0;
573 rvec dx, hbox;
574 gmx_bool bTriclinic;
575 ivec is_aj;
576 t_pbc pbc;
578 for (m = 0; (m < DIM); m++)
580 hbox[m] = box[m][m] * 0.5;
582 bTriclinic = TRICLINIC(box);
584 g0 = g->edgeAtomBegin;
585 ng = 0;
586 ai = g0 + *AtomI;
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 */
592 if (g->useScrewPbc)
594 mk_1shift_screw(box, hbox, x[ai], x[aj], g->ishift[ai], is_aj);
596 else if (bTriclinic)
598 mk_1shift_tric(npbcdim, box, hbox, x[ai], x[aj], g->ishift[ai], is_aj);
600 else
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)
609 *AtomI = aj - g0;
611 edgeColor[aj - g0] = egcolGrey;
613 copy_ivec(is_aj, g->ishift[aj]);
615 ng++;
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]))
620 if (gmx_debug_at)
622 set_pbc(&pbc, PbcType::Unset, box);
623 pbc_dx(&pbc, x[ai], x[aj], dx);
624 fprintf(debug,
625 "mk_grey: shifts for atom %d due to atom %d\n"
626 "are (%d,%d,%d), should be (%d,%d,%d)\n"
627 "dx = (%g,%g,%g)\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]);
631 (*nerror)++;
634 return ng;
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)
646 return i;
650 return -1;
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[])
656 t_pbc pbc;
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])
666 rvec dx;
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;
678 int npbcdim;
679 int ng, i;
680 int nW, nG, nB; /* Number of Grey, Black, White */
681 int fW, fG; /* First of each category */
682 int nerror = 0;
684 g->useScrewPbc = (pbcType == PbcType::Screw);
686 if (pbcType == PbcType::XY)
688 npbcdim = 2;
690 else
692 npbcdim = 3;
695 GCHECK(g);
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)
707 return;
710 std::fill(g->edgeColor.begin(), g->edgeColor.end(), egcolWhite);
712 nW = g->numConnectedAtoms;
713 nG = 0;
714 nB = 0;
716 fW = 0;
718 /* We even have a loop invariant:
719 * nW+nG+nB == g->nbound
721 #ifdef DEBUG2
722 fprintf(log, "Starting W loop\n");
723 #endif
724 while (nW > 0)
726 /* Find the first white, this will allways be a larger
727 * number than before, because no nodes are made white
728 * in the loop
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;
737 nG++;
738 nW--;
740 /* Initial value for the first grey */
741 fG = fW;
742 #ifdef DEBUG2
743 fprintf(log, "Starting G loop (nW=%d, nG=%d, nB=%d, total %d)\n", nW, nG, nB, nW + nG + nB);
744 #endif
745 while (nG > 0)
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;
754 nB++;
755 nG--;
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 */
762 nG += ng;
763 nW -= ng;
766 if (nerror > 0)
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");
793 switch (g->parts)
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.";
804 break;
805 default:
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. */
814 nerror_tot++;
815 if (nerror_tot <= 100)
817 fprintf(stderr, "There were %d inconsistent shifts. Check your topology\n", nerror);
818 if (log)
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");
826 if (log)
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[])
842 int j, tx, ty, tz;
844 GCHECK(g);
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]);
854 if (g->useScrewPbc)
856 for (j = g0; (j < g1); j++)
858 tx = is[j][XX];
859 ty = is[j][YY];
860 tz = is[j][ZZ];
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];
868 else
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++)
880 tx = is[j][XX];
881 ty = is[j][YY];
882 tz = is[j][ZZ];
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];
889 else
891 for (j = g0; (j < g1); j++)
893 tx = is[j][XX];
894 ty = is[j][YY];
895 tz = is[j][ZZ];
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[])
911 int j, tx, ty, tz;
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;
919 #ifdef DEBUG
920 fprintf(stderr, "Shifting atoms %d to %d\n", g0, g0 + gn);
921 #endif
922 if (TRICLINIC(box))
924 for (j = g0; (j < g1); j++)
926 tx = is[j][XX];
927 ty = is[j][YY];
928 tz = is[j][ZZ];
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];
935 else
937 for (j = g0; (j < g1); j++)
939 tx = is[j][XX];
940 ty = is[j][YY];
941 tz = is[j][ZZ];
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[])
957 int j, tx, ty, tz;
959 if (g->useScrewPbc)
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]);
973 if (TRICLINIC(box))
975 for (j = g0; (j < g1); j++)
977 tx = is[j][XX];
978 ty = is[j][YY];
979 tz = is[j][ZZ];
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];
986 else
988 for (j = g0; (j < g1); j++)
990 tx = is[j][XX];
991 ty = is[j][YY];
992 tz = is[j][ZZ];
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[])
1008 int j, tx, ty, tz;
1010 if (g->useScrewPbc)
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;
1019 if (TRICLINIC(box))
1021 for (j = g0; (j < g1); j++)
1023 tx = is[j][XX];
1024 ty = is[j][YY];
1025 tz = is[j][ZZ];
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];
1032 else
1034 for (j = g0; (j < g1); j++)
1036 tx = is[j][XX];
1037 ty = is[j][YY];
1038 tz = is[j][ZZ];
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];
1046 #undef GCHECK