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.
40 * Implements routines in pbc.h.
42 * Utility functions for handling periodic boundary conditions.
43 * Mainly used in analysis tools.
53 #include "gromacs/math/functions.h"
54 #include "gromacs/math/units.h"
55 #include "gromacs/math/utilities.h"
56 #include "gromacs/math/vec.h"
57 #include "gromacs/math/vecdump.h"
58 #include "gromacs/pbcutil/ishift.h"
59 #include "gromacs/pbcutil/mshift.h"
60 #include "gromacs/topology/topology.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/fatalerror.h"
63 #include "gromacs/utility/gmxassert.h"
64 #include "gromacs/utility/smalloc.h"
66 const gmx::EnumerationArray<PbcType, std::string> c_pbcTypeNames = { { "xyz", "no", "xy", "screw",
69 /* Skip 0 so we have more chance of detecting if we forgot to call set_pbc. */
72 epbcdxRECTANGULAR = 1,
84 //! Margin factor for error message
85 #define BOX_MARGIN 1.0010
86 //! Margin correction if the box is too skewed
87 #define BOX_MARGIN_CORRECT 1.0005
89 int numPbcDimensions(PbcType pbcType)
96 gmx_fatal(FARGS, "Number of PBC dimensions was requested before the PBC type set.");
97 case PbcType::Xyz: npbcdim = 3; break;
98 case PbcType::XY: npbcdim = 2; break;
99 case PbcType::Screw: npbcdim = 3; break;
100 case PbcType::No: npbcdim = 0; break;
102 gmx_fatal(FARGS, "Unknown pbcType=%s in numPbcDimensions", c_pbcTypeNames[pbcType].c_str());
108 void dump_pbc(FILE* fp, t_pbc* pbc)
112 fprintf(fp, "pbcTypeDX = %d\n", pbc->pbcTypeDX);
113 pr_rvecs(fp, 0, "box", pbc->box, DIM);
114 pr_rvecs(fp, 0, "fbox_diag", &pbc->fbox_diag, 1);
115 pr_rvecs(fp, 0, "hbox_diag", &pbc->hbox_diag, 1);
116 pr_rvecs(fp, 0, "mhbox_diag", &pbc->mhbox_diag, 1);
117 rvec_add(pbc->hbox_diag, pbc->mhbox_diag, sum_box);
118 pr_rvecs(fp, 0, "sum of the above two", &sum_box, 1);
119 fprintf(fp, "max_cutoff2 = %g\n", pbc->max_cutoff2);
120 fprintf(fp, "ntric_vec = %d\n", pbc->ntric_vec);
121 if (pbc->ntric_vec > 0)
123 pr_ivecs(fp, 0, "tric_shift", pbc->tric_shift, pbc->ntric_vec, FALSE);
124 pr_rvecs(fp, 0, "tric_vec", pbc->tric_vec, pbc->ntric_vec);
128 const char* check_box(PbcType pbcType, const matrix box)
132 if (pbcType == PbcType::Unset)
134 pbcType = guessPbcType(box);
137 if (pbcType == PbcType::No)
142 if ((box[XX][YY] != 0) || (box[XX][ZZ] != 0) || (box[YY][ZZ] != 0))
144 ptr = "Only triclinic boxes with the first vector parallel to the x-axis and the second "
145 "vector in the xy-plane are supported.";
147 else if (pbcType == PbcType::Screw && (box[YY][XX] != 0 || box[ZZ][XX] != 0))
149 ptr = "The unit cell can not have off-diagonal x-components with screw pbc";
151 else if (std::fabs(box[YY][XX]) > BOX_MARGIN * 0.5 * box[XX][XX]
152 || (pbcType != PbcType::XY
153 && (std::fabs(box[ZZ][XX]) > BOX_MARGIN * 0.5 * box[XX][XX]
154 || std::fabs(box[ZZ][YY]) > BOX_MARGIN * 0.5 * box[YY][YY])))
156 ptr = "Triclinic box is too skewed.";
166 void matrix_convert(matrix box, const rvec vec, const rvec angleInDegrees)
169 svmul(DEG2RAD, angleInDegrees, angle);
170 box[XX][XX] = vec[XX];
171 box[YY][XX] = vec[YY] * cos(angle[ZZ]);
172 box[YY][YY] = vec[YY] * sin(angle[ZZ]);
173 box[ZZ][XX] = vec[ZZ] * cos(angle[YY]);
174 box[ZZ][YY] = vec[ZZ] * (cos(angle[XX]) - cos(angle[YY]) * cos(angle[ZZ])) / sin(angle[ZZ]);
176 std::sqrt(gmx::square(vec[ZZ]) - box[ZZ][XX] * box[ZZ][XX] - box[ZZ][YY] * box[ZZ][YY]);
179 real max_cutoff2(PbcType pbcType, const matrix box)
181 real min_hv2, min_ss;
182 const real oneFourth = 0.25;
184 /* Physical limitation of the cut-off
185 * by half the length of the shortest box vector.
187 min_hv2 = oneFourth * std::min(norm2(box[XX]), norm2(box[YY]));
188 if (pbcType != PbcType::XY)
190 min_hv2 = std::min(min_hv2, oneFourth * norm2(box[ZZ]));
193 /* Limitation to the smallest diagonal element due to optimizations:
194 * checking only linear combinations of single box-vectors (2 in x)
195 * in the grid search and pbc_dx is a lot faster
196 * than checking all possible combinations.
198 if (pbcType == PbcType::XY)
200 min_ss = std::min(box[XX][XX], box[YY][YY]);
204 min_ss = std::min(box[XX][XX], std::min(box[YY][YY] - std::fabs(box[ZZ][YY]), box[ZZ][ZZ]));
207 return std::min(min_hv2, min_ss * min_ss);
210 //! Set to true if warning has been printed
211 static gmx_bool bWarnedGuess = FALSE;
213 PbcType guessPbcType(const matrix box)
217 if (box[XX][XX] > 0 && box[YY][YY] > 0 && box[ZZ][ZZ] > 0)
219 pbcType = PbcType::Xyz;
221 else if (box[XX][XX] > 0 && box[YY][YY] > 0 && box[ZZ][ZZ] == 0)
223 pbcType = PbcType::XY;
225 else if (box[XX][XX] == 0 && box[YY][YY] == 0 && box[ZZ][ZZ] == 0)
227 pbcType = PbcType::No;
234 "WARNING: Unsupported box diagonal %f %f %f, "
235 "will not use periodic boundary conditions\n\n",
236 box[XX][XX], box[YY][YY], box[ZZ][ZZ]);
239 pbcType = PbcType::No;
244 fprintf(debug, "Guessed pbc = %s from the box matrix\n", c_pbcTypeNames[pbcType].c_str());
250 //! Check if the box still obeys the restrictions, if not, correct it
251 static int correct_box_elem(FILE* fplog, int step, tensor box, int v, int d)
253 int shift, maxshift = 10;
257 /* correct elem d of vector v with vector d */
258 while (box[v][d] > BOX_MARGIN_CORRECT * 0.5 * box[d][d])
262 fprintf(fplog, "Step %d: correcting invalid box:\n", step);
263 pr_rvecs(fplog, 0, "old box", box, DIM);
265 rvec_dec(box[v], box[d]);
269 pr_rvecs(fplog, 0, "new box", box, DIM);
271 if (shift <= -maxshift)
273 gmx_fatal(FARGS, "Box was shifted at least %d times. Please see log-file.", maxshift);
276 while (box[v][d] < -BOX_MARGIN_CORRECT * 0.5 * box[d][d])
280 fprintf(fplog, "Step %d: correcting invalid box:\n", step);
281 pr_rvecs(fplog, 0, "old box", box, DIM);
283 rvec_inc(box[v], box[d]);
287 pr_rvecs(fplog, 0, "new box", box, DIM);
289 if (shift >= maxshift)
291 gmx_fatal(FARGS, "Box was shifted at least %d times. Please see log-file.", maxshift);
298 gmx_bool correct_box(FILE* fplog, int step, tensor box)
303 zy = correct_box_elem(fplog, step, box, ZZ, YY);
304 zx = correct_box_elem(fplog, step, box, ZZ, XX);
305 yx = correct_box_elem(fplog, step, box, YY, XX);
307 bCorrected = ((zy != 0) || (zx != 0) || (yx != 0));
312 //! Do the real arithmetic for filling the pbc struct
313 static void low_set_pbc(t_pbc* pbc, PbcType pbcType, const ivec dd_pbc, const matrix box)
315 int order[3] = { 0, -1, 1 };
319 pbc->pbcType = pbcType;
320 pbc->ndim_ePBC = numPbcDimensions(pbcType);
322 if (pbc->pbcType == PbcType::No)
324 pbc->pbcTypeDX = epbcdxNOPBC;
329 copy_mat(box, pbc->box);
330 pbc->max_cutoff2 = 0;
334 for (int i = 0; (i < DIM); i++)
336 pbc->fbox_diag[i] = box[i][i];
337 pbc->hbox_diag[i] = pbc->fbox_diag[i] * 0.5;
338 pbc->mhbox_diag[i] = -pbc->hbox_diag[i];
341 ptr = check_box(pbcType, box);
344 fprintf(stderr, "Warning: %s\n", ptr);
345 pr_rvecs(stderr, 0, " Box", box, DIM);
346 fprintf(stderr, " Can not fix pbc.\n\n");
347 pbc->pbcTypeDX = epbcdxUNSUPPORTED;
351 if (pbcType == PbcType::Screw && nullptr != dd_pbc)
353 /* This combinated should never appear here */
354 gmx_incons("low_set_pbc called with screw pbc and dd_nc != NULL");
358 for (int i = 0; i < DIM; i++)
360 if ((dd_pbc && dd_pbc[i] == 0) || (pbcType == PbcType::XY && i == ZZ))
373 /* 1D pbc is not an mdp option and it is therefore only used
374 * with single shifts.
376 pbc->pbcTypeDX = epbcdx1D_RECT;
377 for (int i = 0; i < DIM; i++)
384 GMX_ASSERT(pbc->dim < DIM, "Dimension for PBC incorrect");
385 for (int i = 0; i < pbc->dim; i++)
387 if (pbc->box[pbc->dim][i] != 0)
389 pbc->pbcTypeDX = epbcdx1D_TRIC;
394 pbc->pbcTypeDX = epbcdx2D_RECT;
395 for (int i = 0; i < DIM; i++)
402 for (int i = 0; i < DIM; i++)
406 for (int j = 0; j < i; j++)
408 if (pbc->box[i][j] != 0)
410 pbc->pbcTypeDX = epbcdx2D_TRIC;
417 if (pbcType != PbcType::Screw)
421 pbc->pbcTypeDX = epbcdxTRICLINIC;
425 pbc->pbcTypeDX = epbcdxRECTANGULAR;
430 pbc->pbcTypeDX = (box[ZZ][YY] == 0 ? epbcdxSCREW_RECT : epbcdxSCREW_TRIC);
431 if (pbc->pbcTypeDX == epbcdxSCREW_TRIC)
434 "Screw pbc is not yet implemented for triclinic boxes.\n"
435 "Can not fix pbc.\n");
436 pbc->pbcTypeDX = epbcdxUNSUPPORTED;
440 default: gmx_fatal(FARGS, "Incorrect number of pbc dimensions with DD: %d", npbcdim);
442 pbc->max_cutoff2 = max_cutoff2(pbcType, box);
444 if (pbc->pbcTypeDX == epbcdxTRICLINIC || pbc->pbcTypeDX == epbcdx2D_TRIC
445 || pbc->pbcTypeDX == epbcdxSCREW_TRIC)
449 pr_rvecs(debug, 0, "Box", box, DIM);
450 fprintf(debug, "max cutoff %.3f\n", sqrt(pbc->max_cutoff2));
452 /* We will only need single shifts here */
453 for (int kk = 0; kk < 3; kk++)
456 if (!bPBC[ZZ] && k != 0)
460 for (int jj = 0; jj < 3; jj++)
463 if (!bPBC[YY] && j != 0)
467 for (int ii = 0; ii < 3; ii++)
470 if (!bPBC[XX] && i != 0)
474 /* A shift is only useful when it is trilinic */
475 if (j != 0 || k != 0)
482 for (int d = 0; d < DIM; d++)
484 trial[d] = i * box[XX][d] + j * box[YY][d] + k * box[ZZ][d];
485 /* Choose the vector within the brick around 0,0,0 that
486 * will become the shortest due to shift try.
497 pos[d] = std::min(pbc->hbox_diag[d], -trial[d]);
501 pos[d] = std::max(-pbc->hbox_diag[d], -trial[d]);
504 d2old += gmx::square(pos[d]);
505 d2new += gmx::square(pos[d] + trial[d]);
507 if (BOX_MARGIN * d2new < d2old)
509 /* Check if shifts with one box vector less do better */
510 gmx_bool bUse = TRUE;
511 for (int dd = 0; dd < DIM; dd++)
513 int shift = (dd == 0 ? i : (dd == 1 ? j : k));
517 for (int d = 0; d < DIM; d++)
519 d2new_c += gmx::square(pos[d] + trial[d] - shift * box[dd][d]);
521 if (d2new_c <= BOX_MARGIN * d2new)
529 /* Accept this shift vector. */
530 if (pbc->ntric_vec >= MAX_NTRICVEC)
533 "\nWARNING: Found more than %d triclinic "
534 "correction vectors, ignoring some.\n"
535 " There is probably something wrong with your "
538 pr_rvecs(stderr, 0, " Box", box, DIM);
542 copy_rvec(trial, pbc->tric_vec[pbc->ntric_vec]);
543 pbc->tric_shift[pbc->ntric_vec][XX] = i;
544 pbc->tric_shift[pbc->ntric_vec][YY] = j;
545 pbc->tric_shift[pbc->ntric_vec][ZZ] = k;
551 " tricvec %2d = %2d %2d %2d %5.2f %5.2f "
552 "%5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
553 pbc->ntric_vec, i, j, k, sqrt(d2old),
554 sqrt(d2new), trial[XX], trial[YY], trial[ZZ],
555 pos[XX], pos[YY], pos[ZZ]);
568 void set_pbc(t_pbc* pbc, PbcType pbcType, const matrix box)
570 if (pbcType == PbcType::Unset)
572 pbcType = guessPbcType(box);
575 low_set_pbc(pbc, pbcType, nullptr, box);
578 t_pbc* set_pbc_dd(t_pbc* pbc, PbcType pbcType, const ivec domdecCells, gmx_bool bSingleDir, const matrix box)
580 if (pbcType == PbcType::No)
582 pbc->pbcType = pbcType;
587 if (nullptr == domdecCells)
589 low_set_pbc(pbc, pbcType, nullptr, box);
593 if (pbcType == PbcType::Screw && domdecCells[XX] > 1)
595 /* The rotation has been taken care of during coordinate communication */
596 pbcType = PbcType::Xyz;
601 for (int i = 0; i < DIM; i++)
604 if (domdecCells[i] <= (bSingleDir ? 1 : 2) && !(pbcType == PbcType::XY && i == ZZ))
613 low_set_pbc(pbc, pbcType, usePBC, box);
617 pbc->pbcType = PbcType::No;
621 return (pbc->pbcType != PbcType::No ? pbc : nullptr);
624 void pbc_dx(const t_pbc* pbc, const rvec x1, const rvec x2, rvec dx)
627 rvec dx_start, trial;
631 rvec_sub(x1, x2, dx);
633 switch (pbc->pbcTypeDX)
635 case epbcdxRECTANGULAR:
636 for (i = 0; i < DIM; i++)
638 while (dx[i] > pbc->hbox_diag[i])
640 dx[i] -= pbc->fbox_diag[i];
642 while (dx[i] <= pbc->mhbox_diag[i])
644 dx[i] += pbc->fbox_diag[i];
648 case epbcdxTRICLINIC:
649 for (i = DIM - 1; i >= 0; i--)
651 while (dx[i] > pbc->hbox_diag[i])
653 for (j = i; j >= 0; j--)
655 dx[j] -= pbc->box[i][j];
658 while (dx[i] <= pbc->mhbox_diag[i])
660 for (j = i; j >= 0; j--)
662 dx[j] += pbc->box[i][j];
666 /* dx is the distance in a rectangular box */
668 if (d2min > pbc->max_cutoff2)
670 copy_rvec(dx, dx_start);
672 /* Now try all possible shifts, when the distance is within max_cutoff
673 * it must be the shortest possible distance.
676 while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec))
678 rvec_add(dx_start, pbc->tric_vec[i], trial);
679 d2trial = norm2(trial);
682 copy_rvec(trial, dx);
690 for (i = 0; i < DIM; i++)
694 while (dx[i] > pbc->hbox_diag[i])
696 dx[i] -= pbc->fbox_diag[i];
698 while (dx[i] <= pbc->mhbox_diag[i])
700 dx[i] += pbc->fbox_diag[i];
707 for (i = DIM - 1; i >= 0; i--)
711 while (dx[i] > pbc->hbox_diag[i])
713 for (j = i; j >= 0; j--)
715 dx[j] -= pbc->box[i][j];
718 while (dx[i] <= pbc->mhbox_diag[i])
720 for (j = i; j >= 0; j--)
722 dx[j] += pbc->box[i][j];
725 d2min += dx[i] * dx[i];
728 if (d2min > pbc->max_cutoff2)
730 copy_rvec(dx, dx_start);
732 /* Now try all possible shifts, when the distance is within max_cutoff
733 * it must be the shortest possible distance.
736 while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec))
738 rvec_add(dx_start, pbc->tric_vec[i], trial);
740 for (j = 0; j < DIM; j++)
744 d2trial += trial[j] * trial[j];
749 copy_rvec(trial, dx);
756 case epbcdxSCREW_RECT:
757 /* The shift definition requires x first */
759 while (dx[XX] > pbc->hbox_diag[XX])
761 dx[XX] -= pbc->fbox_diag[XX];
764 while (dx[XX] <= pbc->mhbox_diag[XX])
766 dx[XX] += pbc->fbox_diag[YY];
771 /* Rotate around the x-axis in the middle of the box */
772 dx[YY] = pbc->box[YY][YY] - x1[YY] - x2[YY];
773 dx[ZZ] = pbc->box[ZZ][ZZ] - x1[ZZ] - x2[ZZ];
775 /* Normal pbc for y and z */
776 for (i = YY; i <= ZZ; i++)
778 while (dx[i] > pbc->hbox_diag[i])
780 dx[i] -= pbc->fbox_diag[i];
782 while (dx[i] <= pbc->mhbox_diag[i])
784 dx[i] += pbc->fbox_diag[i];
789 case epbcdxUNSUPPORTED: break;
790 default: gmx_fatal(FARGS, "Internal error in pbc_dx, set_pbc has not been called");
794 int pbc_dx_aiuc(const t_pbc* pbc, const rvec x1, const rvec x2, rvec dx)
797 rvec dx_start, trial;
799 ivec ishift, ishift_start;
801 rvec_sub(x1, x2, dx);
804 switch (pbc->pbcTypeDX)
806 case epbcdxRECTANGULAR:
807 for (i = 0; i < DIM; i++)
809 if (dx[i] > pbc->hbox_diag[i])
811 dx[i] -= pbc->fbox_diag[i];
814 else if (dx[i] <= pbc->mhbox_diag[i])
816 dx[i] += pbc->fbox_diag[i];
821 case epbcdxTRICLINIC:
822 /* For triclinic boxes the performance difference between
823 * if/else and two while loops is negligible.
824 * However, the while version can cause extreme delays
825 * before a simulation crashes due to large forces which
826 * can cause unlimited displacements.
827 * Also allowing multiple shifts would index fshift beyond bounds.
829 for (i = DIM - 1; i >= 1; i--)
831 if (dx[i] > pbc->hbox_diag[i])
833 for (j = i; j >= 0; j--)
835 dx[j] -= pbc->box[i][j];
839 else if (dx[i] <= pbc->mhbox_diag[i])
841 for (j = i; j >= 0; j--)
843 dx[j] += pbc->box[i][j];
848 /* Allow 2 shifts in x */
849 if (dx[XX] > pbc->hbox_diag[XX])
851 dx[XX] -= pbc->fbox_diag[XX];
853 if (dx[XX] > pbc->hbox_diag[XX])
855 dx[XX] -= pbc->fbox_diag[XX];
859 else if (dx[XX] <= pbc->mhbox_diag[XX])
861 dx[XX] += pbc->fbox_diag[XX];
863 if (dx[XX] <= pbc->mhbox_diag[XX])
865 dx[XX] += pbc->fbox_diag[XX];
869 /* dx is the distance in a rectangular box */
871 if (d2min > pbc->max_cutoff2)
873 copy_rvec(dx, dx_start);
874 copy_ivec(ishift, ishift_start);
876 /* Now try all possible shifts, when the distance is within max_cutoff
877 * it must be the shortest possible distance.
880 while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec))
882 rvec_add(dx_start, pbc->tric_vec[i], trial);
883 d2trial = norm2(trial);
886 copy_rvec(trial, dx);
887 ivec_add(ishift_start, pbc->tric_shift[i], ishift);
895 for (i = 0; i < DIM; i++)
899 if (dx[i] > pbc->hbox_diag[i])
901 dx[i] -= pbc->fbox_diag[i];
904 else if (dx[i] <= pbc->mhbox_diag[i])
906 dx[i] += pbc->fbox_diag[i];
914 for (i = DIM - 1; i >= 1; i--)
918 if (dx[i] > pbc->hbox_diag[i])
920 for (j = i; j >= 0; j--)
922 dx[j] -= pbc->box[i][j];
926 else if (dx[i] <= pbc->mhbox_diag[i])
928 for (j = i; j >= 0; j--)
930 dx[j] += pbc->box[i][j];
934 d2min += dx[i] * dx[i];
939 /* Allow 2 shifts in x */
940 if (dx[XX] > pbc->hbox_diag[XX])
942 dx[XX] -= pbc->fbox_diag[XX];
944 if (dx[XX] > pbc->hbox_diag[XX])
946 dx[XX] -= pbc->fbox_diag[XX];
950 else if (dx[XX] <= pbc->mhbox_diag[XX])
952 dx[XX] += pbc->fbox_diag[XX];
954 if (dx[XX] <= pbc->mhbox_diag[XX])
956 dx[XX] += pbc->fbox_diag[XX];
960 d2min += dx[XX] * dx[XX];
962 if (d2min > pbc->max_cutoff2)
964 copy_rvec(dx, dx_start);
965 copy_ivec(ishift, ishift_start);
966 /* Now try all possible shifts, when the distance is within max_cutoff
967 * it must be the shortest possible distance.
970 while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec))
972 rvec_add(dx_start, pbc->tric_vec[i], trial);
974 for (j = 0; j < DIM; j++)
978 d2trial += trial[j] * trial[j];
983 copy_rvec(trial, dx);
984 ivec_add(ishift_start, pbc->tric_shift[i], ishift);
993 if (dx[i] > pbc->hbox_diag[i])
995 dx[i] -= pbc->fbox_diag[i];
998 else if (dx[i] <= pbc->mhbox_diag[i])
1000 dx[i] += pbc->fbox_diag[i];
1006 if (dx[i] > pbc->hbox_diag[i])
1008 rvec_dec(dx, pbc->box[i]);
1011 else if (dx[i] <= pbc->mhbox_diag[i])
1013 rvec_inc(dx, pbc->box[i]);
1017 case epbcdxSCREW_RECT:
1018 /* The shift definition requires x first */
1019 if (dx[XX] > pbc->hbox_diag[XX])
1021 dx[XX] -= pbc->fbox_diag[XX];
1024 else if (dx[XX] <= pbc->mhbox_diag[XX])
1026 dx[XX] += pbc->fbox_diag[XX];
1029 if (ishift[XX] == 1 || ishift[XX] == -1)
1031 /* Rotate around the x-axis in the middle of the box */
1032 dx[YY] = pbc->box[YY][YY] - x1[YY] - x2[YY];
1033 dx[ZZ] = pbc->box[ZZ][ZZ] - x1[ZZ] - x2[ZZ];
1035 /* Normal pbc for y and z */
1036 for (i = YY; i <= ZZ; i++)
1038 if (dx[i] > pbc->hbox_diag[i])
1040 dx[i] -= pbc->fbox_diag[i];
1043 else if (dx[i] <= pbc->mhbox_diag[i])
1045 dx[i] += pbc->fbox_diag[i];
1051 case epbcdxUNSUPPORTED: break;
1054 "Internal error in pbc_dx_aiuc, set_pbc_dd or set_pbc has not been called");
1057 is = IVEC2IS(ishift);
1060 range_check_mesg(is, 0, SHIFTS, "PBC shift vector index range check.");
1066 //! Compute distance vector in double precision
1067 void pbc_dx_d(const t_pbc* pbc, const dvec x1, const dvec x2, dvec dx)
1070 dvec dx_start, trial;
1071 double d2min, d2trial;
1074 dvec_sub(x1, x2, dx);
1076 switch (pbc->pbcTypeDX)
1078 case epbcdxRECTANGULAR:
1080 for (i = 0; i < DIM; i++)
1084 while (dx[i] > pbc->hbox_diag[i])
1086 dx[i] -= pbc->fbox_diag[i];
1088 while (dx[i] <= pbc->mhbox_diag[i])
1090 dx[i] += pbc->fbox_diag[i];
1095 case epbcdxTRICLINIC:
1098 for (i = DIM - 1; i >= 0; i--)
1102 while (dx[i] > pbc->hbox_diag[i])
1104 for (j = i; j >= 0; j--)
1106 dx[j] -= pbc->box[i][j];
1109 while (dx[i] <= pbc->mhbox_diag[i])
1111 for (j = i; j >= 0; j--)
1113 dx[j] += pbc->box[i][j];
1116 d2min += dx[i] * dx[i];
1119 if (d2min > pbc->max_cutoff2)
1121 copy_dvec(dx, dx_start);
1122 /* Now try all possible shifts, when the distance is within max_cutoff
1123 * it must be the shortest possible distance.
1126 while ((d2min > pbc->max_cutoff2) && (i < pbc->ntric_vec))
1128 for (j = 0; j < DIM; j++)
1130 trial[j] = dx_start[j] + pbc->tric_vec[i][j];
1133 for (j = 0; j < DIM; j++)
1137 d2trial += trial[j] * trial[j];
1140 if (d2trial < d2min)
1142 copy_dvec(trial, dx);
1149 case epbcdxSCREW_RECT:
1150 /* The shift definition requires x first */
1152 while (dx[XX] > pbc->hbox_diag[XX])
1154 dx[XX] -= pbc->fbox_diag[XX];
1157 while (dx[XX] <= pbc->mhbox_diag[XX])
1159 dx[XX] += pbc->fbox_diag[YY];
1164 /* Rotate around the x-axis in the middle of the box */
1165 dx[YY] = pbc->box[YY][YY] - x1[YY] - x2[YY];
1166 dx[ZZ] = pbc->box[ZZ][ZZ] - x1[ZZ] - x2[ZZ];
1168 /* Normal pbc for y and z */
1169 for (i = YY; i <= ZZ; i++)
1171 while (dx[i] > pbc->hbox_diag[i])
1173 dx[i] -= pbc->fbox_diag[i];
1175 while (dx[i] <= pbc->mhbox_diag[i])
1177 dx[i] += pbc->fbox_diag[i];
1182 case epbcdxUNSUPPORTED: break;
1183 default: gmx_fatal(FARGS, "Internal error in pbc_dx, set_pbc has not been called");
1187 void calc_shifts(const matrix box, rvec shift_vec[])
1189 for (int n = 0, m = -D_BOX_Z; m <= D_BOX_Z; m++)
1191 for (int l = -D_BOX_Y; l <= D_BOX_Y; l++)
1193 for (int k = -D_BOX_X; k <= D_BOX_X; k++, n++)
1195 for (int d = 0; d < DIM; d++)
1197 shift_vec[n][d] = k * box[XX][d] + l * box[YY][d] + m * box[ZZ][d];
1204 void calc_box_center(int ecenter, const matrix box, rvec box_center)
1208 clear_rvec(box_center);
1212 for (m = 0; (m < DIM); m++)
1214 for (d = 0; d < DIM; d++)
1216 box_center[d] += 0.5 * box[m][d];
1221 for (d = 0; d < DIM; d++)
1223 box_center[d] = 0.5 * box[d][d];
1226 case ecenterZERO: break;
1227 default: gmx_fatal(FARGS, "Unsupported value %d for ecenter", ecenter);
1231 void calc_triclinic_images(const matrix box, rvec img[])
1235 /* Calculate 3 adjacent images in the xy-plane */
1236 copy_rvec(box[0], img[0]);
1237 copy_rvec(box[1], img[1]);
1240 svmul(-1, img[1], img[1]);
1242 rvec_sub(img[1], img[0], img[2]);
1244 /* Get the next 3 in the xy-plane as mirror images */
1245 for (i = 0; i < 3; i++)
1247 svmul(-1, img[i], img[3 + i]);
1250 /* Calculate the first 4 out of xy-plane images */
1251 copy_rvec(box[2], img[6]);
1254 svmul(-1, img[6], img[6]);
1256 for (i = 0; i < 3; i++)
1258 rvec_add(img[6], img[i + 1], img[7 + i]);
1261 /* Mirror the last 4 from the previous in opposite rotation */
1262 for (i = 0; i < 4; i++)
1264 svmul(-1, img[6 + (2 + i) % 4], img[10 + i]);
1268 void calc_compact_unitcell_vertices(int ecenter, const matrix box, rvec vert[])
1270 rvec img[NTRICIMG], box_center;
1271 int n, i, j, tmp[4], d;
1272 const real oneFourth = 0.25;
1274 calc_triclinic_images(box, img);
1277 for (i = 2; i <= 5; i += 3)
1288 tmp[2] = (i + 1) % 6;
1289 tmp[3] = tmp[1] + 4;
1290 for (j = 0; j < 4; j++)
1292 for (d = 0; d < DIM; d++)
1294 vert[n][d] = img[i][d] + img[tmp[j]][d] + img[tmp[(j + 1) % 4]][d];
1299 for (i = 7; i <= 13; i += 6)
1301 tmp[0] = (i - 7) / 2;
1302 tmp[1] = tmp[0] + 1;
1312 for (j = 0; j < 4; j++)
1314 for (d = 0; d < DIM; d++)
1316 vert[n][d] = img[i][d] + img[tmp[j]][d] + img[tmp[(j + 1) % 4]][d];
1321 for (i = 9; i <= 11; i += 2)
1331 tmp[1] = tmp[0] + 1;
1341 for (j = 0; j < 4; j++)
1343 for (d = 0; d < DIM; d++)
1345 vert[n][d] = img[i][d] + img[tmp[j]][d] + img[tmp[(j + 1) % 4]][d];
1351 calc_box_center(ecenter, box, box_center);
1352 for (i = 0; i < NCUCVERT; i++)
1354 for (d = 0; d < DIM; d++)
1356 vert[i][d] = vert[i][d] * oneFourth + box_center[d];
1361 int* compact_unitcell_edges()
1363 /* this is an index in vert[] (see calc_box_vertices) */
1364 /*static int edge[NCUCEDGE*2];*/
1366 static const int hexcon[24] = { 0, 9, 1, 19, 2, 15, 3, 21, 4, 17, 5, 11,
1367 6, 23, 7, 13, 8, 20, 10, 18, 12, 16, 14, 22 };
1370 snew(edge, NCUCEDGE * 2);
1373 for (i = 0; i < 6; i++)
1375 for (j = 0; j < 4; j++)
1377 edge[e++] = 4 * i + j;
1378 edge[e++] = 4 * i + (j + 1) % 4;
1381 for (i = 0; i < 12 * 2; i++)
1383 edge[e++] = hexcon[i];
1389 void put_atoms_in_box(PbcType pbcType, const matrix box, gmx::ArrayRef<gmx::RVec> x)
1393 if (pbcType == PbcType::Screw)
1395 gmx_fatal(FARGS, "Sorry, %s pbc is not yet supported", c_pbcTypeNames[pbcType].c_str());
1398 if (pbcType == PbcType::XY)
1409 for (gmx::index i = 0; (i < x.ssize()); ++i)
1411 for (m = npbcdim - 1; m >= 0; m--)
1415 for (d = 0; d <= m; d++)
1417 x[i][d] += box[m][d];
1420 while (x[i][m] >= box[m][m])
1422 for (d = 0; d <= m; d++)
1424 x[i][d] -= box[m][d];
1432 for (gmx::index i = 0; (i < x.ssize()); ++i)
1434 for (d = 0; d < npbcdim; d++)
1438 x[i][d] += box[d][d];
1440 while (x[i][d] >= box[d][d])
1442 x[i][d] -= box[d][d];
1449 void put_atoms_in_box_omp(PbcType pbcType, const matrix box, gmx::ArrayRef<gmx::RVec> x, gmx_unused int nth)
1451 #pragma omp parallel for num_threads(nth) schedule(static)
1452 for (int t = 0; t < nth; t++)
1456 size_t natoms = x.size();
1457 size_t offset = (natoms * t) / nth;
1458 size_t len = (natoms * (t + 1)) / nth - offset;
1459 put_atoms_in_box(pbcType, box, x.subArray(offset, len));
1461 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1465 void put_atoms_in_triclinic_unitcell(int ecenter, const matrix box, gmx::ArrayRef<gmx::RVec> x)
1467 rvec box_center, shift_center;
1468 real shm01, shm02, shm12, shift;
1471 calc_box_center(ecenter, box, box_center);
1473 /* The product of matrix shm with a coordinate gives the shift vector
1474 which is required determine the periodic cell position */
1475 shm01 = box[1][0] / box[1][1];
1476 shm02 = (box[1][1] * box[2][0] - box[2][1] * box[1][0]) / (box[1][1] * box[2][2]);
1477 shm12 = box[2][1] / box[2][2];
1479 clear_rvec(shift_center);
1480 for (d = 0; d < DIM; d++)
1482 rvec_inc(shift_center, box[d]);
1484 svmul(0.5, shift_center, shift_center);
1485 rvec_sub(box_center, shift_center, shift_center);
1487 shift_center[0] = shm01 * shift_center[1] + shm02 * shift_center[2];
1488 shift_center[1] = shm12 * shift_center[2];
1489 shift_center[2] = 0;
1491 for (gmx::index i = 0; (i < x.ssize()); ++i)
1493 for (m = DIM - 1; m >= 0; m--)
1495 shift = shift_center[m];
1498 shift += shm01 * x[i][1] + shm02 * x[i][2];
1502 shift += shm12 * x[i][2];
1504 while (x[i][m] - shift < 0)
1506 for (d = 0; d <= m; d++)
1508 x[i][d] += box[m][d];
1511 while (x[i][m] - shift >= box[m][m])
1513 for (d = 0; d <= m; d++)
1515 x[i][d] -= box[m][d];
1522 void put_atoms_in_compact_unitcell(PbcType pbcType, int ecenter, const matrix box, gmx::ArrayRef<gmx::RVec> x)
1525 rvec box_center, dx;
1527 set_pbc(&pbc, pbcType, box);
1529 if (pbc.pbcTypeDX == epbcdxUNSUPPORTED)
1531 gmx_fatal(FARGS, "Can not put atoms in compact unitcell with unsupported PBC");
1534 calc_box_center(ecenter, box, box_center);
1535 for (gmx::index i = 0; (i < x.ssize()); ++i)
1537 pbc_dx(&pbc, x[i], box_center, dx);
1538 rvec_add(box_center, dx, x[i]);
1542 /*! \brief Make molecules whole by shifting positions
1544 * \param[in] fplog Log file
1545 * \param[in] pbcType The PBC type
1546 * \param[in] box The simulation box
1547 * \param[in] mtop System topology definition
1548 * \param[in,out] x The coordinates of the atoms
1549 * \param[in] bFirst Specifier for first-time PBC removal
1552 low_do_pbc_mtop(FILE* fplog, PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[], gmx_bool bFirst)
1556 if (bFirst && fplog)
1558 fprintf(fplog, "Removing pbc first time\n");
1562 for (const gmx_molblock_t& molb : mtop->molblock)
1564 const gmx_moltype_t& moltype = mtop->moltype[molb.type];
1565 if (moltype.atoms.nr == 1 || (!bFirst && moltype.atoms.nr == 1))
1567 /* Just one atom or charge group in the molecule, no PBC required */
1568 as += molb.nmol * moltype.atoms.nr;
1572 t_graph graph = mk_graph_moltype(moltype);
1574 for (mol = 0; mol < molb.nmol; mol++)
1576 mk_mshift(fplog, &graph, pbcType, box, x + as);
1578 shift_self(graph, box, x + as);
1579 /* The molecule is whole now.
1580 * We don't need the second mk_mshift call as in do_pbc_first,
1581 * since we no longer need this graph.
1584 as += moltype.atoms.nr;
1590 void do_pbc_first_mtop(FILE* fplog, PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[])
1592 low_do_pbc_mtop(fplog, pbcType, box, mtop, x, TRUE);
1595 void do_pbc_mtop(PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[])
1597 low_do_pbc_mtop(nullptr, pbcType, box, mtop, x, FALSE);