1 /*
2 * This file is part of the GROMACS molecular simulation package.
3 *
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.
11 *
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.
16 *
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.
21 *
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.
26 *
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.
34 *
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.
37 */
38 /*! \internal \file
39 * \brief Defines LINCS code.
40 *
41 * \author Berk Hess <hess@kth.se>
42 * \author Mark Abraham <mark.j.abraham@gmail.com>
43 * \ingroup module_mdlib
44 */
45 #include "gmxpre.h"
46
47 #include "lincs.h"
48
49 #include "config.h"
50
51 #include <cassert>
52 #include <cmath>
53 #include <cstdlib>
54
55 #include <algorithm>
56 #include <vector>
57
58 #include "gromacs/domdec/domdec.h"
59 #include "gromacs/domdec/domdec_struct.h"
60 #include "gromacs/gmxlib/nrnb.h"
61 #include "gromacs/math/functions.h"
62 #include "gromacs/math/paddedvector.h"
63 #include "gromacs/math/units.h"
64 #include "gromacs/math/vec.h"
65 #include "gromacs/mdlib/constr.h"
66 #include "gromacs/mdlib/gmx_omp_nthreads.h"
67 #include "gromacs/mdrunutility/multisim.h"
68 #include "gromacs/mdtypes/commrec.h"
69 #include "gromacs/mdtypes/inputrec.h"
70 #include "gromacs/mdtypes/md_enums.h"
71 #include "gromacs/pbcutil/pbc.h"
72 #include "gromacs/pbcutil/pbc_simd.h"
73 #include "gromacs/simd/simd.h"
74 #include "gromacs/simd/simd_math.h"
75 #include "gromacs/simd/vector_operations.h"
76 #include "gromacs/topology/mtop_util.h"
77 #include "gromacs/utility/alignedallocator.h"
78 #include "gromacs/utility/arrayref.h"
79 #include "gromacs/utility/basedefinitions.h"
80 #include "gromacs/utility/bitmask.h"
81 #include "gromacs/utility/cstringutil.h"
82 #include "gromacs/utility/exceptions.h"
83 #include "gromacs/utility/fatalerror.h"
84 #include "gromacs/utility/gmxomp.h"
85 #include "gromacs/utility/listoflists.h"
86 #include "gromacs/utility/pleasecite.h"
87
88 using namespace gmx; // TODO: Remove when this file is moved into gmx namespace
89
90 namespace gmx
91 {
92
93 //! Indices of the two atoms involved in a single constraint
94 struct AtomPair
95 {
96 //! \brief Constructor, does not initialize to catch bugs and faster construction
AtomPairgmx::AtomPair97 AtomPair() {}
98
99 //! Index of atom 1
100 int index1;
101 //! Index of atom 2
102 int index2;
103 };
104
105 //! Unit of work within LINCS.
106 struct Task
107 {
108 //! First constraint for this task.
109 int b0 = 0;
110 //! b1-1 is the last constraint for this task.
111 int b1 = 0;
112 //! The number of constraints in triangles.
113 int ntriangle = 0;
114 //! The list of triangle constraints.
115 std::vector<int> triangle;
116 //! The bits tell if the matrix element should be used.
117 std::vector<int> tri_bits;
118 //! Constraint index for updating atom data.
119 std::vector<int> ind;
120 //! Constraint index for updating atom data.
121 std::vector<int> ind_r;
122 //! Temporary variable for virial calculation.
123 tensor vir_r_m_dr = { { 0 } };
124 //! Temporary variable for lambda derivative.
125 real dhdlambda;
126 };
127
128 /*! \brief Data for LINCS algorithm.
129 */
130 class Lincs
131 {
132 public:
133 //! The global number of constraints.
134 int ncg = 0;
135 //! The global number of flexible constraints.
136 int ncg_flex = 0;
137 //! The global number of constraints in triangles.
138 int ncg_triangle = 0;
139 //! The number of iterations.
140 int nIter = 0;
141 //! The order of the matrix expansion.
142 int nOrder = 0;
143 //! The maximum number of constraints connected to a single atom.
144 int max_connect = 0;
145
146 //! The number of real constraints.
147 int nc_real = 0;
148 //! The number of constraints including padding for SIMD.
149 int nc = 0;
150 //! The number of constraint connections.
151 int ncc = 0;
152 //! The FE lambda value used for filling blc and blmf.
153 real matlam = 0;
154 //! mapping from topology to LINCS constraints.
155 std::vector<int> con_index;
156 //! The reference distance in topology A.
157 std::vector<real, AlignedAllocator<real>> bllen0;
158 //! The reference distance in top B - the r.d. in top A.
159 std::vector<real, AlignedAllocator<real>> ddist;
160 //! The atom pairs involved in the constraints.
161 std::vector<AtomPair> atoms;
162 //! 1/sqrt(invmass1 invmass2).
163 std::vector<real, AlignedAllocator<real>> blc;
164 //! As blc, but with all masses 1.
165 std::vector<real, AlignedAllocator<real>> blc1;
166 //! Index into blbnb and blmf.
167 std::vector<int> blnr;
168 //! List of constraint connections.
169 std::vector<int> blbnb;
170 //! The local number of constraints in triangles.
171 int ntriangle = 0;
172 //! The number of constraint connections in triangles.
173 int ncc_triangle = 0;
174 //! Communicate before each LINCS interation.
175 bool bCommIter = false;
176 //! Matrix of mass factors for constraint connections.
177 std::vector<real> blmf;
178 //! As blmf, but with all masses 1.
179 std::vector<real> blmf1;
180 //! The reference bond length.
181 std::vector<real, AlignedAllocator<real>> bllen;
182 //! The local atom count per constraint, can be NULL.
183 std::vector<int> nlocat;
184
185 /*! \brief The number of tasks used for LINCS work.
186 *
187 * \todo This is mostly used to loop over \c task, which would
188 * be nicer to do with range-based for loops, but the thread
189 * index is used for constructing bit masks and organizing the
190 * virial output buffer, so other things need to change,
191 * first. */
192 int ntask = 0;
193 /*! \brief LINCS thread division */
194 std::vector<Task> task;
195 //! Atom flags for thread parallelization.
196 std::vector<gmx_bitmask_t> atf;
197 //! Are the LINCS tasks interdependent?
198 bool bTaskDep = false;
199 //! Are there triangle constraints that cross task borders?
200 bool bTaskDepTri = false;
201 //! Arrays for temporary storage in the LINCS algorithm.
202 /*! @{ */
203 PaddedVector<gmx::RVec> tmpv;
204 std::vector<real> tmpncc;
205 std::vector<real, AlignedAllocator<real>> tmp1;
206 std::vector<real, AlignedAllocator<real>> tmp2;
207 std::vector<real, AlignedAllocator<real>> tmp3;
208 std::vector<real, AlignedAllocator<real>> tmp4;
209 /*! @} */
210 //! The Lagrange multipliers times -1.
211 std::vector<real, AlignedAllocator<real>> mlambda;
212 //! Storage for the constraint RMS relative deviation output.
213 std::array<real, 2> rmsdData = { { 0 } };
214 };
215
216 /*! \brief Define simd_width for memory allocation used for SIMD code */
217 #if GMX_SIMD_HAVE_REAL
218 static const int simd_width = GMX_SIMD_REAL_WIDTH;
219 #else
220 static const int simd_width = 1;
221 #endif
222
lincs_rmsdData(Lincs * lincsd)223 ArrayRef<real> lincs_rmsdData(Lincs* lincsd)
224 {
225 return lincsd->rmsdData;
226 }
227
lincs_rmsd(const Lincs * lincsd)228 real lincs_rmsd(const Lincs* lincsd)
229 {
230 if (lincsd->rmsdData[0] > 0)
231 {
232 return std::sqrt(lincsd->rmsdData[1] / lincsd->rmsdData[0]);
233 }
234 else
235 {
236 return 0;
237 }
238 }
239
240 /*! \brief Do a set of nrec LINCS matrix multiplications.
241 *
242 * This function will return with up to date thread-local
243 * constraint data, without an OpenMP barrier.
244 */
lincs_matrix_expand(const Lincs & lincsd,const Task & li_task,gmx::ArrayRef<const real> blcc,gmx::ArrayRef<real> rhs1,gmx::ArrayRef<real> rhs2,gmx::ArrayRef<real> sol)245 static void lincs_matrix_expand(const Lincs& lincsd,
246 const Task& li_task,
247 gmx::ArrayRef<const real> blcc,
248 gmx::ArrayRef<real> rhs1,
249 gmx::ArrayRef<real> rhs2,
250 gmx::ArrayRef<real> sol)
251 {
252 gmx::ArrayRef<const int> blnr = lincsd.blnr;
253 gmx::ArrayRef<const int> blbnb = lincsd.blbnb;
254
255 const int b0 = li_task.b0;
256 const int b1 = li_task.b1;
257 const int nrec = lincsd.nOrder;
258
259 for (int rec = 0; rec < nrec; rec++)
260 {
261 if (lincsd.bTaskDep)
262 {
263 #pragma omp barrier
264 }
265 for (int b = b0; b < b1; b++)
266 {
267 real mvb;
268 int n;
269
270 mvb = 0;
271 for (n = blnr[b]; n < blnr[b + 1]; n++)
272 {
273 mvb = mvb + blcc[n] * rhs1[blbnb[n]];
274 }
275 rhs2[b] = mvb;
276 sol[b] = sol[b] + mvb;
277 }
278
279 std::swap(rhs1, rhs2);
280 } /* nrec*(ncons+2*nrtot) flops */
281
282 if (lincsd.ntriangle > 0)
283 {
284 /* Perform an extra nrec recursions for only the constraints
285 * involved in rigid triangles.
286 * In this way their accuracy should come close to those of the other
287 * constraints, since traingles of constraints can produce eigenvalues
288 * around 0.7, while the effective eigenvalue for bond constraints
289 * is around 0.4 (and 0.7*0.7=0.5).
290 */
291
292 if (lincsd.bTaskDep)
293 {
294 /* We need a barrier here, since other threads might still be
295 * reading the contents of rhs1 and/o rhs2.
296 * We could avoid this barrier by introducing two extra rhs
297 * arrays for the triangle constraints only.
298 */
299 #pragma omp barrier
300 }
301
302 /* Constraints involved in a triangle are ensured to be in the same
303 * LINCS task. This means no barriers are required during the extra
304 * iterations for the triangle constraints.
305 */
306 gmx::ArrayRef<const int> triangle = li_task.triangle;
307 gmx::ArrayRef<const int> tri_bits = li_task.tri_bits;
308
309 for (int rec = 0; rec < nrec; rec++)
310 {
311 for (int tb = 0; tb < li_task.ntriangle; tb++)
312 {
313 int b, bits, nr0, nr1, n;
314 real mvb;
315
316 b = triangle[tb];
317 bits = tri_bits[tb];
318 mvb = 0;
319 nr0 = blnr[b];
320 nr1 = blnr[b + 1];
321 for (n = nr0; n < nr1; n++)
322 {
323 if (bits & (1 << (n - nr0)))
324 {
325 mvb = mvb + blcc[n] * rhs1[blbnb[n]];
326 }
327 }
328 rhs2[b] = mvb;
329 sol[b] = sol[b] + mvb;
330 }
331
332 std::swap(rhs1, rhs2);
333 } /* nrec*(ntriangle + ncc_triangle*2) flops */
334
335 if (lincsd.bTaskDepTri)
336 {
337 /* The constraints triangles are decoupled from each other,
338 * but constraints in one triangle cross thread task borders.
339 * We could probably avoid this with more advanced setup code.
340 */
341 #pragma omp barrier
342 }
343 }
344 }
345
346 //! Update atomic coordinates when an index is not required.
lincs_update_atoms_noind(int ncons,gmx::ArrayRef<const AtomPair> atoms,real preFactor,gmx::ArrayRef<const real> fac,gmx::ArrayRef<const gmx::RVec> r,const real * invmass,rvec * x)347 static void lincs_update_atoms_noind(int ncons,
348 gmx::ArrayRef<const AtomPair> atoms,
349 real preFactor,
350 gmx::ArrayRef<const real> fac,
351 gmx::ArrayRef<const gmx::RVec> r,
352 const real* invmass,
353 rvec* x)
354 {
355 if (invmass != nullptr)
356 {
357 for (int b = 0; b < ncons; b++)
358 {
359 int i = atoms[b].index1;
360 int j = atoms[b].index2;
361 real mvb = preFactor * fac[b];
362 real im1 = invmass[i];
363 real im2 = invmass[j];
364 real tmp0 = r[b][0] * mvb;
365 real tmp1 = r[b][1] * mvb;
366 real tmp2 = r[b][2] * mvb;
367 x[i][0] -= tmp0 * im1;
368 x[i][1] -= tmp1 * im1;
369 x[i][2] -= tmp2 * im1;
370 x[j][0] += tmp0 * im2;
371 x[j][1] += tmp1 * im2;
372 x[j][2] += tmp2 * im2;
373 } /* 16 ncons flops */
374 }
375 else
376 {
377 for (int b = 0; b < ncons; b++)
378 {
379 int i = atoms[b].index1;
380 int j = atoms[b].index2;
381 real mvb = preFactor * fac[b];
382 real tmp0 = r[b][0] * mvb;
383 real tmp1 = r[b][1] * mvb;
384 real tmp2 = r[b][2] * mvb;
385 x[i][0] -= tmp0;
386 x[i][1] -= tmp1;
387 x[i][2] -= tmp2;
388 x[j][0] += tmp0;
389 x[j][1] += tmp1;
390 x[j][2] += tmp2;
391 }
392 }
393 }
394
395 //! Update atomic coordinates when an index is required.
lincs_update_atoms_ind(gmx::ArrayRef<const int> ind,gmx::ArrayRef<const AtomPair> atoms,real preFactor,gmx::ArrayRef<const real> fac,gmx::ArrayRef<const gmx::RVec> r,const real * invmass,rvec * x)396 static void lincs_update_atoms_ind(gmx::ArrayRef<const int> ind,
397 gmx::ArrayRef<const AtomPair> atoms,
398 real preFactor,
399 gmx::ArrayRef<const real> fac,
400 gmx::ArrayRef<const gmx::RVec> r,
401 const real* invmass,
402 rvec* x)
403 {
404 if (invmass != nullptr)
405 {
406 for (int b : ind)
407 {
408 int i = atoms[b].index1;
409 int j = atoms[b].index2;
410 real mvb = preFactor * fac[b];
411 real im1 = invmass[i];
412 real im2 = invmass[j];
413 real tmp0 = r[b][0] * mvb;
414 real tmp1 = r[b][1] * mvb;
415 real tmp2 = r[b][2] * mvb;
416 x[i][0] -= tmp0 * im1;
417 x[i][1] -= tmp1 * im1;
418 x[i][2] -= tmp2 * im1;
419 x[j][0] += tmp0 * im2;
420 x[j][1] += tmp1 * im2;
421 x[j][2] += tmp2 * im2;
422 } /* 16 ncons flops */
423 }
424 else
425 {
426 for (int b : ind)
427 {
428 int i = atoms[b].index1;
429 int j = atoms[b].index2;
430 real mvb = preFactor * fac[b];
431 real tmp0 = r[b][0] * mvb;
432 real tmp1 = r[b][1] * mvb;
433 real tmp2 = r[b][2] * mvb;
434 x[i][0] -= tmp0;
435 x[i][1] -= tmp1;
436 x[i][2] -= tmp2;
437 x[j][0] += tmp0;
438 x[j][1] += tmp1;
439 x[j][2] += tmp2;
440 } /* 16 ncons flops */
441 }
442 }
443
444 //! Update coordinates for atoms.
lincs_update_atoms(Lincs * li,int th,real preFactor,gmx::ArrayRef<const real> fac,gmx::ArrayRef<const gmx::RVec> r,const real * invmass,rvec * x)445 static void lincs_update_atoms(Lincs* li,
446 int th,
447 real preFactor,
448 gmx::ArrayRef<const real> fac,
449 gmx::ArrayRef<const gmx::RVec> r,
450 const real* invmass,
451 rvec* x)
452 {
453 if (li->ntask == 1)
454 {
455 /* Single thread, we simply update for all constraints */
456 lincs_update_atoms_noind(li->nc_real, li->atoms, preFactor, fac, r, invmass, x);
457 }
458 else
459 {
460 /* Update the atom vector components for our thread local
461 * constraints that only access our local atom range.
462 * This can be done without a barrier.
463 */
464 lincs_update_atoms_ind(li->task[th].ind, li->atoms, preFactor, fac, r, invmass, x);
465
466 if (!li->task[li->ntask].ind.empty())
467 {
468 /* Update the constraints that operate on atoms
469 * in multiple thread atom blocks on the master thread.
470 */
471 #pragma omp barrier
472 #pragma omp master
473 {
474 lincs_update_atoms_ind(li->task[li->ntask].ind, li->atoms, preFactor, fac, r, invmass, x);
475 }
476 }
477 }
478 }
479
480 #if GMX_SIMD_HAVE_REAL
481 //! Helper function so that we can run TSAN with SIMD support (where implemented).
482 template<int align>
gatherLoadUTransposeTSANSafe(const real * base,const std::int32_t * offset,SimdReal * v0,SimdReal * v1,SimdReal * v2)483 static inline void gmx_simdcall gatherLoadUTransposeTSANSafe(const real* base,
484 const std::int32_t* offset,
485 SimdReal* v0,
486 SimdReal* v1,
487 SimdReal* v2)
488 {
489 # if (CMAKE_BUILD_TYPE == CMAKE_BUILD_TYPE_TSAN) && GMX_SIMD_X86_AVX2_256
490 // This function is only implemented in this case
491 gatherLoadUTransposeSafe<align>(base, offset, v0, v1, v2);
492 # else
493 gatherLoadUTranspose<align>(base, offset, v0, v1, v2);
494 # endif
495 }
496
497 /*! \brief Calculate the constraint distance vectors r to project on from x.
498 *
499 * Determine the right-hand side of the matrix equation using quantity f.
500 * This function only differs from calc_dr_x_xp_simd below in that
501 * no constraint length is subtracted and no PBC is used for f. */
calc_dr_x_f_simd(int b0,int b1,gmx::ArrayRef<const AtomPair> atoms,const rvec * gmx_restrict x,const rvec * gmx_restrict f,const real * gmx_restrict blc,const real * pbc_simd,rvec * gmx_restrict r,real * gmx_restrict rhs,real * gmx_restrict sol)502 static void gmx_simdcall calc_dr_x_f_simd(int b0,
503 int b1,
504 gmx::ArrayRef<const AtomPair> atoms,
505 const rvec* gmx_restrict x,
506 const rvec* gmx_restrict f,
507 const real* gmx_restrict blc,
508 const real* pbc_simd,
509 rvec* gmx_restrict r,
510 real* gmx_restrict rhs,
511 real* gmx_restrict sol)
512 {
513 assert(b0 % GMX_SIMD_REAL_WIDTH == 0);
514
515 alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset2[GMX_SIMD_REAL_WIDTH];
516
517 for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
518 {
519 offset2[i] = i;
520 }
521
522 for (int bs = b0; bs < b1; bs += GMX_SIMD_REAL_WIDTH)
523 {
524 SimdReal x0_S, y0_S, z0_S;
525 SimdReal x1_S, y1_S, z1_S;
526 SimdReal rx_S, ry_S, rz_S, n2_S, il_S;
527 SimdReal fx_S, fy_S, fz_S, ip_S, rhs_S;
528 alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset0[GMX_SIMD_REAL_WIDTH];
529 alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset1[GMX_SIMD_REAL_WIDTH];
530
531 for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
532 {
533 offset0[i] = atoms[bs + i].index1;
534 offset1[i] = atoms[bs + i].index2;
535 }
536
537 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset0, &x0_S, &y0_S, &z0_S);
538 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset1, &x1_S, &y1_S, &z1_S);
539 rx_S = x0_S - x1_S;
540 ry_S = y0_S - y1_S;
541 rz_S = z0_S - z1_S;
542
543 pbc_correct_dx_simd(&rx_S, &ry_S, &rz_S, pbc_simd);
544
545 n2_S = norm2(rx_S, ry_S, rz_S);
546 il_S = invsqrt(n2_S);
547
548 rx_S = rx_S * il_S;
549 ry_S = ry_S * il_S;
550 rz_S = rz_S * il_S;
551
552 transposeScatterStoreU<3>(reinterpret_cast<real*>(r + bs), offset2, rx_S, ry_S, rz_S);
553
554 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(f), offset0, &x0_S, &y0_S, &z0_S);
555 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(f), offset1, &x1_S, &y1_S, &z1_S);
556 fx_S = x0_S - x1_S;
557 fy_S = y0_S - y1_S;
558 fz_S = z0_S - z1_S;
559
560 ip_S = iprod(rx_S, ry_S, rz_S, fx_S, fy_S, fz_S);
561
562 rhs_S = load<SimdReal>(blc + bs) * ip_S;
563
564 store(rhs + bs, rhs_S);
565 store(sol + bs, rhs_S);
566 }
567 }
568 #endif // GMX_SIMD_HAVE_REAL
569
570 /*! \brief LINCS projection, works on derivatives of the coordinates. */
do_lincsp(ArrayRefWithPadding<const RVec> xPadded,ArrayRefWithPadding<RVec> fPadded,ArrayRef<RVec> fp,t_pbc * pbc,Lincs * lincsd,int th,const real * invmass,ConstraintVariable econq,bool bCalcDHDL,bool bCalcVir,tensor rmdf)571 static void do_lincsp(ArrayRefWithPadding<const RVec> xPadded,
572 ArrayRefWithPadding<RVec> fPadded,
573 ArrayRef<RVec> fp,
574 t_pbc* pbc,
575 Lincs* lincsd,
576 int th,
577 const real* invmass,
578 ConstraintVariable econq,
579 bool bCalcDHDL,
580 bool bCalcVir,
581 tensor rmdf)
582 {
583 const int b0 = lincsd->task[th].b0;
584 const int b1 = lincsd->task[th].b1;
585
586 gmx::ArrayRef<const AtomPair> atoms = lincsd->atoms;
587 gmx::ArrayRef<gmx::RVec> r = lincsd->tmpv;
588 gmx::ArrayRef<const int> blnr = lincsd->blnr;
589 gmx::ArrayRef<const int> blbnb = lincsd->blbnb;
590
591 gmx::ArrayRef<const real> blc;
592 gmx::ArrayRef<const real> blmf;
593 if (econq != ConstraintVariable::Force)
594 {
595 /* Use mass-weighted parameters */
596 blc = lincsd->blc;
597 blmf = lincsd->blmf;
598 }
599 else
600 {
601 /* Use non mass-weighted parameters */
602 blc = lincsd->blc1;
603 blmf = lincsd->blmf1;
604 }
605 gmx::ArrayRef<real> blcc = lincsd->tmpncc;
606 gmx::ArrayRef<real> rhs1 = lincsd->tmp1;
607 gmx::ArrayRef<real> rhs2 = lincsd->tmp2;
608 gmx::ArrayRef<real> sol = lincsd->tmp3;
609
610 const rvec* x = as_rvec_array(xPadded.paddedArrayRef().data());
611 rvec* f = as_rvec_array(fPadded.paddedArrayRef().data());
612
613 #if GMX_SIMD_HAVE_REAL
614 /* This SIMD code does the same as the plain-C code after the #else.
615 * The only difference is that we always call pbc code, as with SIMD
616 * the overhead of pbc computation (when not needed) is small.
617 */
618 alignas(GMX_SIMD_ALIGNMENT) real pbc_simd[9 * GMX_SIMD_REAL_WIDTH];
619
620 /* Convert the pbc struct for SIMD */
621 set_pbc_simd(pbc, pbc_simd);
622
623 /* Compute normalized x i-j vectors, store in r.
624 * Compute the inner product of r and xp i-j and store in rhs1.
625 */
626 calc_dr_x_f_simd(b0, b1, atoms, x, f, blc.data(), pbc_simd, as_rvec_array(r.data()),
627 rhs1.data(), sol.data());
628
629 #else // GMX_SIMD_HAVE_REAL
630
631 /* Compute normalized i-j vectors */
632 if (pbc)
633 {
634 for (int b = b0; b < b1; b++)
635 {
636 rvec dx;
637
638 pbc_dx_aiuc(pbc, x[atoms[b].index1], x[atoms[b].index2], dx);
639 unitv(dx, r[b]);
640 }
641 }
642 else
643 {
644 for (int b = b0; b < b1; b++)
645 {
646 rvec dx;
647
648 rvec_sub(x[atoms[b].index1], x[atoms[b].index2], dx);
649 unitv(dx, r[b]);
650 } /* 16 ncons flops */
651 }
652
653 for (int b = b0; b < b1; b++)
654 {
655 int i = atoms[b].index1;
656 int j = atoms[b].index2;
657 real mvb = blc[b]
658 * (r[b][0] * (f[i][0] - f[j][0]) + r[b][1] * (f[i][1] - f[j][1])
659 + r[b][2] * (f[i][2] - f[j][2]));
660 rhs1[b] = mvb;
661 sol[b] = mvb;
662 /* 7 flops */
663 }
664
665 #endif // GMX_SIMD_HAVE_REAL
666
667 if (lincsd->bTaskDep)
668 {
669 /* We need a barrier, since the matrix construction below
670 * can access entries in r of other threads.
671 */
672 #pragma omp barrier
673 }
674
675 /* Construct the (sparse) LINCS matrix */
676 for (int b = b0; b < b1; b++)
677 {
678 for (int n = blnr[b]; n < blnr[b + 1]; n++)
679 {
680 blcc[n] = blmf[n] * ::iprod(r[b], r[blbnb[n]]);
681 } /* 6 nr flops */
682 }
683 /* Together: 23*ncons + 6*nrtot flops */
684
685 lincs_matrix_expand(*lincsd, lincsd->task[th], blcc, rhs1, rhs2, sol);
686 /* nrec*(ncons+2*nrtot) flops */
687
688 if (econq == ConstraintVariable::Deriv_FlexCon)
689 {
690 /* We only want to constraint the flexible constraints,
691 * so we mask out the normal ones by setting sol to 0.
692 */
693 for (int b = b0; b < b1; b++)
694 {
695 if (!(lincsd->bllen0[b] == 0 && lincsd->ddist[b] == 0))
696 {
697 sol[b] = 0;
698 }
699 }
700 }
701
702 /* We multiply sol by blc, so we can use lincs_update_atoms for OpenMP */
703 for (int b = b0; b < b1; b++)
704 {
705 sol[b] *= blc[b];
706 }
707
708 /* When constraining forces, we should not use mass weighting,
709 * so we pass invmass=NULL, which results in the use of 1 for all atoms.
710 */
711 lincs_update_atoms(lincsd, th, 1.0, sol, r, (econq != ConstraintVariable::Force) ? invmass : nullptr,
712 as_rvec_array(fp.data()));
713
714 if (bCalcDHDL)
715 {
716 real dhdlambda = 0;
717 for (int b = b0; b < b1; b++)
718 {
719 dhdlambda -= sol[b] * lincsd->ddist[b];
720 }
721
722 lincsd->task[th].dhdlambda = dhdlambda;
723 }
724
725 if (bCalcVir)
726 {
727 /* Constraint virial,
728 * determines sum r_bond x delta f,
729 * where delta f is the constraint correction
730 * of the quantity that is being constrained.
731 */
732 for (int b = b0; b < b1; b++)
733 {
734 const real mvb = lincsd->bllen[b] * sol[b];
735 for (int i = 0; i < DIM; i++)
736 {
737 const real tmp1 = mvb * r[b][i];
738 for (int j = 0; j < DIM; j++)
739 {
740 rmdf[i][j] += tmp1 * r[b][j];
741 }
742 }
743 } /* 23 ncons flops */
744 }
745 }
746
747 #if GMX_SIMD_HAVE_REAL
748
749 /*! \brief Calculate the constraint distance vectors r to project on from x.
750 *
751 * Determine the right-hand side of the matrix equation using coordinates xp. */
calc_dr_x_xp_simd(int b0,int b1,gmx::ArrayRef<const AtomPair> atoms,const rvec * gmx_restrict x,const rvec * gmx_restrict xp,const real * gmx_restrict bllen,const real * gmx_restrict blc,const real * pbc_simd,rvec * gmx_restrict r,real * gmx_restrict rhs,real * gmx_restrict sol)752 static void gmx_simdcall calc_dr_x_xp_simd(int b0,
753 int b1,
754 gmx::ArrayRef<const AtomPair> atoms,
755 const rvec* gmx_restrict x,
756 const rvec* gmx_restrict xp,
757 const real* gmx_restrict bllen,
758 const real* gmx_restrict blc,
759 const real* pbc_simd,
760 rvec* gmx_restrict r,
761 real* gmx_restrict rhs,
762 real* gmx_restrict sol)
763 {
764 assert(b0 % GMX_SIMD_REAL_WIDTH == 0);
765 alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset2[GMX_SIMD_REAL_WIDTH];
766
767 for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
768 {
769 offset2[i] = i;
770 }
771
772 for (int bs = b0; bs < b1; bs += GMX_SIMD_REAL_WIDTH)
773 {
774 SimdReal x0_S, y0_S, z0_S;
775 SimdReal x1_S, y1_S, z1_S;
776 SimdReal rx_S, ry_S, rz_S, n2_S, il_S;
777 SimdReal rxp_S, ryp_S, rzp_S, ip_S, rhs_S;
778 alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset0[GMX_SIMD_REAL_WIDTH];
779 alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset1[GMX_SIMD_REAL_WIDTH];
780
781 for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
782 {
783 offset0[i] = atoms[bs + i].index1;
784 offset1[i] = atoms[bs + i].index2;
785 }
786
787 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset0, &x0_S, &y0_S, &z0_S);
788 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset1, &x1_S, &y1_S, &z1_S);
789 rx_S = x0_S - x1_S;
790 ry_S = y0_S - y1_S;
791 rz_S = z0_S - z1_S;
792
793 pbc_correct_dx_simd(&rx_S, &ry_S, &rz_S, pbc_simd);
794
795 n2_S = norm2(rx_S, ry_S, rz_S);
796 il_S = invsqrt(n2_S);
797
798 rx_S = rx_S * il_S;
799 ry_S = ry_S * il_S;
800 rz_S = rz_S * il_S;
801
802 transposeScatterStoreU<3>(reinterpret_cast<real*>(r + bs), offset2, rx_S, ry_S, rz_S);
803
804 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(xp), offset0, &x0_S, &y0_S, &z0_S);
805 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(xp), offset1, &x1_S, &y1_S, &z1_S);
806
807 rxp_S = x0_S - x1_S;
808 ryp_S = y0_S - y1_S;
809 rzp_S = z0_S - z1_S;
810
811 pbc_correct_dx_simd(&rxp_S, &ryp_S, &rzp_S, pbc_simd);
812
813 ip_S = iprod(rx_S, ry_S, rz_S, rxp_S, ryp_S, rzp_S);
814
815 rhs_S = load<SimdReal>(blc + bs) * (ip_S - load<SimdReal>(bllen + bs));
816
817 store(rhs + bs, rhs_S);
818 store(sol + bs, rhs_S);
819 }
820 }
821 #endif // GMX_SIMD_HAVE_REAL
822
823 /*! \brief Determine the distances and right-hand side for the next iteration. */
calc_dist_iter(int b0,int b1,gmx::ArrayRef<const AtomPair> atoms,const rvec * gmx_restrict xp,const real * gmx_restrict bllen,const real * gmx_restrict blc,const t_pbc * pbc,real wfac,real * gmx_restrict rhs,real * gmx_restrict sol,bool * bWarn)824 gmx_unused static void calc_dist_iter(int b0,
825 int b1,
826 gmx::ArrayRef<const AtomPair> atoms,
827 const rvec* gmx_restrict xp,
828 const real* gmx_restrict bllen,
829 const real* gmx_restrict blc,
830 const t_pbc* pbc,
831 real wfac,
832 real* gmx_restrict rhs,
833 real* gmx_restrict sol,
834 bool* bWarn)
835 {
836 for (int b = b0; b < b1; b++)
837 {
838 real len = bllen[b];
839 rvec dx;
840 if (pbc)
841 {
842 pbc_dx_aiuc(pbc, xp[atoms[b].index1], xp[atoms[b].index2], dx);
843 }
844 else
845 {
846 rvec_sub(xp[atoms[b].index1], xp[atoms[b].index2], dx);
847 }
848 real len2 = len * len;
849 real dlen2 = 2 * len2 - ::norm2(dx);
850 if (dlen2 < wfac * len2)
851 {
852 /* not race free - see detailed comment in caller */
853 *bWarn = TRUE;
854 }
855 real mvb;
856 if (dlen2 > 0)
857 {
858 mvb = blc[b] * (len - dlen2 * gmx::invsqrt(dlen2));
859 }
860 else
861 {
862 mvb = blc[b] * len;
863 }
864 rhs[b] = mvb;
865 sol[b] = mvb;
866 } /* 20*ncons flops */
867 }
868
869 #if GMX_SIMD_HAVE_REAL
870 /*! \brief As calc_dist_iter(), but using SIMD intrinsics. */
calc_dist_iter_simd(int b0,int b1,gmx::ArrayRef<const AtomPair> atoms,const rvec * gmx_restrict x,const real * gmx_restrict bllen,const real * gmx_restrict blc,const real * pbc_simd,real wfac,real * gmx_restrict rhs,real * gmx_restrict sol,bool * bWarn)871 static void gmx_simdcall calc_dist_iter_simd(int b0,
872 int b1,
873 gmx::ArrayRef<const AtomPair> atoms,
874 const rvec* gmx_restrict x,
875 const real* gmx_restrict bllen,
876 const real* gmx_restrict blc,
877 const real* pbc_simd,
878 real wfac,
879 real* gmx_restrict rhs,
880 real* gmx_restrict sol,
881 bool* bWarn)
882 {
883 SimdReal min_S(GMX_REAL_MIN);
884 SimdReal two_S(2.0);
885 SimdReal wfac_S(wfac);
886 SimdBool warn_S;
887
888 assert(b0 % GMX_SIMD_REAL_WIDTH == 0);
889
890 /* Initialize all to FALSE */
891 warn_S = (two_S < setZero());
892
893 for (int bs = b0; bs < b1; bs += GMX_SIMD_REAL_WIDTH)
894 {
895 SimdReal x0_S, y0_S, z0_S;
896 SimdReal x1_S, y1_S, z1_S;
897 SimdReal rx_S, ry_S, rz_S, n2_S;
898 SimdReal len_S, len2_S, dlen2_S, lc_S, blc_S;
899 alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset0[GMX_SIMD_REAL_WIDTH];
900 alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset1[GMX_SIMD_REAL_WIDTH];
901
902 for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
903 {
904 offset0[i] = atoms[bs + i].index1;
905 offset1[i] = atoms[bs + i].index2;
906 }
907
908 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset0, &x0_S, &y0_S, &z0_S);
909 gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset1, &x1_S, &y1_S, &z1_S);
910
911 rx_S = x0_S - x1_S;
912 ry_S = y0_S - y1_S;
913 rz_S = z0_S - z1_S;
914
915 pbc_correct_dx_simd(&rx_S, &ry_S, &rz_S, pbc_simd);
916
917 n2_S = norm2(rx_S, ry_S, rz_S);
918
919 len_S = load<SimdReal>(bllen + bs);
920 len2_S = len_S * len_S;
921
922 dlen2_S = fms(two_S, len2_S, n2_S);
923
924 warn_S = warn_S || (dlen2_S < (wfac_S * len2_S));
925
926 /* Avoid 1/0 by taking the max with REAL_MIN.
927 * Note: when dlen2 is close to zero (90 degree constraint rotation),
928 * the accuracy of the algorithm is no longer relevant.
929 */
930 dlen2_S = max(dlen2_S, min_S);
931
932 lc_S = fnma(dlen2_S, invsqrt(dlen2_S), len_S);
933
934 blc_S = load<SimdReal>(blc + bs);
935
936 lc_S = blc_S * lc_S;
937
938 store(rhs + bs, lc_S);
939 store(sol + bs, lc_S);
940 }
941
942 if (anyTrue(warn_S))
943 {
944 *bWarn = TRUE;
945 }
946 }
947 #endif // GMX_SIMD_HAVE_REAL
948
949 //! Implements LINCS constraining.
do_lincs(ArrayRefWithPadding<const RVec> xPadded,ArrayRefWithPadding<RVec> xpPadded,const matrix box,t_pbc * pbc,Lincs * lincsd,int th,const real * invmass,const t_commrec * cr,bool bCalcDHDL,real wangle,bool * bWarn,real invdt,ArrayRef<RVec> vRef,bool bCalcVir,tensor vir_r_m_dr)950 static void do_lincs(ArrayRefWithPadding<const RVec> xPadded,
951 ArrayRefWithPadding<RVec> xpPadded,
952 const matrix box,
953 t_pbc* pbc,
954 Lincs* lincsd,
955 int th,
956 const real* invmass,
957 const t_commrec* cr,
958 bool bCalcDHDL,
959 real wangle,
960 bool* bWarn,
961 real invdt,
962 ArrayRef<RVec> vRef,
963 bool bCalcVir,
964 tensor vir_r_m_dr)
965 {
966 const rvec* x = as_rvec_array(xPadded.paddedArrayRef().data());
967 rvec* xp = as_rvec_array(xpPadded.paddedArrayRef().data());
968 rvec* gmx_restrict v = as_rvec_array(vRef.data());
969
970 const int b0 = lincsd->task[th].b0;
971 const int b1 = lincsd->task[th].b1;
972
973 gmx::ArrayRef<const AtomPair> atoms = lincsd->atoms;
974 gmx::ArrayRef<gmx::RVec> r = lincsd->tmpv;
975 gmx::ArrayRef<const int> blnr = lincsd->blnr;
976 gmx::ArrayRef<const int> blbnb = lincsd->blbnb;
977 gmx::ArrayRef<const real> blc = lincsd->blc;
978 gmx::ArrayRef<const real> blmf = lincsd->blmf;
979 gmx::ArrayRef<const real> bllen = lincsd->bllen;
980 gmx::ArrayRef<real> blcc = lincsd->tmpncc;
981 gmx::ArrayRef<real> rhs1 = lincsd->tmp1;
982 gmx::ArrayRef<real> rhs2 = lincsd->tmp2;
983 gmx::ArrayRef<real> sol = lincsd->tmp3;
984 gmx::ArrayRef<real> blc_sol = lincsd->tmp4;
985 gmx::ArrayRef<real> mlambda = lincsd->mlambda;
986 gmx::ArrayRef<const int> nlocat = lincsd->nlocat;
987
988 #if GMX_SIMD_HAVE_REAL
989
990 /* This SIMD code does the same as the plain-C code after the #else.
991 * The only difference is that we always call pbc code, as with SIMD
992 * the overhead of pbc computation (when not needed) is small.
993 */
994 alignas(GMX_SIMD_ALIGNMENT) real pbc_simd[9 * GMX_SIMD_REAL_WIDTH];
995
996 /* Convert the pbc struct for SIMD */
997 set_pbc_simd(pbc, pbc_simd);
998
999 /* Compute normalized x i-j vectors, store in r.
1000 * Compute the inner product of r and xp i-j and store in rhs1.
1001 */
1002 calc_dr_x_xp_simd(b0, b1, atoms, x, xp, bllen.data(), blc.data(), pbc_simd,
1003 as_rvec_array(r.data()), rhs1.data(), sol.data());
1004
1005 #else // GMX_SIMD_HAVE_REAL
1006
1007 if (pbc)
1008 {
1009 /* Compute normalized i-j vectors */
1010 for (int b = b0; b < b1; b++)
1011 {
1012 rvec dx;
1013 pbc_dx_aiuc(pbc, x[atoms[b].index1], x[atoms[b].index2], dx);
1014 unitv(dx, r[b]);
1015
1016 pbc_dx_aiuc(pbc, xp[atoms[b].index1], xp[atoms[b].index2], dx);
1017 real mvb = blc[b] * (::iprod(r[b], dx) - bllen[b]);
1018 rhs1[b] = mvb;
1019 sol[b] = mvb;
1020 }
1021 }
1022 else
1023 {
1024 /* Compute normalized i-j vectors */
1025 for (int b = b0; b < b1; b++)
1026 {
1027 int i = atoms[b].index1;
1028 int j = atoms[b].index2;
1029 real tmp0 = x[i][0] - x[j][0];
1030 real tmp1 = x[i][1] - x[j][1];
1031 real tmp2 = x[i][2] - x[j][2];
1032 real rlen = gmx::invsqrt(tmp0 * tmp0 + tmp1 * tmp1 + tmp2 * tmp2);
1033 r[b][0] = rlen * tmp0;
1034 r[b][1] = rlen * tmp1;
1035 r[b][2] = rlen * tmp2;
1036 /* 16 ncons flops */
1037
1038 real mvb = blc[b]
1039 * (r[b][0] * (xp[i][0] - xp[j][0]) + r[b][1] * (xp[i][1] - xp[j][1])
1040 + r[b][2] * (xp[i][2] - xp[j][2]) - bllen[b]);
1041 rhs1[b] = mvb;
1042 sol[b] = mvb;
1043 /* 10 flops */
1044 }
1045 /* Together: 26*ncons + 6*nrtot flops */
1046 }
1047
1048 #endif // GMX_SIMD_HAVE_REAL
1049
1050 if (lincsd->bTaskDep)
1051 {
1052 /* We need a barrier, since the matrix construction below
1053 * can access entries in r of other threads.
1054 */
1055 #pragma omp barrier
1056 }
1057
1058 /* Construct the (sparse) LINCS matrix */
1059 for (int b = b0; b < b1; b++)
1060 {
1061 for (int n = blnr[b]; n < blnr[b + 1]; n++)
1062 {
1063 blcc[n] = blmf[n] * gmx::dot(r[b], r[blbnb[n]]);
1064 }
1065 }
1066 /* Together: 26*ncons + 6*nrtot flops */
1067
1068 lincs_matrix_expand(*lincsd, lincsd->task[th], blcc, rhs1, rhs2, sol);
1069 /* nrec*(ncons+2*nrtot) flops */
1070
1071 #if GMX_SIMD_HAVE_REAL
1072 for (int b = b0; b < b1; b += GMX_SIMD_REAL_WIDTH)
1073 {
1074 SimdReal t1 = load<SimdReal>(blc.data() + b);
1075 SimdReal t2 = load<SimdReal>(sol.data() + b);
1076 store(mlambda.data() + b, t1 * t2);
1077 }
1078 #else
1079 for (int b = b0; b < b1; b++)
1080 {
1081 mlambda[b] = blc[b] * sol[b];
1082 }
1083 #endif // GMX_SIMD_HAVE_REAL
1084
1085 /* Update the coordinates */
1086 lincs_update_atoms(lincsd, th, 1.0, mlambda, r, invmass, xp);
1087
1088 /*
1089 ******** Correction for centripetal effects ********
1090 */
1091
1092 real wfac;
1093
1094 wfac = std::cos(DEG2RAD * wangle);
1095 wfac = wfac * wfac;
1096
1097 for (int iter = 0; iter < lincsd->nIter; iter++)
1098 {
1099 if ((lincsd->bCommIter && DOMAINDECOMP(cr) && cr->dd->constraints))
1100 {
1101 #pragma omp barrier
1102 #pragma omp master
1103 {
1104 /* Communicate the corrected non-local coordinates */
1105 if (DOMAINDECOMP(cr))
1106 {
1107 dd_move_x_constraints(cr->dd, box, xpPadded.unpaddedArrayRef(), ArrayRef<RVec>(), FALSE);
1108 }
1109 }
1110 #pragma omp barrier
1111 }
1112 else if (lincsd->bTaskDep)
1113 {
1114 #pragma omp barrier
1115 }
1116
1117 #if GMX_SIMD_HAVE_REAL
1118 calc_dist_iter_simd(b0, b1, atoms, xp, bllen.data(), blc.data(), pbc_simd, wfac,
1119 rhs1.data(), sol.data(), bWarn);
1120 #else
1121 calc_dist_iter(b0, b1, atoms, xp, bllen.data(), blc.data(), pbc, wfac, rhs1.data(),
1122 sol.data(), bWarn);
1123 /* 20*ncons flops */
1124 #endif // GMX_SIMD_HAVE_REAL
1125
1126 lincs_matrix_expand(*lincsd, lincsd->task[th], blcc, rhs1, rhs2, sol);
1127 /* nrec*(ncons+2*nrtot) flops */
1128
1129 #if GMX_SIMD_HAVE_REAL
1130 for (int b = b0; b < b1; b += GMX_SIMD_REAL_WIDTH)
1131 {
1132 SimdReal t1 = load<SimdReal>(blc.data() + b);
1133 SimdReal t2 = load<SimdReal>(sol.data() + b);
1134 SimdReal mvb = t1 * t2;
1135 store(blc_sol.data() + b, mvb);
1136 store(mlambda.data() + b, load<SimdReal>(mlambda.data() + b) + mvb);
1137 }
1138 #else
1139 for (int b = b0; b < b1; b++)
1140 {
1141 real mvb = blc[b] * sol[b];
1142 blc_sol[b] = mvb;
1143 mlambda[b] += mvb;
1144 }
1145 #endif // GMX_SIMD_HAVE_REAL
1146
1147 /* Update the coordinates */
1148 lincs_update_atoms(lincsd, th, 1.0, blc_sol, r, invmass, xp);
1149 }
1150 /* nit*ncons*(37+9*nrec) flops */
1151
1152 if (v != nullptr)
1153 {
1154 /* Update the velocities */
1155 lincs_update_atoms(lincsd, th, invdt, mlambda, r, invmass, v);
1156 /* 16 ncons flops */
1157 }
1158
1159 if (!nlocat.empty() && (bCalcDHDL || bCalcVir))
1160 {
1161 if (lincsd->bTaskDep)
1162 {
1163 /* In lincs_update_atoms threads might cross-read mlambda */
1164 #pragma omp barrier
1165 }
1166
1167 /* Only account for local atoms */
1168 for (int b = b0; b < b1; b++)
1169 {
1170 mlambda[b] *= 0.5 * nlocat[b];
1171 }
1172 }
1173
1174 if (bCalcDHDL)
1175 {
1176 real dhdl = 0;
1177 for (int b = b0; b < b1; b++)
1178 {
1179 /* Note that this this is dhdl*dt^2, the dt^2 factor is corrected
1180 * later after the contributions are reduced over the threads.
1181 */
1182 dhdl -= lincsd->mlambda[b] * lincsd->ddist[b];
1183 }
1184 lincsd->task[th].dhdlambda = dhdl;
1185 }
1186
1187 if (bCalcVir)
1188 {
1189 /* Constraint virial */
1190 for (int b = b0; b < b1; b++)
1191 {
1192 real tmp0 = -bllen[b] * mlambda[b];
1193 for (int i = 0; i < DIM; i++)
1194 {
1195 real tmp1 = tmp0 * r[b][i];
1196 for (int j = 0; j < DIM; j++)
1197 {
1198 vir_r_m_dr[i][j] -= tmp1 * r[b][j];
1199 }
1200 }
1201 } /* 22 ncons flops */
1202 }
1203
1204 /* Total:
1205 * 26*ncons + 6*nrtot + nrec*(ncons+2*nrtot)
1206 * + nit * (20*ncons + nrec*(ncons+2*nrtot) + 17 ncons)
1207 *
1208 * (26+nrec)*ncons + (6+2*nrec)*nrtot
1209 * + nit * ((37+nrec)*ncons + 2*nrec*nrtot)
1210 * if nit=1
1211 * (63+nrec)*ncons + (6+4*nrec)*nrtot
1212 */
1213 }
1214
1215 /*! \brief Sets the elements in the LINCS matrix for task task. */
set_lincs_matrix_task(Lincs * li,Task * li_task,const real * invmass,int * ncc_triangle,int * nCrossTaskTriangles)1216 static void set_lincs_matrix_task(Lincs* li, Task* li_task, const real* invmass, int* ncc_triangle, int* nCrossTaskTriangles)
1217 {
1218 /* Construct the coupling coefficient matrix blmf */
1219 li_task->ntriangle = 0;
1220 *ncc_triangle = 0;
1221 *nCrossTaskTriangles = 0;
1222 for (int i = li_task->b0; i < li_task->b1; i++)
1223 {
1224 const int a1 = li->atoms[i].index1;
1225 const int a2 = li->atoms[i].index2;
1226 for (int n = li->blnr[i]; n < li->blnr[i + 1]; n++)
1227 {
1228 const int k = li->blbnb[n];
1229
1230 /* If we are using multiple, independent tasks for LINCS,
1231 * the calls to check_assign_connected should have
1232 * put all connected constraints in our task.
1233 */
1234 assert(li->bTaskDep || (k >= li_task->b0 && k < li_task->b1));
1235
1236 int sign;
1237 if (a1 == li->atoms[k].index1 || a2 == li->atoms[k].index2)
1238 {
1239 sign = -1;
1240 }
1241 else
1242 {
1243 sign = 1;
1244 }
1245 int center;
1246 int end;
1247 if (a1 == li->atoms[k].index1 || a1 == li->atoms[k].index2)
1248 {
1249 center = a1;
1250 end = a2;
1251 }
1252 else
1253 {
1254 center = a2;
1255 end = a1;
1256 }
1257 li->blmf[n] = sign * invmass[center] * li->blc[i] * li->blc[k];
1258 li->blmf1[n] = sign * 0.5;
1259 if (li->ncg_triangle > 0)
1260 {
1261 /* Look for constraint triangles */
1262 for (int nk = li->blnr[k]; nk < li->blnr[k + 1]; nk++)
1263 {
1264 const int kk = li->blbnb[nk];
1265 if (kk != i && kk != k && (li->atoms[kk].index1 == end || li->atoms[kk].index2 == end))
1266 {
1267 /* Check if the constraints in this triangle actually
1268 * belong to a different task. We still assign them
1269 * here, since it's convenient for the triangle
1270 * iterations, but we then need an extra barrier.
1271 */
1272 if (k < li_task->b0 || k >= li_task->b1 || kk < li_task->b0 || kk >= li_task->b1)
1273 {
1274 (*nCrossTaskTriangles)++;
1275 }
1276
1277 if (li_task->ntriangle == 0 || li_task->triangle[li_task->ntriangle - 1] < i)
1278 {
1279 /* Add this constraint to the triangle list */
1280 li_task->triangle[li_task->ntriangle] = i;
1281 li_task->tri_bits[li_task->ntriangle] = 0;
1282 li_task->ntriangle++;
1283 if (li->blnr[i + 1] - li->blnr[i]
1284 > static_cast<int>(sizeof(li_task->tri_bits[0]) * 8 - 1))
1285 {
1286 gmx_fatal(FARGS,
1287 "A constraint is connected to %d constraints, this is "
1288 "more than the %zu allowed for constraints participating "
1289 "in triangles",
1290 li->blnr[i + 1] - li->blnr[i],
1291 sizeof(li_task->tri_bits[0]) * 8 - 1);
1292 }
1293 }
1294 li_task->tri_bits[li_task->ntriangle - 1] |= (1 << (n - li->blnr[i]));
1295 (*ncc_triangle)++;
1296 }
1297 }
1298 }
1299 }
1300 }
1301 }
1302
1303 /*! \brief Sets the elements in the LINCS matrix. */
set_lincs_matrix(Lincs * li,const real * invmass,real lambda)1304 static void set_lincs_matrix(Lincs* li, const real* invmass, real lambda)
1305 {
1306 const real invsqrt2 = 0.7071067811865475244;
1307
1308 for (int i = 0; (i < li->nc); i++)
1309 {
1310 const int a1 = li->atoms[i].index1;
1311 const int a2 = li->atoms[i].index2;
1312 li->blc[i] = gmx::invsqrt(invmass[a1] + invmass[a2]);
1313 li->blc1[i] = invsqrt2;
1314 }
1315
1316 /* Construct the coupling coefficient matrix blmf */
1317 int ntriangle = 0, ncc_triangle = 0, nCrossTaskTriangles = 0;
1318 #pragma omp parallel for reduction(+: ntriangle, ncc_triangle, nCrossTaskTriangles) num_threads(li->ntask) schedule(static)
1319 for (int th = 0; th < li->ntask; th++)
1320 {
1321 try
1322 {
1323 set_lincs_matrix_task(li, &li->task[th], invmass, &ncc_triangle, &nCrossTaskTriangles);
1324 ntriangle += li->task[th].ntriangle;
1325 }
1326 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1327 }
1328 li->ntriangle = ntriangle;
1329 li->ncc_triangle = ncc_triangle;
1330 li->bTaskDepTri = (nCrossTaskTriangles > 0);
1331
1332 if (debug)
1333 {
1334 fprintf(debug, "The %d constraints participate in %d triangles\n", li->nc, li->ntriangle);
1335 fprintf(debug, "There are %d constraint couplings, of which %d in triangles\n", li->ncc,
1336 li->ncc_triangle);
1337 if (li->ntriangle > 0 && li->ntask > 1)
1338 {
1339 fprintf(debug,
1340 "%d constraint triangles contain constraints assigned to different tasks\n",
1341 nCrossTaskTriangles);
1342 }
1343 }
1344
1345 /* Set matlam,
1346 * so we know with which lambda value the masses have been set.
1347 */
1348 li->matlam = lambda;
1349 }
1350
1351 //! Finds all triangles of atoms that share constraints to a central atom.
count_triangle_constraints(const InteractionLists & ilist,const ListOfLists<int> & at2con)1352 static int count_triangle_constraints(const InteractionLists& ilist, const ListOfLists<int>& at2con)
1353 {
1354 const int ncon1 = ilist[F_CONSTR].size() / 3;
1355 const int ncon_tot = ncon1 + ilist[F_CONSTRNC].size() / 3;
1356
1357 gmx::ArrayRef<const int> ia1 = ilist[F_CONSTR].iatoms;
1358 gmx::ArrayRef<const int> ia2 = ilist[F_CONSTRNC].iatoms;
1359
1360 int ncon_triangle = 0;
1361 for (int c0 = 0; c0 < ncon_tot; c0++)
1362 {
1363 bool bTriangle = FALSE;
1364 const int* iap = constr_iatomptr(ia1, ia2, c0);
1365 const int a00 = iap[1];
1366 const int a01 = iap[2];
1367 for (const int c1 : at2con[a01])
1368 {
1369 if (c1 != c0)
1370 {
1371 const int* iap = constr_iatomptr(ia1, ia2, c1);
1372 const int a10 = iap[1];
1373 const int a11 = iap[2];
1374 int ac1;
1375 if (a10 == a01)
1376 {
1377 ac1 = a11;
1378 }
1379 else
1380 {
1381 ac1 = a10;
1382 }
1383 for (const int c2 : at2con[ac1])
1384 {
1385 if (c2 != c0 && c2 != c1)
1386 {
1387 const int* iap = constr_iatomptr(ia1, ia2, c2);
1388 const int a20 = iap[1];
1389 const int a21 = iap[2];
1390 if (a20 == a00 || a21 == a00)
1391 {
1392 bTriangle = TRUE;
1393 }
1394 }
1395 }
1396 }
1397 }
1398 if (bTriangle)
1399 {
1400 ncon_triangle++;
1401 }
1402 }
1403
1404 return ncon_triangle;
1405 }
1406
1407 //! Finds sequences of sequential constraints.
more_than_two_sequential_constraints(const InteractionLists & ilist,const ListOfLists<int> & at2con)1408 static bool more_than_two_sequential_constraints(const InteractionLists& ilist, const ListOfLists<int>& at2con)
1409 {
1410 const int ncon1 = ilist[F_CONSTR].size() / 3;
1411 const int ncon_tot = ncon1 + ilist[F_CONSTRNC].size() / 3;
1412
1413 gmx::ArrayRef<const int> ia1 = ilist[F_CONSTR].iatoms;
1414 gmx::ArrayRef<const int> ia2 = ilist[F_CONSTRNC].iatoms;
1415
1416 for (int c = 0; c < ncon_tot; c++)
1417 {
1418 const int* iap = constr_iatomptr(ia1, ia2, c);
1419 const int a1 = iap[1];
1420 const int a2 = iap[2];
1421 /* Check if this constraint has constraints connected at both atoms */
1422 if (at2con[a1].ssize() > 1 && at2con[a2].ssize() > 1)
1423 {
1424 return true;
1425 }
1426 }
1427
1428 return false;
1429 }
1430
init_lincs(FILE * fplog,const gmx_mtop_t & mtop,int nflexcon_global,ArrayRef<const ListOfLists<int>> atomToConstraintsPerMolType,bool bPLINCS,int nIter,int nProjOrder)1431 Lincs* init_lincs(FILE* fplog,
1432 const gmx_mtop_t& mtop,
1433 int nflexcon_global,
1434 ArrayRef<const ListOfLists<int>> atomToConstraintsPerMolType,
1435 bool bPLINCS,
1436 int nIter,
1437 int nProjOrder)
1438 {
1439 // TODO this should become a unique_ptr
1440 Lincs* li;
1441 bool bMoreThanTwoSeq;
1442
1443 if (fplog)
1444 {
1445 fprintf(fplog, "\nInitializing%s LINear Constraint Solver\n", bPLINCS ? " Parallel" : "");
1446 }
1447
1448 li = new Lincs;
1449
1450 li->ncg = gmx_mtop_ftype_count(mtop, F_CONSTR) + gmx_mtop_ftype_count(mtop, F_CONSTRNC);
1451 li->ncg_flex = nflexcon_global;
1452
1453 li->nIter = nIter;
1454 li->nOrder = nProjOrder;
1455
1456 li->max_connect = 0;
1457 for (size_t mt = 0; mt < mtop.moltype.size(); mt++)
1458 {
1459 const auto& at2con = atomToConstraintsPerMolType[mt];
1460 for (int a = 0; a < mtop.moltype[mt].atoms.nr; a++)
1461 {
1462 li->max_connect = std::max(li->max_connect, int(at2con[a].ssize()));
1463 }
1464 }
1465
1466 li->ncg_triangle = 0;
1467 bMoreThanTwoSeq = FALSE;
1468 for (const gmx_molblock_t& molb : mtop.molblock)
1469 {
1470 const gmx_moltype_t& molt = mtop.moltype[molb.type];
1471 const auto& at2con = atomToConstraintsPerMolType[molb.type];
1472
1473 li->ncg_triangle += molb.nmol * count_triangle_constraints(molt.ilist, at2con);
1474
1475 if (!bMoreThanTwoSeq && more_than_two_sequential_constraints(molt.ilist, at2con))
1476 {
1477 bMoreThanTwoSeq = TRUE;
1478 }
1479 }
1480
1481 /* Check if we need to communicate not only before LINCS,
1482 * but also before each iteration.
1483 * The check for only two sequential constraints is only
1484 * useful for the common case of H-bond only constraints.
1485 * With more effort we could also make it useful for small
1486 * molecules with nr. sequential constraints <= nOrder-1.
1487 */
1488 li->bCommIter = (bPLINCS && (li->nOrder < 1 || bMoreThanTwoSeq));
1489
1490 if (debug && bPLINCS)
1491 {
1492 fprintf(debug, "PLINCS communication before each iteration: %d\n", static_cast<int>(li->bCommIter));
1493 }
1494
1495 /* LINCS can run on any number of threads.
1496 * Currently the number is fixed for the whole simulation,
1497 * but it could be set in set_lincs().
1498 * The current constraint to task assignment code can create independent
1499 * tasks only when not more than two constraints are connected sequentially.
1500 */
1501 li->ntask = gmx_omp_nthreads_get(emntLINCS);
1502 li->bTaskDep = (li->ntask > 1 && bMoreThanTwoSeq);
1503 if (debug)
1504 {
1505 fprintf(debug, "LINCS: using %d threads, tasks are %sdependent\n", li->ntask,
1506 li->bTaskDep ? "" : "in");
1507 }
1508 if (li->ntask == 1)
1509 {
1510 li->task.resize(1);
1511 }
1512 else
1513 {
1514 /* Allocate an extra elements for "task-overlap" constraints */
1515 li->task.resize(li->ntask + 1);
1516 }
1517
1518 if (bPLINCS || li->ncg_triangle > 0)
1519 {
1520 please_cite(fplog, "Hess2008a");
1521 }
1522 else
1523 {
1524 please_cite(fplog, "Hess97a");
1525 }
1526
1527 if (fplog)
1528 {
1529 fprintf(fplog, "The number of constraints is %d\n", li->ncg);
1530 if (bPLINCS)
1531 {
1532 fprintf(fplog,
1533 "There are constraints between atoms in different decomposition domains,\n"
1534 "will communicate selected coordinates each lincs iteration\n");
1535 }
1536 if (li->ncg_triangle > 0)
1537 {
1538 fprintf(fplog,
1539 "%d constraints are involved in constraint triangles,\n"
1540 "will apply an additional matrix expansion of order %d for couplings\n"
1541 "between constraints inside triangles\n",
1542 li->ncg_triangle, li->nOrder);
1543 }
1544 }
1545
1546 return li;
1547 }
1548
done_lincs(Lincs * li)1549 void done_lincs(Lincs* li)
1550 {
1551 delete li;
1552 }
1553
1554 /*! \brief Sets up the work division over the threads. */
lincs_thread_setup(Lincs * li,int natoms)1555 static void lincs_thread_setup(Lincs* li, int natoms)
1556 {
1557 li->atf.resize(natoms);
1558
1559 gmx::ArrayRef<gmx_bitmask_t> atf = li->atf;
1560
1561 /* Clear the atom flags */
1562 for (gmx_bitmask_t& mask : atf)
1563 {
1564 bitmask_clear(&mask);
1565 }
1566
1567 if (li->ntask > BITMASK_SIZE)
1568 {
1569 gmx_fatal(FARGS, "More than %d threads is not supported for LINCS.", BITMASK_SIZE);
1570 }
1571
1572 for (int th = 0; th < li->ntask; th++)
1573 {
1574 const Task* li_task = &li->task[th];
1575
1576 /* For each atom set a flag for constraints from each */
1577 for (int b = li_task->b0; b < li_task->b1; b++)
1578 {
1579 bitmask_set_bit(&atf[li->atoms[b].index1], th);
1580 bitmask_set_bit(&atf[li->atoms[b].index2], th);
1581 }
1582 }
1583
1584 #pragma omp parallel for num_threads(li->ntask) schedule(static)
1585 for (int th = 0; th < li->ntask; th++)
1586 {
1587 try
1588 {
1589 Task* li_task;
1590 gmx_bitmask_t mask;
1591 int b;
1592
1593 li_task = &li->task[th];
1594
1595 bitmask_init_low_bits(&mask, th);
1596
1597 li_task->ind.clear();
1598 li_task->ind_r.clear();
1599 for (b = li_task->b0; b < li_task->b1; b++)
1600 {
1601 /* We let the constraint with the lowest thread index
1602 * operate on atoms with constraints from multiple threads.
1603 */
1604 if (bitmask_is_disjoint(atf[li->atoms[b].index1], mask)
1605 && bitmask_is_disjoint(atf[li->atoms[b].index2], mask))
1606 {
1607 /* Add the constraint to the local atom update index */
1608 li_task->ind.push_back(b);
1609 }
1610 else
1611 {
1612 /* Add the constraint to the rest block */
1613 li_task->ind_r.push_back(b);
1614 }
1615 }
1616 }
1617 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1618 }
1619
1620 /* We need to copy all constraints which have not be assigned
1621 * to a thread to a separate list which will be handled by one thread.
1622 */
1623 Task* li_m = &li->task[li->ntask];
1624
1625 li_m->ind.clear();
1626 for (int th = 0; th < li->ntask; th++)
1627 {
1628 const Task& li_task = li->task[th];
1629
1630 for (int ind_r : li_task.ind_r)
1631 {
1632 li_m->ind.push_back(ind_r);
1633 }
1634
1635 if (debug)
1636 {
1637 fprintf(debug, "LINCS thread %d: %zu constraints\n", th, li_task.ind.size());
1638 }
1639 }
1640
1641 if (debug)
1642 {
1643 fprintf(debug, "LINCS thread r: %zu constraints\n", li_m->ind.size());
1644 }
1645 }
1646
1647 //! Assign a constraint.
assign_constraint(Lincs * li,int constraint_index,int a1,int a2,real lenA,real lenB,const ListOfLists<int> & at2con)1648 static void assign_constraint(Lincs* li,
1649 int constraint_index,
1650 int a1,
1651 int a2,
1652 real lenA,
1653 real lenB,
1654 const ListOfLists<int>& at2con)
1655 {
1656 int con;
1657
1658 con = li->nc;
1659
1660 /* Make an mapping of local topology constraint index to LINCS index */
1661 li->con_index[constraint_index] = con;
1662
1663 li->bllen0[con] = lenA;
1664 li->ddist[con] = lenB - lenA;
1665 /* Set the length to the topology A length */
1666 li->bllen[con] = lenA;
1667 li->atoms[con].index1 = a1;
1668 li->atoms[con].index2 = a2;
1669
1670 /* Make space in the constraint connection matrix for constraints
1671 * connected to both end of the current constraint.
1672 */
1673 li->ncc += at2con[a1].ssize() - 1 + at2con[a2].ssize() - 1;
1674
1675 li->blnr[con + 1] = li->ncc;
1676
1677 /* Increase the constraint count */
1678 li->nc++;
1679 }
1680
1681 /*! \brief Check if constraint with topology index constraint_index is connected
1682 * to other constraints, and if so add those connected constraints to our task. */
check_assign_connected(Lincs * li,gmx::ArrayRef<const int> iatom,const InteractionDefinitions & idef,bool bDynamics,int a1,int a2,const ListOfLists<int> & at2con)1683 static void check_assign_connected(Lincs* li,
1684 gmx::ArrayRef<const int> iatom,
1685 const InteractionDefinitions& idef,
1686 bool bDynamics,
1687 int a1,
1688 int a2,
1689 const ListOfLists<int>& at2con)
1690 {
1691 /* Currently this function only supports constraint groups
1692 * in which all constraints share at least one atom
1693 * (e.g. H-bond constraints).
1694 * Check both ends of the current constraint for
1695 * connected constraints. We need to assign those
1696 * to the same task.
1697 */
1698 for (int end = 0; end < 2; end++)
1699 {
1700 const int a = (end == 0 ? a1 : a2);
1701
1702 for (const int cc : at2con[a])
1703 {
1704 /* Check if constraint cc has not yet been assigned */
1705 if (li->con_index[cc] == -1)
1706 {
1707 const int type = iatom[cc * 3];
1708 const real lenA = idef.iparams[type].constr.dA;
1709 const real lenB = idef.iparams[type].constr.dB;
1710
1711 if (bDynamics || lenA != 0 || lenB != 0)
1712 {
1713 assign_constraint(li, cc, iatom[3 * cc + 1], iatom[3 * cc + 2], lenA, lenB, at2con);
1714 }
1715 }
1716 }
1717 }
1718 }
1719
1720 /*! \brief Check if constraint with topology index constraint_index is involved
1721 * in a constraint triangle, and if so add the other two constraints
1722 * in the triangle to our task. */
check_assign_triangle(Lincs * li,gmx::ArrayRef<const int> iatom,const InteractionDefinitions & idef,bool bDynamics,int constraint_index,int a1,int a2,const ListOfLists<int> & at2con)1723 static void check_assign_triangle(Lincs* li,
1724 gmx::ArrayRef<const int> iatom,
1725 const InteractionDefinitions& idef,
1726 bool bDynamics,
1727 int constraint_index,
1728 int a1,
1729 int a2,
1730 const ListOfLists<int>& at2con)
1731 {
1732 int nca, cc[32], ca[32];
1733 int c_triangle[2] = { -1, -1 };
1734
1735 nca = 0;
1736 for (const int c : at2con[a1])
1737 {
1738 if (c != constraint_index)
1739 {
1740 int aa1, aa2;
1741
1742 aa1 = iatom[c * 3 + 1];
1743 aa2 = iatom[c * 3 + 2];
1744 if (aa1 != a1)
1745 {
1746 cc[nca] = c;
1747 ca[nca] = aa1;
1748 nca++;
1749 }
1750 if (aa2 != a1)
1751 {
1752 cc[nca] = c;
1753 ca[nca] = aa2;
1754 nca++;
1755 }
1756 }
1757 }
1758
1759 for (const int c : at2con[a2])
1760 {
1761 if (c != constraint_index)
1762 {
1763 int aa1, aa2, i;
1764
1765 aa1 = iatom[c * 3 + 1];
1766 aa2 = iatom[c * 3 + 2];
1767 if (aa1 != a2)
1768 {
1769 for (i = 0; i < nca; i++)
1770 {
1771 if (aa1 == ca[i])
1772 {
1773 c_triangle[0] = cc[i];
1774 c_triangle[1] = c;
1775 }
1776 }
1777 }
1778 if (aa2 != a2)
1779 {
1780 for (i = 0; i < nca; i++)
1781 {
1782 if (aa2 == ca[i])
1783 {
1784 c_triangle[0] = cc[i];
1785 c_triangle[1] = c;
1786 }
1787 }
1788 }
1789 }
1790 }
1791
1792 if (c_triangle[0] >= 0)
1793 {
1794 int end;
1795
1796 for (end = 0; end < 2; end++)
1797 {
1798 /* Check if constraint c_triangle[end] has not yet been assigned */
1799 if (li->con_index[c_triangle[end]] == -1)
1800 {
1801 int i, type;
1802 real lenA, lenB;
1803
1804 i = c_triangle[end] * 3;
1805 type = iatom[i];
1806 lenA = idef.iparams[type].constr.dA;
1807 lenB = idef.iparams[type].constr.dB;
1808
1809 if (bDynamics || lenA != 0 || lenB != 0)
1810 {
1811 assign_constraint(li, c_triangle[end], iatom[i + 1], iatom[i + 2], lenA, lenB, at2con);
1812 }
1813 }
1814 }
1815 }
1816 }
1817
1818 //! Sets matrix indices.
set_matrix_indices(Lincs * li,const Task & li_task,const ListOfLists<int> & at2con,bool bSortMatrix)1819 static void set_matrix_indices(Lincs* li, const Task& li_task, const ListOfLists<int>& at2con, bool bSortMatrix)
1820 {
1821 for (int b = li_task.b0; b < li_task.b1; b++)
1822 {
1823 const int a1 = li->atoms[b].index1;
1824 const int a2 = li->atoms[b].index2;
1825
1826 int i = li->blnr[b];
1827 for (const int constraint : at2con[a1])
1828 {
1829 const int concon = li->con_index[constraint];
1830 if (concon != b)
1831 {
1832 li->blbnb[i++] = concon;
1833 }
1834 }
1835 for (const int constraint : at2con[a2])
1836 {
1837 const int concon = li->con_index[constraint];
1838 if (concon != b)
1839 {
1840 li->blbnb[i++] = concon;
1841 }
1842 }
1843
1844 if (bSortMatrix)
1845 {
1846 /* Order the blbnb matrix to optimize memory access */
1847 std::sort(li->blbnb.begin() + li->blnr[b], li->blbnb.begin() + li->blnr[b + 1]);
1848 }
1849 }
1850 }
1851
set_lincs(const InteractionDefinitions & idef,const int numAtoms,const real * invmass,const real lambda,bool bDynamics,const t_commrec * cr,Lincs * li)1852 void set_lincs(const InteractionDefinitions& idef,
1853 const int numAtoms,
1854 const real* invmass,
1855 const real lambda,
1856 bool bDynamics,
1857 const t_commrec* cr,
1858 Lincs* li)
1859 {
1860 li->nc_real = 0;
1861 li->nc = 0;
1862 li->ncc = 0;
1863 /* Zero the thread index ranges.
1864 * Otherwise without local constraints we could return with old ranges.
1865 */
1866 for (int i = 0; i < li->ntask; i++)
1867 {
1868 li->task[i].b0 = 0;
1869 li->task[i].b1 = 0;
1870 li->task[i].ind.clear();
1871 }
1872 if (li->ntask > 1)
1873 {
1874 li->task[li->ntask].ind.clear();
1875 }
1876
1877 /* This is the local topology, so there are only F_CONSTR constraints */
1878 if (idef.il[F_CONSTR].empty())
1879 {
1880 /* There are no constraints,
1881 * we do not need to fill any data structures.
1882 */
1883 return;
1884 }
1885
1886 if (debug)
1887 {
1888 fprintf(debug, "Building the LINCS connectivity\n");
1889 }
1890
1891 int natoms;
1892 if (DOMAINDECOMP(cr))
1893 {
1894 if (cr->dd->constraints)
1895 {
1896 int start;
1897
1898 dd_get_constraint_range(cr->dd, &start, &natoms);
1899 }
1900 else
1901 {
1902 natoms = dd_numHomeAtoms(*cr->dd);
1903 }
1904 }
1905 else
1906 {
1907 natoms = numAtoms;
1908 }
1909
1910 const ListOfLists<int> at2con =
1911 make_at2con(natoms, idef.il, idef.iparams, flexibleConstraintTreatment(bDynamics));
1912
1913 const int ncon_tot = idef.il[F_CONSTR].size() / 3;
1914
1915 /* Ensure we have enough padding for aligned loads for each thread */
1916 const int numEntries = ncon_tot + li->ntask * simd_width;
1917 li->con_index.resize(numEntries);
1918 li->bllen0.resize(numEntries);
1919 li->ddist.resize(numEntries);
1920 li->atoms.resize(numEntries);
1921 li->blc.resize(numEntries);
1922 li->blc1.resize(numEntries);
1923 li->blnr.resize(numEntries + 1);
1924 li->bllen.resize(numEntries);
1925 li->tmpv.resizeWithPadding(numEntries);
1926 if (DOMAINDECOMP(cr))
1927 {
1928 li->nlocat.resize(numEntries);
1929 }
1930 li->tmp1.resize(numEntries);
1931 li->tmp2.resize(numEntries);
1932 li->tmp3.resize(numEntries);
1933 li->tmp4.resize(numEntries);
1934 li->mlambda.resize(numEntries);
1935
1936 gmx::ArrayRef<const int> iatom = idef.il[F_CONSTR].iatoms;
1937
1938 li->blnr[0] = li->ncc;
1939
1940 /* Assign the constraints for li->ntask LINCS tasks.
1941 * We target a uniform distribution of constraints over the tasks.
1942 * Note that when flexible constraints are present, but are removed here
1943 * (e.g. because we are doing EM) we get imbalance, but since that doesn't
1944 * happen during normal MD, that's ok.
1945 */
1946
1947 /* Determine the number of constraints we need to assign here */
1948 int ncon_assign = ncon_tot;
1949 if (!bDynamics)
1950 {
1951 /* With energy minimization, flexible constraints are ignored
1952 * (and thus minimized, as they should be).
1953 */
1954 ncon_assign -= countFlexibleConstraints(idef.il, idef.iparams);
1955 }
1956
1957 /* Set the target constraint count per task to exactly uniform,
1958 * this might be overridden below.
1959 */
1960 int ncon_target = (ncon_assign + li->ntask - 1) / li->ntask;
1961
1962 /* Mark all constraints as unassigned by setting their index to -1 */
1963 for (int con = 0; con < ncon_tot; con++)
1964 {
1965 li->con_index[con] = -1;
1966 }
1967
1968 int con = 0;
1969 for (int th = 0; th < li->ntask; th++)
1970 {
1971 Task* li_task;
1972
1973 li_task = &li->task[th];
1974
1975 #if GMX_SIMD_HAVE_REAL
1976 /* With indepedent tasks we likely have H-bond constraints or constraint
1977 * pairs. The connected constraints will be pulled into the task, so the
1978 * constraints per task will often exceed ncon_target.
1979 * Triangle constraints can also increase the count, but there are
1980 * relatively few of those, so we usually expect to get ncon_target.
1981 */
1982 if (li->bTaskDep)
1983 {
1984 /* We round ncon_target to a multiple of GMX_SIMD_WIDTH,
1985 * since otherwise a lot of operations can be wasted.
1986 * There are several ways to round here, we choose the one
1987 * that alternates block sizes, which helps with Intel HT.
1988 */
1989 ncon_target = ((ncon_assign * (th + 1)) / li->ntask - li->nc_real + GMX_SIMD_REAL_WIDTH - 1)
1990 & ~(GMX_SIMD_REAL_WIDTH - 1);
1991 }
1992 #endif // GMX_SIMD==2 && GMX_SIMD_HAVE_REAL
1993
1994 /* Continue filling the arrays where we left off with the previous task,
1995 * including padding for SIMD.
1996 */
1997 li_task->b0 = li->nc;
1998
1999 gmx::ArrayRef<const t_iparams> iparams = idef.iparams;
2000
2001 while (con < ncon_tot && li->nc - li_task->b0 < ncon_target)
2002 {
2003 if (li->con_index[con] == -1)
2004 {
2005 int type, a1, a2;
2006 real lenA, lenB;
2007
2008 type = iatom[3 * con];
2009 a1 = iatom[3 * con + 1];
2010 a2 = iatom[3 * con + 2];
2011 lenA = iparams[type].constr.dA;
2012 lenB = iparams[type].constr.dB;
2013 /* Skip the flexible constraints when not doing dynamics */
2014 if (bDynamics || lenA != 0 || lenB != 0)
2015 {
2016 assign_constraint(li, con, a1, a2, lenA, lenB, at2con);
2017
2018 if (li->ntask > 1 && !li->bTaskDep)
2019 {
2020 /* We can generate independent tasks. Check if we
2021 * need to assign connected constraints to our task.
2022 */
2023 check_assign_connected(li, iatom, idef, bDynamics, a1, a2, at2con);
2024 }
2025 if (li->ntask > 1 && li->ncg_triangle > 0)
2026 {
2027 /* Ensure constraints in one triangle are assigned
2028 * to the same task.
2029 */
2030 check_assign_triangle(li, iatom, idef, bDynamics, con, a1, a2, at2con);
2031 }
2032 }
2033 }
2034
2035 con++;
2036 }
2037
2038 li_task->b1 = li->nc;
2039
2040 if (simd_width > 1)
2041 {
2042 /* Copy the last atom pair indices and lengths for constraints
2043 * up to a multiple of simd_width, such that we can do all
2044 * SIMD operations without having to worry about end effects.
2045 */
2046 int i, last;
2047
2048 li->nc = ((li_task->b1 + simd_width - 1) / simd_width) * simd_width;
2049 last = li_task->b1 - 1;
2050 for (i = li_task->b1; i < li->nc; i++)
2051 {
2052 li->atoms[i] = li->atoms[last];
2053 li->bllen0[i] = li->bllen0[last];
2054 li->ddist[i] = li->ddist[last];
2055 li->bllen[i] = li->bllen[last];
2056 li->blnr[i + 1] = li->blnr[last + 1];
2057 }
2058 }
2059
2060 /* Keep track of how many constraints we assigned */
2061 li->nc_real += li_task->b1 - li_task->b0;
2062
2063 if (debug)
2064 {
2065 fprintf(debug, "LINCS task %d constraints %d - %d\n", th, li_task->b0, li_task->b1);
2066 }
2067 }
2068
2069 assert(li->nc_real == ncon_assign);
2070
2071 bool bSortMatrix;
2072
2073 /* Without DD we order the blbnb matrix to optimize memory access.
2074 * With DD the overhead of sorting is more than the gain during access.
2075 */
2076 bSortMatrix = !DOMAINDECOMP(cr);
2077
2078 li->blbnb.resize(li->ncc);
2079
2080 #pragma omp parallel for num_threads(li->ntask) schedule(static)
2081 for (int th = 0; th < li->ntask; th++)
2082 {
2083 try
2084 {
2085 Task& li_task = li->task[th];
2086
2087 if (li->ncg_triangle > 0)
2088 {
2089 /* This is allocating too much, but it is difficult to improve */
2090 li_task.triangle.resize(li_task.b1 - li_task.b0);
2091 li_task.tri_bits.resize(li_task.b1 - li_task.b0);
2092 }
2093
2094 set_matrix_indices(li, li_task, at2con, bSortMatrix);
2095 }
2096 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
2097 }
2098
2099 if (cr->dd == nullptr)
2100 {
2101 /* Since the matrix is static, we should free some memory */
2102 li->blbnb.resize(li->ncc);
2103 }
2104
2105 li->blmf.resize(li->ncc);
2106 li->blmf1.resize(li->ncc);
2107 li->tmpncc.resize(li->ncc);
2108
2109 gmx::ArrayRef<const int> nlocat_dd = dd_constraints_nlocalatoms(cr->dd);
2110 if (!nlocat_dd.empty())
2111 {
2112 /* Convert nlocat from local topology to LINCS constraint indexing */
2113 for (con = 0; con < ncon_tot; con++)
2114 {
2115 li->nlocat[li->con_index[con]] = nlocat_dd[con];
2116 }
2117 }
2118 else
2119 {
2120 li->nlocat.clear();
2121 }
2122
2123 if (debug)
2124 {
2125 fprintf(debug, "Number of constraints is %d, padded %d, couplings %d\n", li->nc_real,
2126 li->nc, li->ncc);
2127 }
2128
2129 if (li->ntask > 1)
2130 {
2131 lincs_thread_setup(li, numAtoms);
2132 }
2133
2134 set_lincs_matrix(li, invmass, lambda);
2135
2136 li->rmsdData[0] = 0.0;
2137 li->rmsdData[1] = 0.0;
2138 }
2139
2140 //! Issues a warning when LINCS constraints cannot be satisfied.
lincs_warning(gmx_domdec_t * dd,ArrayRef<const RVec> x,ArrayRef<const RVec> xprime,t_pbc * pbc,int ncons,gmx::ArrayRef<const AtomPair> atoms,gmx::ArrayRef<const real> bllen,real wangle,int maxwarn,int * warncount)2141 static void lincs_warning(gmx_domdec_t* dd,
2142 ArrayRef<const RVec> x,
2143 ArrayRef<const RVec> xprime,
2144 t_pbc* pbc,
2145 int ncons,
2146 gmx::ArrayRef<const AtomPair> atoms,
2147 gmx::ArrayRef<const real> bllen,
2148 real wangle,
2149 int maxwarn,
2150 int* warncount)
2151 {
2152 real wfac = std::cos(DEG2RAD * wangle);
2153
2154 fprintf(stderr,
2155 "bonds that rotated more than %g degrees:\n"
2156 " atom 1 atom 2 angle previous, current, constraint length\n",
2157 wangle);
2158
2159 for (int b = 0; b < ncons; b++)
2160 {
2161 const int i = atoms[b].index1;
2162 const int j = atoms[b].index2;
2163 rvec v0;
2164 rvec v1;
2165 if (pbc)
2166 {
2167 pbc_dx_aiuc(pbc, x[i], x[j], v0);
2168 pbc_dx_aiuc(pbc, xprime[i], xprime[j], v1);
2169 }
2170 else
2171 {
2172 rvec_sub(x[i], x[j], v0);
2173 rvec_sub(xprime[i], xprime[j], v1);
2174 }
2175 real d0 = norm(v0);
2176 real d1 = norm(v1);
2177 real cosine = ::iprod(v0, v1) / (d0 * d1);
2178 if (cosine < wfac)
2179 {
2180 fprintf(stderr, " %6d %6d %5.1f %8.4f %8.4f %8.4f\n", ddglatnr(dd, i),
2181 ddglatnr(dd, j), RAD2DEG * std::acos(cosine), d0, d1, bllen[b]);
2182 if (!std::isfinite(d1))
2183 {
2184 gmx_fatal(FARGS, "Bond length not finite.");
2185 }
2186
2187 (*warncount)++;
2188 }
2189 }
2190 if (*warncount > maxwarn)
2191 {
2192 too_many_constraint_warnings(econtLINCS, *warncount);
2193 }
2194 }
2195
2196 //! Status information about how well LINCS satisified the constraints in this domain
2197 struct LincsDeviations
2198 {
2199 //! The maximum over all bonds in this domain of the relative deviation in bond lengths
2200 real maxDeviation = 0;
2201 //! Sum over all bonds in this domain of the squared relative deviation
2202 real sumSquaredDeviation = 0;
2203 //! Index of bond with max deviation
2204 int indexOfMaxDeviation = -1;
2205 //! Number of bonds constrained in this domain
2206 int numConstraints = 0;
2207 };
2208
2209 //! Determine how well the constraints have been satisfied.
makeLincsDeviations(const Lincs & lincsd,ArrayRef<const RVec> x,const t_pbc * pbc)2210 static LincsDeviations makeLincsDeviations(const Lincs& lincsd, ArrayRef<const RVec> x, const t_pbc* pbc)
2211 {
2212 LincsDeviations result;
2213 const ArrayRef<const AtomPair> atoms = lincsd.atoms;
2214 const ArrayRef<const real> bllen = lincsd.bllen;
2215 const ArrayRef<const int> nlocat = lincsd.nlocat;
2216
2217 for (int task = 0; task < lincsd.ntask; task++)
2218 {
2219 for (int b = lincsd.task[task].b0; b < lincsd.task[task].b1; b++)
2220 {
2221 rvec dx;
2222 if (pbc)
2223 {
2224 pbc_dx_aiuc(pbc, x[atoms[b].index1], x[atoms[b].index2], dx);
2225 }
2226 else
2227 {
2228 rvec_sub(x[atoms[b].index1], x[atoms[b].index2], dx);
2229 }
2230 real r2 = ::norm2(dx);
2231 real len = r2 * gmx::invsqrt(r2);
2232 real d = std::abs(len / bllen[b] - 1.0_real);
2233 if (d > result.maxDeviation && (nlocat.empty() || nlocat[b]))
2234 {
2235 result.maxDeviation = d;
2236 result.indexOfMaxDeviation = b;
2237 }
2238 if (nlocat.empty())
2239 {
2240 result.sumSquaredDeviation += d * d;
2241 result.numConstraints++;
2242 }
2243 else
2244 {
2245 result.sumSquaredDeviation += nlocat[b] * d * d;
2246 result.numConstraints += nlocat[b];
2247 }
2248 }
2249 }
2250
2251 if (!nlocat.empty())
2252 {
2253 result.numConstraints /= 2;
2254 result.sumSquaredDeviation *= 0.5;
2255 }
2256 return result;
2257 }
2258
constrain_lincs(bool computeRmsd,const t_inputrec & ir,int64_t step,Lincs * lincsd,const real * invmass,const t_commrec * cr,const gmx_multisim_t * ms,ArrayRefWithPadding<const RVec> xPadded,ArrayRefWithPadding<RVec> xprimePadded,ArrayRef<RVec> min_proj,const matrix box,t_pbc * pbc,const bool hasMassPerturbed,real lambda,real * dvdlambda,real invdt,ArrayRef<RVec> v,bool bCalcVir,tensor vir_r_m_dr,ConstraintVariable econq,t_nrnb * nrnb,int maxwarn,int * warncount)2259 bool constrain_lincs(bool computeRmsd,
2260 const t_inputrec& ir,
2261 int64_t step,
2262 Lincs* lincsd,
2263 const real* invmass,
2264 const t_commrec* cr,
2265 const gmx_multisim_t* ms,
2266 ArrayRefWithPadding<const RVec> xPadded,
2267 ArrayRefWithPadding<RVec> xprimePadded,
2268 ArrayRef<RVec> min_proj,
2269 const matrix box,
2270 t_pbc* pbc,
2271 const bool hasMassPerturbed,
2272 real lambda,
2273 real* dvdlambda,
2274 real invdt,
2275 ArrayRef<RVec> v,
2276 bool bCalcVir,
2277 tensor vir_r_m_dr,
2278 ConstraintVariable econq,
2279 t_nrnb* nrnb,
2280 int maxwarn,
2281 int* warncount)
2282 {
2283 bool bOK = TRUE;
2284
2285 /* This boolean should be set by a flag passed to this routine.
2286 * We can also easily check if any constraint length is changed,
2287 * if not dH/dlambda=0 and we can also set the boolean to FALSE.
2288 */
2289 bool bCalcDHDL = (ir.efep != efepNO && dvdlambda != nullptr);
2290
2291 if (lincsd->nc == 0 && cr->dd == nullptr)
2292 {
2293 if (computeRmsd)
2294 {
2295 lincsd->rmsdData = { { 0 } };
2296 }
2297
2298 return bOK;
2299 }
2300
2301 ArrayRef<const RVec> x = xPadded.unpaddedArrayRef();
2302 ArrayRef<RVec> xprime = xprimePadded.unpaddedArrayRef();
2303
2304 if (econq == ConstraintVariable::Positions)
2305 {
2306 /* We can't use bCalcDHDL here, since NULL can be passed for dvdlambda
2307 * also with efep!=fepNO.
2308 */
2309 if (ir.efep != efepNO)
2310 {
2311 if (hasMassPerturbed && lincsd->matlam != lambda)
2312 {
2313 set_lincs_matrix(lincsd, invmass, lambda);
2314 }
2315
2316 for (int i = 0; i < lincsd->nc; i++)
2317 {
2318 lincsd->bllen[i] = lincsd->bllen0[i] + lambda * lincsd->ddist[i];
2319 }
2320 }
2321
2322 if (lincsd->ncg_flex)
2323 {
2324 /* Set the flexible constraint lengths to the old lengths */
2325 if (pbc != nullptr)
2326 {
2327 for (int i = 0; i < lincsd->nc; i++)
2328 {
2329 if (lincsd->bllen[i] == 0)
2330 {
2331 rvec dx;
2332 pbc_dx_aiuc(pbc, x[lincsd->atoms[i].index1], x[lincsd->atoms[i].index2], dx);
2333 lincsd->bllen[i] = norm(dx);
2334 }
2335 }
2336 }
2337 else
2338 {
2339 for (int i = 0; i < lincsd->nc; i++)
2340 {
2341 if (lincsd->bllen[i] == 0)
2342 {
2343 lincsd->bllen[i] = std::sqrt(
2344 distance2(x[lincsd->atoms[i].index1], x[lincsd->atoms[i].index2]));
2345 }
2346 }
2347 }
2348 }
2349
2350 const bool printDebugOutput = ((debug != nullptr) && lincsd->nc > 0);
2351 if (printDebugOutput)
2352 {
2353 LincsDeviations deviations = makeLincsDeviations(*lincsd, xprime, pbc);
2354 fprintf(debug, " Rel. Constraint Deviation: RMS MAX between atoms\n");
2355 fprintf(debug, " Before LINCS %.6f %.6f %6d %6d\n",
2356 std::sqrt(deviations.sumSquaredDeviation / deviations.numConstraints),
2357 deviations.maxDeviation,
2358 ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index1),
2359 ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index2));
2360 }
2361
2362 /* This bWarn var can be updated by multiple threads
2363 * at the same time. But as we only need to detect
2364 * if a warning occurred or not, this is not an issue.
2365 */
2366 bool bWarn = FALSE;
2367
2368 /* The OpenMP parallel region of constrain_lincs for coords */
2369 #pragma omp parallel num_threads(lincsd->ntask)
2370 {
2371 try
2372 {
2373 int th = gmx_omp_get_thread_num();
2374
2375 clear_mat(lincsd->task[th].vir_r_m_dr);
2376
2377 do_lincs(xPadded, xprimePadded, box, pbc, lincsd, th, invmass, cr, bCalcDHDL,
2378 ir.LincsWarnAngle, &bWarn, invdt, v, bCalcVir,
2379 th == 0 ? vir_r_m_dr : lincsd->task[th].vir_r_m_dr);
2380 }
2381 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
2382 }
2383
2384 if (computeRmsd || printDebugOutput || bWarn)
2385 {
2386 LincsDeviations deviations = makeLincsDeviations(*lincsd, xprime, pbc);
2387
2388 if (computeRmsd)
2389 {
2390 // This is reduced across domains in compute_globals and
2391 // reported to the log file.
2392 lincsd->rmsdData[0] = deviations.numConstraints;
2393 lincsd->rmsdData[1] = deviations.sumSquaredDeviation;
2394 }
2395 else
2396 {
2397 // This is never read
2398 lincsd->rmsdData = { { 0 } };
2399 }
2400 if (printDebugOutput)
2401 {
2402 fprintf(debug, " After LINCS %.6f %.6f %6d %6d\n\n",
2403 std::sqrt(deviations.sumSquaredDeviation / deviations.numConstraints),
2404 deviations.maxDeviation,
2405 ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index1),
2406 ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index2));
2407 }
2408
2409 if (bWarn)
2410 {
2411 if (maxwarn < INT_MAX)
2412 {
2413 std::string simMesg;
2414 if (isMultiSim(ms))
2415 {
2416 simMesg += gmx::formatString(" in simulation %d", ms->simulationIndex_);
2417 }
2418 fprintf(stderr,
2419 "\nStep %" PRId64
2420 ", time %g (ps) LINCS WARNING%s\n"
2421 "relative constraint deviation after LINCS:\n"
2422 "rms %.6f, max %.6f (between atoms %d and %d)\n",
2423 step, ir.init_t + step * ir.delta_t, simMesg.c_str(),
2424 std::sqrt(deviations.sumSquaredDeviation / deviations.numConstraints),
2425 deviations.maxDeviation,
2426 ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index1),
2427 ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index2));
2428
2429 lincs_warning(cr->dd, x, xprime, pbc, lincsd->nc, lincsd->atoms, lincsd->bllen,
2430 ir.LincsWarnAngle, maxwarn, warncount);
2431 }
2432 bOK = (deviations.maxDeviation < 0.5);
2433 }
2434 }
2435
2436 if (lincsd->ncg_flex)
2437 {
2438 for (int i = 0; (i < lincsd->nc); i++)
2439 {
2440 if (lincsd->bllen0[i] == 0 && lincsd->ddist[i] == 0)
2441 {
2442 lincsd->bllen[i] = 0;
2443 }
2444 }
2445 }
2446 }
2447 else
2448 {
2449 /* The OpenMP parallel region of constrain_lincs for derivatives */
2450 #pragma omp parallel num_threads(lincsd->ntask)
2451 {
2452 try
2453 {
2454 int th = gmx_omp_get_thread_num();
2455
2456 do_lincsp(xPadded, xprimePadded, min_proj, pbc, lincsd, th, invmass, econq,
2457 bCalcDHDL, bCalcVir, th == 0 ? vir_r_m_dr : lincsd->task[th].vir_r_m_dr);
2458 }
2459 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
2460 }
2461 }
2462
2463 if (bCalcDHDL)
2464 {
2465 /* Reduce the dH/dlambda contributions over the threads */
2466 real dhdlambda;
2467 int th;
2468
2469 dhdlambda = 0;
2470 for (th = 0; th < lincsd->ntask; th++)
2471 {
2472 dhdlambda += lincsd->task[th].dhdlambda;
2473 }
2474 if (econq == ConstraintVariable::Positions)
2475 {
2476 /* dhdlambda contains dH/dlambda*dt^2, correct for this */
2477 /* TODO This should probably use invdt, so that sd integrator scaling works properly */
2478 dhdlambda /= ir.delta_t * ir.delta_t;
2479 }
2480 *dvdlambda += dhdlambda;
2481 }
2482
2483 if (bCalcVir && lincsd->ntask > 1)
2484 {
2485 for (int i = 1; i < lincsd->ntask; i++)
2486 {
2487 m_add(vir_r_m_dr, lincsd->task[i].vir_r_m_dr, vir_r_m_dr);
2488 }
2489 }
2490
2491 /* count assuming nit=1 */
2492 inc_nrnb(nrnb, eNR_LINCS, lincsd->nc_real);
2493 inc_nrnb(nrnb, eNR_LINCSMAT, (2 + lincsd->nOrder) * lincsd->ncc);
2494 if (lincsd->ntriangle > 0)
2495 {
2496 inc_nrnb(nrnb, eNR_LINCSMAT, lincsd->nOrder * lincsd->ncc_triangle);
2497 }
2498 if (!v.empty())
2499 {
2500 inc_nrnb(nrnb, eNR_CONSTR_V, lincsd->nc_real * 2);
2501 }
2502 if (bCalcVir)
2503 {
2504 inc_nrnb(nrnb, eNR_CONSTR_VIR, lincsd->nc_real);
2505 }
2506
2507 return bOK;
2508 }
2509
2510 } // namespace gmx
2511