Merge branch release-2021 into merge-2021-into-master
[alexxy/gromacs.git] / src / gromacs / mdlib / lincs.cpp
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,2021, 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
97     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
223 ArrayRef<real> lincs_rmsdData(Lincs* lincsd)
224 {
225     return lincsd->rmsdData;
226 }
227
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  */
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.
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                                      gmx::ArrayRef<const real>      invmass,
353                                      rvec*                          x)
354 {
355     if (!invmass.empty())
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.
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                                    gmx::ArrayRef<const real>      invmass,
402                                    rvec*                          x)
403 {
404     if (!invmass.empty())
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.
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                                gmx::ArrayRef<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>
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. */
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. */
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                       ArrayRef<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(
627             b0, b1, atoms, x, f, blc.data(), pbc_simd, as_rvec_array(r.data()), 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,
712                        th,
713                        1.0,
714                        sol,
715                        r,
716                        (econq != ConstraintVariable::Force) ? invmass : gmx::ArrayRef<real>(),
717                        as_rvec_array(fp.data()));
718
719     if (bCalcDHDL)
720     {
721         real dhdlambda = 0;
722         for (int b = b0; b < b1; b++)
723         {
724             dhdlambda -= sol[b] * lincsd->ddist[b];
725         }
726
727         lincsd->task[th].dhdlambda = dhdlambda;
728     }
729
730     if (bCalcVir)
731     {
732         /* Constraint virial,
733          * determines sum r_bond x delta f,
734          * where delta f is the constraint correction
735          * of the quantity that is being constrained.
736          */
737         for (int b = b0; b < b1; b++)
738         {
739             const real mvb = lincsd->bllen[b] * sol[b];
740             for (int i = 0; i < DIM; i++)
741             {
742                 const real tmp1 = mvb * r[b][i];
743                 for (int j = 0; j < DIM; j++)
744                 {
745                     rmdf[i][j] += tmp1 * r[b][j];
746                 }
747             }
748         } /* 23 ncons flops */
749     }
750 }
751
752 #if GMX_SIMD_HAVE_REAL
753
754 /*! \brief Calculate the constraint distance vectors r to project on from x.
755  *
756  * Determine the right-hand side of the matrix equation using coordinates xp. */
757 static void gmx_simdcall calc_dr_x_xp_simd(int                           b0,
758                                            int                           b1,
759                                            gmx::ArrayRef<const AtomPair> atoms,
760                                            const rvec* gmx_restrict x,
761                                            const rvec* gmx_restrict xp,
762                                            const real* gmx_restrict bllen,
763                                            const real* gmx_restrict blc,
764                                            const real*              pbc_simd,
765                                            rvec* gmx_restrict r,
766                                            real* gmx_restrict rhs,
767                                            real* gmx_restrict sol)
768 {
769     assert(b0 % GMX_SIMD_REAL_WIDTH == 0);
770     alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset2[GMX_SIMD_REAL_WIDTH];
771
772     for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
773     {
774         offset2[i] = i;
775     }
776
777     for (int bs = b0; bs < b1; bs += GMX_SIMD_REAL_WIDTH)
778     {
779         SimdReal                                 x0_S, y0_S, z0_S;
780         SimdReal                                 x1_S, y1_S, z1_S;
781         SimdReal                                 rx_S, ry_S, rz_S, n2_S, il_S;
782         SimdReal                                 rxp_S, ryp_S, rzp_S, ip_S, rhs_S;
783         alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset0[GMX_SIMD_REAL_WIDTH];
784         alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset1[GMX_SIMD_REAL_WIDTH];
785
786         for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
787         {
788             offset0[i] = atoms[bs + i].index1;
789             offset1[i] = atoms[bs + i].index2;
790         }
791
792         gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset0, &x0_S, &y0_S, &z0_S);
793         gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset1, &x1_S, &y1_S, &z1_S);
794         rx_S = x0_S - x1_S;
795         ry_S = y0_S - y1_S;
796         rz_S = z0_S - z1_S;
797
798         pbc_correct_dx_simd(&rx_S, &ry_S, &rz_S, pbc_simd);
799
800         n2_S = norm2(rx_S, ry_S, rz_S);
801         il_S = invsqrt(n2_S);
802
803         rx_S = rx_S * il_S;
804         ry_S = ry_S * il_S;
805         rz_S = rz_S * il_S;
806
807         transposeScatterStoreU<3>(reinterpret_cast<real*>(r + bs), offset2, rx_S, ry_S, rz_S);
808
809         gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(xp), offset0, &x0_S, &y0_S, &z0_S);
810         gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(xp), offset1, &x1_S, &y1_S, &z1_S);
811
812         rxp_S = x0_S - x1_S;
813         ryp_S = y0_S - y1_S;
814         rzp_S = z0_S - z1_S;
815
816         pbc_correct_dx_simd(&rxp_S, &ryp_S, &rzp_S, pbc_simd);
817
818         ip_S = iprod(rx_S, ry_S, rz_S, rxp_S, ryp_S, rzp_S);
819
820         rhs_S = load<SimdReal>(blc + bs) * (ip_S - load<SimdReal>(bllen + bs));
821
822         store(rhs + bs, rhs_S);
823         store(sol + bs, rhs_S);
824     }
825 }
826 #endif // GMX_SIMD_HAVE_REAL
827
828 /*! \brief Determine the distances and right-hand side for the next iteration. */
829 gmx_unused static void calc_dist_iter(int                           b0,
830                                       int                           b1,
831                                       gmx::ArrayRef<const AtomPair> atoms,
832                                       const rvec* gmx_restrict xp,
833                                       const real* gmx_restrict bllen,
834                                       const real* gmx_restrict blc,
835                                       const t_pbc*             pbc,
836                                       real                     wfac,
837                                       real* gmx_restrict rhs,
838                                       real* gmx_restrict sol,
839                                       bool*              bWarn)
840 {
841     for (int b = b0; b < b1; b++)
842     {
843         real len = bllen[b];
844         rvec dx;
845         if (pbc)
846         {
847             pbc_dx_aiuc(pbc, xp[atoms[b].index1], xp[atoms[b].index2], dx);
848         }
849         else
850         {
851             rvec_sub(xp[atoms[b].index1], xp[atoms[b].index2], dx);
852         }
853         real len2  = len * len;
854         real dlen2 = 2 * len2 - ::norm2(dx);
855         if (dlen2 < wfac * len2)
856         {
857             /* not race free - see detailed comment in caller */
858             *bWarn = TRUE;
859         }
860         real mvb;
861         if (dlen2 > 0)
862         {
863             mvb = blc[b] * (len - dlen2 * gmx::invsqrt(dlen2));
864         }
865         else
866         {
867             mvb = blc[b] * len;
868         }
869         rhs[b] = mvb;
870         sol[b] = mvb;
871     } /* 20*ncons flops */
872 }
873
874 #if GMX_SIMD_HAVE_REAL
875 /*! \brief As calc_dist_iter(), but using SIMD intrinsics. */
876 static void gmx_simdcall calc_dist_iter_simd(int                           b0,
877                                              int                           b1,
878                                              gmx::ArrayRef<const AtomPair> atoms,
879                                              const rvec* gmx_restrict x,
880                                              const real* gmx_restrict bllen,
881                                              const real* gmx_restrict blc,
882                                              const real*              pbc_simd,
883                                              real                     wfac,
884                                              real* gmx_restrict rhs,
885                                              real* gmx_restrict sol,
886                                              bool*              bWarn)
887 {
888     SimdReal min_S(GMX_REAL_MIN);
889     SimdReal two_S(2.0);
890     SimdReal wfac_S(wfac);
891     SimdBool warn_S;
892
893     assert(b0 % GMX_SIMD_REAL_WIDTH == 0);
894
895     /* Initialize all to FALSE */
896     warn_S = (two_S < setZero());
897
898     for (int bs = b0; bs < b1; bs += GMX_SIMD_REAL_WIDTH)
899     {
900         SimdReal                                 x0_S, y0_S, z0_S;
901         SimdReal                                 x1_S, y1_S, z1_S;
902         SimdReal                                 rx_S, ry_S, rz_S, n2_S;
903         SimdReal                                 len_S, len2_S, dlen2_S, lc_S, blc_S;
904         alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset0[GMX_SIMD_REAL_WIDTH];
905         alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset1[GMX_SIMD_REAL_WIDTH];
906
907         for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
908         {
909             offset0[i] = atoms[bs + i].index1;
910             offset1[i] = atoms[bs + i].index2;
911         }
912
913         gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset0, &x0_S, &y0_S, &z0_S);
914         gatherLoadUTransposeTSANSafe<3>(reinterpret_cast<const real*>(x), offset1, &x1_S, &y1_S, &z1_S);
915
916         rx_S = x0_S - x1_S;
917         ry_S = y0_S - y1_S;
918         rz_S = z0_S - z1_S;
919
920         pbc_correct_dx_simd(&rx_S, &ry_S, &rz_S, pbc_simd);
921
922         n2_S = norm2(rx_S, ry_S, rz_S);
923
924         len_S  = load<SimdReal>(bllen + bs);
925         len2_S = len_S * len_S;
926
927         dlen2_S = fms(two_S, len2_S, n2_S);
928
929         warn_S = warn_S || (dlen2_S < (wfac_S * len2_S));
930
931         /* Avoid 1/0 by taking the max with REAL_MIN.
932          * Note: when dlen2 is close to zero (90 degree constraint rotation),
933          * the accuracy of the algorithm is no longer relevant.
934          */
935         dlen2_S = max(dlen2_S, min_S);
936
937         lc_S = fnma(dlen2_S, invsqrt(dlen2_S), len_S);
938
939         blc_S = load<SimdReal>(blc + bs);
940
941         lc_S = blc_S * lc_S;
942
943         store(rhs + bs, lc_S);
944         store(sol + bs, lc_S);
945     }
946
947     if (anyTrue(warn_S))
948     {
949         *bWarn = TRUE;
950     }
951 }
952 #endif // GMX_SIMD_HAVE_REAL
953
954 //! Implements LINCS constraining.
955 static void do_lincs(ArrayRefWithPadding<const RVec> xPadded,
956                      ArrayRefWithPadding<RVec>       xpPadded,
957                      const matrix                    box,
958                      t_pbc*                          pbc,
959                      Lincs*                          lincsd,
960                      int                             th,
961                      ArrayRef<const real>            invmass,
962                      const t_commrec*                cr,
963                      bool                            bCalcDHDL,
964                      real                            wangle,
965                      bool*                           bWarn,
966                      real                            invdt,
967                      ArrayRef<RVec>                  vRef,
968                      bool                            bCalcVir,
969                      tensor                          vir_r_m_dr)
970 {
971     const rvec* x        = as_rvec_array(xPadded.paddedArrayRef().data());
972     rvec*       xp       = as_rvec_array(xpPadded.paddedArrayRef().data());
973     rvec* gmx_restrict v = as_rvec_array(vRef.data());
974
975     const int b0 = lincsd->task[th].b0;
976     const int b1 = lincsd->task[th].b1;
977
978     gmx::ArrayRef<const AtomPair> atoms   = lincsd->atoms;
979     gmx::ArrayRef<gmx::RVec>      r       = lincsd->tmpv;
980     gmx::ArrayRef<const int>      blnr    = lincsd->blnr;
981     gmx::ArrayRef<const int>      blbnb   = lincsd->blbnb;
982     gmx::ArrayRef<const real>     blc     = lincsd->blc;
983     gmx::ArrayRef<const real>     blmf    = lincsd->blmf;
984     gmx::ArrayRef<const real>     bllen   = lincsd->bllen;
985     gmx::ArrayRef<real>           blcc    = lincsd->tmpncc;
986     gmx::ArrayRef<real>           rhs1    = lincsd->tmp1;
987     gmx::ArrayRef<real>           rhs2    = lincsd->tmp2;
988     gmx::ArrayRef<real>           sol     = lincsd->tmp3;
989     gmx::ArrayRef<real>           blc_sol = lincsd->tmp4;
990     gmx::ArrayRef<real>           mlambda = lincsd->mlambda;
991     gmx::ArrayRef<const int>      nlocat  = lincsd->nlocat;
992
993 #if GMX_SIMD_HAVE_REAL
994
995     /* This SIMD code does the same as the plain-C code after the #else.
996      * The only difference is that we always call pbc code, as with SIMD
997      * the overhead of pbc computation (when not needed) is small.
998      */
999     alignas(GMX_SIMD_ALIGNMENT) real pbc_simd[9 * GMX_SIMD_REAL_WIDTH];
1000
1001     /* Convert the pbc struct for SIMD */
1002     set_pbc_simd(pbc, pbc_simd);
1003
1004     /* Compute normalized x i-j vectors, store in r.
1005      * Compute the inner product of r and xp i-j and store in rhs1.
1006      */
1007     calc_dr_x_xp_simd(
1008             b0, b1, atoms, x, xp, bllen.data(), blc.data(), pbc_simd, as_rvec_array(r.data()), rhs1.data(), sol.data());
1009
1010 #else // GMX_SIMD_HAVE_REAL
1011
1012     if (pbc)
1013     {
1014         /* Compute normalized i-j vectors */
1015         for (int b = b0; b < b1; b++)
1016         {
1017             rvec dx;
1018             pbc_dx_aiuc(pbc, x[atoms[b].index1], x[atoms[b].index2], dx);
1019             unitv(dx, r[b]);
1020
1021             pbc_dx_aiuc(pbc, xp[atoms[b].index1], xp[atoms[b].index2], dx);
1022             real mvb = blc[b] * (::iprod(r[b], dx) - bllen[b]);
1023             rhs1[b]  = mvb;
1024             sol[b]   = mvb;
1025         }
1026     }
1027     else
1028     {
1029         /* Compute normalized i-j vectors */
1030         for (int b = b0; b < b1; b++)
1031         {
1032             int  i    = atoms[b].index1;
1033             int  j    = atoms[b].index2;
1034             real tmp0 = x[i][0] - x[j][0];
1035             real tmp1 = x[i][1] - x[j][1];
1036             real tmp2 = x[i][2] - x[j][2];
1037             real rlen = gmx::invsqrt(tmp0 * tmp0 + tmp1 * tmp1 + tmp2 * tmp2);
1038             r[b][0]   = rlen * tmp0;
1039             r[b][1]   = rlen * tmp1;
1040             r[b][2]   = rlen * tmp2;
1041             /* 16 ncons flops */
1042
1043             real mvb = blc[b]
1044                        * (r[b][0] * (xp[i][0] - xp[j][0]) + r[b][1] * (xp[i][1] - xp[j][1])
1045                           + r[b][2] * (xp[i][2] - xp[j][2]) - bllen[b]);
1046             rhs1[b] = mvb;
1047             sol[b]  = mvb;
1048             /* 10 flops */
1049         }
1050         /* Together: 26*ncons + 6*nrtot flops */
1051     }
1052
1053 #endif // GMX_SIMD_HAVE_REAL
1054
1055     if (lincsd->bTaskDep)
1056     {
1057         /* We need a barrier, since the matrix construction below
1058          * can access entries in r of other threads.
1059          */
1060 #pragma omp barrier
1061     }
1062
1063     /* Construct the (sparse) LINCS matrix */
1064     for (int b = b0; b < b1; b++)
1065     {
1066         for (int n = blnr[b]; n < blnr[b + 1]; n++)
1067         {
1068             blcc[n] = blmf[n] * gmx::dot(r[b], r[blbnb[n]]);
1069         }
1070     }
1071     /* Together: 26*ncons + 6*nrtot flops */
1072
1073     lincs_matrix_expand(*lincsd, lincsd->task[th], blcc, rhs1, rhs2, sol);
1074     /* nrec*(ncons+2*nrtot) flops */
1075
1076 #if GMX_SIMD_HAVE_REAL
1077     for (int b = b0; b < b1; b += GMX_SIMD_REAL_WIDTH)
1078     {
1079         SimdReal t1 = load<SimdReal>(blc.data() + b);
1080         SimdReal t2 = load<SimdReal>(sol.data() + b);
1081         store(mlambda.data() + b, t1 * t2);
1082     }
1083 #else
1084     for (int b = b0; b < b1; b++)
1085     {
1086         mlambda[b] = blc[b] * sol[b];
1087     }
1088 #endif // GMX_SIMD_HAVE_REAL
1089
1090     /* Update the coordinates */
1091     lincs_update_atoms(lincsd, th, 1.0, mlambda, r, invmass, xp);
1092
1093     /*
1094      ********  Correction for centripetal effects  ********
1095      */
1096
1097     real wfac;
1098
1099     wfac = std::cos(gmx::c_deg2Rad * wangle);
1100     wfac = wfac * wfac;
1101
1102     for (int iter = 0; iter < lincsd->nIter; iter++)
1103     {
1104         if ((lincsd->bCommIter && DOMAINDECOMP(cr) && cr->dd->constraints))
1105         {
1106 #pragma omp barrier
1107 #pragma omp master
1108             {
1109                 /* Communicate the corrected non-local coordinates */
1110                 if (DOMAINDECOMP(cr))
1111                 {
1112                     dd_move_x_constraints(cr->dd, box, xpPadded.unpaddedArrayRef(), ArrayRef<RVec>(), FALSE);
1113                 }
1114             }
1115 #pragma omp barrier
1116         }
1117         else if (lincsd->bTaskDep)
1118         {
1119 #pragma omp barrier
1120         }
1121
1122 #if GMX_SIMD_HAVE_REAL
1123         calc_dist_iter_simd(
1124                 b0, b1, atoms, xp, bllen.data(), blc.data(), pbc_simd, wfac, rhs1.data(), sol.data(), bWarn);
1125 #else
1126         calc_dist_iter(b0, b1, atoms, xp, bllen.data(), blc.data(), pbc, wfac, rhs1.data(), sol.data(), bWarn);
1127         /* 20*ncons flops */
1128 #endif // GMX_SIMD_HAVE_REAL
1129
1130         lincs_matrix_expand(*lincsd, lincsd->task[th], blcc, rhs1, rhs2, sol);
1131         /* nrec*(ncons+2*nrtot) flops */
1132
1133 #if GMX_SIMD_HAVE_REAL
1134         for (int b = b0; b < b1; b += GMX_SIMD_REAL_WIDTH)
1135         {
1136             SimdReal t1  = load<SimdReal>(blc.data() + b);
1137             SimdReal t2  = load<SimdReal>(sol.data() + b);
1138             SimdReal mvb = t1 * t2;
1139             store(blc_sol.data() + b, mvb);
1140             store(mlambda.data() + b, load<SimdReal>(mlambda.data() + b) + mvb);
1141         }
1142 #else
1143         for (int b = b0; b < b1; b++)
1144         {
1145             real mvb   = blc[b] * sol[b];
1146             blc_sol[b] = mvb;
1147             mlambda[b] += mvb;
1148         }
1149 #endif // GMX_SIMD_HAVE_REAL
1150
1151         /* Update the coordinates */
1152         lincs_update_atoms(lincsd, th, 1.0, blc_sol, r, invmass, xp);
1153     }
1154     /* nit*ncons*(37+9*nrec) flops */
1155
1156     if (v != nullptr)
1157     {
1158         /* Update the velocities */
1159         lincs_update_atoms(lincsd, th, invdt, mlambda, r, invmass, v);
1160         /* 16 ncons flops */
1161     }
1162
1163     if (!nlocat.empty() && (bCalcDHDL || bCalcVir))
1164     {
1165         if (lincsd->bTaskDep)
1166         {
1167             /* In lincs_update_atoms threads might cross-read mlambda */
1168 #pragma omp barrier
1169         }
1170
1171         /* Only account for local atoms */
1172         for (int b = b0; b < b1; b++)
1173         {
1174             mlambda[b] *= 0.5 * nlocat[b];
1175         }
1176     }
1177
1178     if (bCalcDHDL)
1179     {
1180         real dhdl = 0;
1181         for (int b = b0; b < b1; b++)
1182         {
1183             /* Note that this this is dhdl*dt^2, the dt^2 factor is corrected
1184              * later after the contributions are reduced over the threads.
1185              */
1186             dhdl -= lincsd->mlambda[b] * lincsd->ddist[b];
1187         }
1188         lincsd->task[th].dhdlambda = dhdl;
1189     }
1190
1191     if (bCalcVir)
1192     {
1193         /* Constraint virial */
1194         for (int b = b0; b < b1; b++)
1195         {
1196             real tmp0 = -bllen[b] * mlambda[b];
1197             for (int i = 0; i < DIM; i++)
1198             {
1199                 real tmp1 = tmp0 * r[b][i];
1200                 for (int j = 0; j < DIM; j++)
1201                 {
1202                     vir_r_m_dr[i][j] -= tmp1 * r[b][j];
1203                 }
1204             }
1205         } /* 22 ncons flops */
1206     }
1207
1208     /* Total:
1209      * 26*ncons + 6*nrtot + nrec*(ncons+2*nrtot)
1210      * + nit * (20*ncons + nrec*(ncons+2*nrtot) + 17 ncons)
1211      *
1212      * (26+nrec)*ncons + (6+2*nrec)*nrtot
1213      * + nit * ((37+nrec)*ncons + 2*nrec*nrtot)
1214      * if nit=1
1215      * (63+nrec)*ncons + (6+4*nrec)*nrtot
1216      */
1217 }
1218
1219 /*! \brief Sets the elements in the LINCS matrix for task task. */
1220 static void set_lincs_matrix_task(Lincs*               li,
1221                                   Task*                li_task,
1222                                   ArrayRef<const real> invmass,
1223                                   int*                 ncc_triangle,
1224                                   int*                 nCrossTaskTriangles)
1225 {
1226     /* Construct the coupling coefficient matrix blmf */
1227     li_task->ntriangle   = 0;
1228     *ncc_triangle        = 0;
1229     *nCrossTaskTriangles = 0;
1230     for (int i = li_task->b0; i < li_task->b1; i++)
1231     {
1232         const int a1 = li->atoms[i].index1;
1233         const int a2 = li->atoms[i].index2;
1234         for (int n = li->blnr[i]; n < li->blnr[i + 1]; n++)
1235         {
1236             const int k = li->blbnb[n];
1237
1238             /* If we are using multiple, independent tasks for LINCS,
1239              * the calls to check_assign_connected should have
1240              * put all connected constraints in our task.
1241              */
1242             assert(li->bTaskDep || (k >= li_task->b0 && k < li_task->b1));
1243
1244             int sign;
1245             if (a1 == li->atoms[k].index1 || a2 == li->atoms[k].index2)
1246             {
1247                 sign = -1;
1248             }
1249             else
1250             {
1251                 sign = 1;
1252             }
1253             int center;
1254             int end;
1255             if (a1 == li->atoms[k].index1 || a1 == li->atoms[k].index2)
1256             {
1257                 center = a1;
1258                 end    = a2;
1259             }
1260             else
1261             {
1262                 center = a2;
1263                 end    = a1;
1264             }
1265             li->blmf[n]  = sign * invmass[center] * li->blc[i] * li->blc[k];
1266             li->blmf1[n] = sign * 0.5;
1267             if (li->ncg_triangle > 0)
1268             {
1269                 /* Look for constraint triangles */
1270                 for (int nk = li->blnr[k]; nk < li->blnr[k + 1]; nk++)
1271                 {
1272                     const int kk = li->blbnb[nk];
1273                     if (kk != i && kk != k && (li->atoms[kk].index1 == end || li->atoms[kk].index2 == end))
1274                     {
1275                         /* Check if the constraints in this triangle actually
1276                          * belong to a different task. We still assign them
1277                          * here, since it's convenient for the triangle
1278                          * iterations, but we then need an extra barrier.
1279                          */
1280                         if (k < li_task->b0 || k >= li_task->b1 || kk < li_task->b0 || kk >= li_task->b1)
1281                         {
1282                             (*nCrossTaskTriangles)++;
1283                         }
1284
1285                         if (li_task->ntriangle == 0 || li_task->triangle[li_task->ntriangle - 1] < i)
1286                         {
1287                             /* Add this constraint to the triangle list */
1288                             li_task->triangle[li_task->ntriangle] = i;
1289                             li_task->tri_bits[li_task->ntriangle] = 0;
1290                             li_task->ntriangle++;
1291                             if (li->blnr[i + 1] - li->blnr[i]
1292                                 > static_cast<int>(sizeof(li_task->tri_bits[0]) * 8 - 1))
1293                             {
1294                                 gmx_fatal(FARGS,
1295                                           "A constraint is connected to %d constraints, this is "
1296                                           "more than the %zu allowed for constraints participating "
1297                                           "in triangles",
1298                                           li->blnr[i + 1] - li->blnr[i],
1299                                           sizeof(li_task->tri_bits[0]) * 8 - 1);
1300                             }
1301                         }
1302                         li_task->tri_bits[li_task->ntriangle - 1] |= (1 << (n - li->blnr[i]));
1303                         (*ncc_triangle)++;
1304                     }
1305                 }
1306             }
1307         }
1308     }
1309 }
1310
1311 /*! \brief Sets the elements in the LINCS matrix. */
1312 static void set_lincs_matrix(Lincs* li, ArrayRef<const real> invmass, real lambda)
1313 {
1314     const real invsqrt2 = 0.7071067811865475244;
1315
1316     for (int i = 0; (i < li->nc); i++)
1317     {
1318         const int a1 = li->atoms[i].index1;
1319         const int a2 = li->atoms[i].index2;
1320         li->blc[i]   = gmx::invsqrt(invmass[a1] + invmass[a2]);
1321         li->blc1[i]  = invsqrt2;
1322     }
1323
1324     /* Construct the coupling coefficient matrix blmf */
1325     int ntriangle = 0, ncc_triangle = 0, nCrossTaskTriangles = 0;
1326 #pragma omp parallel for reduction(+: ntriangle, ncc_triangle, nCrossTaskTriangles) num_threads(li->ntask) schedule(static)
1327     for (int th = 0; th < li->ntask; th++)
1328     {
1329         try
1330         {
1331             set_lincs_matrix_task(li, &li->task[th], invmass, &ncc_triangle, &nCrossTaskTriangles);
1332             ntriangle += li->task[th].ntriangle;
1333         }
1334         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1335     }
1336     li->ntriangle    = ntriangle;
1337     li->ncc_triangle = ncc_triangle;
1338     li->bTaskDepTri  = (nCrossTaskTriangles > 0);
1339
1340     if (debug)
1341     {
1342         fprintf(debug, "The %d constraints participate in %d triangles\n", li->nc, li->ntriangle);
1343         fprintf(debug, "There are %d constraint couplings, of which %d in triangles\n", li->ncc, li->ncc_triangle);
1344         if (li->ntriangle > 0 && li->ntask > 1)
1345         {
1346             fprintf(debug,
1347                     "%d constraint triangles contain constraints assigned to different tasks\n",
1348                     nCrossTaskTriangles);
1349         }
1350     }
1351
1352     /* Set matlam,
1353      * so we know with which lambda value the masses have been set.
1354      */
1355     li->matlam = lambda;
1356 }
1357
1358 //! Finds all triangles of atoms that share constraints to a central atom.
1359 static int count_triangle_constraints(const InteractionLists& ilist, const ListOfLists<int>& at2con)
1360 {
1361     const int ncon1    = ilist[F_CONSTR].size() / 3;
1362     const int ncon_tot = ncon1 + ilist[F_CONSTRNC].size() / 3;
1363
1364     gmx::ArrayRef<const int> ia1 = ilist[F_CONSTR].iatoms;
1365     gmx::ArrayRef<const int> ia2 = ilist[F_CONSTRNC].iatoms;
1366
1367     int ncon_triangle = 0;
1368     for (int c0 = 0; c0 < ncon_tot; c0++)
1369     {
1370         bool       bTriangle = FALSE;
1371         const int* iap       = constr_iatomptr(ia1, ia2, c0);
1372         const int  a00       = iap[1];
1373         const int  a01       = iap[2];
1374         for (const int c1 : at2con[a01])
1375         {
1376             if (c1 != c0)
1377             {
1378                 const int* iap = constr_iatomptr(ia1, ia2, c1);
1379                 const int  a10 = iap[1];
1380                 const int  a11 = iap[2];
1381                 int        ac1;
1382                 if (a10 == a01)
1383                 {
1384                     ac1 = a11;
1385                 }
1386                 else
1387                 {
1388                     ac1 = a10;
1389                 }
1390                 for (const int c2 : at2con[ac1])
1391                 {
1392                     if (c2 != c0 && c2 != c1)
1393                     {
1394                         const int* iap = constr_iatomptr(ia1, ia2, c2);
1395                         const int  a20 = iap[1];
1396                         const int  a21 = iap[2];
1397                         if (a20 == a00 || a21 == a00)
1398                         {
1399                             bTriangle = TRUE;
1400                         }
1401                     }
1402                 }
1403             }
1404         }
1405         if (bTriangle)
1406         {
1407             ncon_triangle++;
1408         }
1409     }
1410
1411     return ncon_triangle;
1412 }
1413
1414 //! Finds sequences of sequential constraints.
1415 static bool more_than_two_sequential_constraints(const InteractionLists& ilist, const ListOfLists<int>& at2con)
1416 {
1417     const int ncon1    = ilist[F_CONSTR].size() / 3;
1418     const int ncon_tot = ncon1 + ilist[F_CONSTRNC].size() / 3;
1419
1420     gmx::ArrayRef<const int> ia1 = ilist[F_CONSTR].iatoms;
1421     gmx::ArrayRef<const int> ia2 = ilist[F_CONSTRNC].iatoms;
1422
1423     for (int c = 0; c < ncon_tot; c++)
1424     {
1425         const int* iap = constr_iatomptr(ia1, ia2, c);
1426         const int  a1  = iap[1];
1427         const int  a2  = iap[2];
1428         /* Check if this constraint has constraints connected at both atoms */
1429         if (at2con[a1].ssize() > 1 && at2con[a2].ssize() > 1)
1430         {
1431             return true;
1432         }
1433     }
1434
1435     return false;
1436 }
1437
1438 Lincs* init_lincs(FILE*                            fplog,
1439                   const gmx_mtop_t&                mtop,
1440                   int                              nflexcon_global,
1441                   ArrayRef<const ListOfLists<int>> atomToConstraintsPerMolType,
1442                   bool                             bPLINCS,
1443                   int                              nIter,
1444                   int                              nProjOrder)
1445 {
1446     // TODO this should become a unique_ptr
1447     Lincs* li;
1448     bool   bMoreThanTwoSeq;
1449
1450     if (fplog)
1451     {
1452         fprintf(fplog, "\nInitializing%s LINear Constraint Solver\n", bPLINCS ? " Parallel" : "");
1453     }
1454
1455     li = new Lincs;
1456
1457     li->ncg      = gmx_mtop_ftype_count(mtop, F_CONSTR) + gmx_mtop_ftype_count(mtop, F_CONSTRNC);
1458     li->ncg_flex = nflexcon_global;
1459
1460     li->nIter  = nIter;
1461     li->nOrder = nProjOrder;
1462
1463     li->max_connect = 0;
1464     for (size_t mt = 0; mt < mtop.moltype.size(); mt++)
1465     {
1466         const auto& at2con = atomToConstraintsPerMolType[mt];
1467         for (int a = 0; a < mtop.moltype[mt].atoms.nr; a++)
1468         {
1469             li->max_connect = std::max(li->max_connect, int(at2con[a].ssize()));
1470         }
1471     }
1472
1473     li->ncg_triangle = 0;
1474     bMoreThanTwoSeq  = FALSE;
1475     for (const gmx_molblock_t& molb : mtop.molblock)
1476     {
1477         const gmx_moltype_t& molt   = mtop.moltype[molb.type];
1478         const auto&          at2con = atomToConstraintsPerMolType[molb.type];
1479
1480         li->ncg_triangle += molb.nmol * count_triangle_constraints(molt.ilist, at2con);
1481
1482         if (!bMoreThanTwoSeq && more_than_two_sequential_constraints(molt.ilist, at2con))
1483         {
1484             bMoreThanTwoSeq = TRUE;
1485         }
1486     }
1487
1488     /* Check if we need to communicate not only before LINCS,
1489      * but also before each iteration.
1490      * The check for only two sequential constraints is only
1491      * useful for the common case of H-bond only constraints.
1492      * With more effort we could also make it useful for small
1493      * molecules with nr. sequential constraints <= nOrder-1.
1494      */
1495     li->bCommIter = (bPLINCS && (li->nOrder < 1 || bMoreThanTwoSeq));
1496
1497     if (debug && bPLINCS)
1498     {
1499         fprintf(debug, "PLINCS communication before each iteration: %d\n", static_cast<int>(li->bCommIter));
1500     }
1501
1502     /* LINCS can run on any number of threads.
1503      * Currently the number is fixed for the whole simulation,
1504      * but it could be set in set_lincs().
1505      * The current constraint to task assignment code can create independent
1506      * tasks only when not more than two constraints are connected sequentially.
1507      */
1508     li->ntask    = gmx_omp_nthreads_get(ModuleMultiThread::Lincs);
1509     li->bTaskDep = (li->ntask > 1 && bMoreThanTwoSeq);
1510     if (debug)
1511     {
1512         fprintf(debug, "LINCS: using %d threads, tasks are %sdependent\n", li->ntask, li->bTaskDep ? "" : "in");
1513     }
1514     if (li->ntask == 1)
1515     {
1516         li->task.resize(1);
1517     }
1518     else
1519     {
1520         /* Allocate an extra elements for "task-overlap" constraints */
1521         li->task.resize(li->ntask + 1);
1522     }
1523
1524     if (bPLINCS || li->ncg_triangle > 0)
1525     {
1526         please_cite(fplog, "Hess2008a");
1527     }
1528     else
1529     {
1530         please_cite(fplog, "Hess97a");
1531     }
1532
1533     if (fplog)
1534     {
1535         fprintf(fplog, "The number of constraints is %d\n", li->ncg);
1536         if (bPLINCS)
1537         {
1538             fprintf(fplog,
1539                     "There are constraints between atoms in different decomposition domains,\n"
1540                     "will communicate selected coordinates each lincs iteration\n");
1541         }
1542         if (li->ncg_triangle > 0)
1543         {
1544             fprintf(fplog,
1545                     "%d constraints are involved in constraint triangles,\n"
1546                     "will apply an additional matrix expansion of order %d for couplings\n"
1547                     "between constraints inside triangles\n",
1548                     li->ncg_triangle,
1549                     li->nOrder);
1550         }
1551     }
1552
1553     return li;
1554 }
1555
1556 void done_lincs(Lincs* li)
1557 {
1558     delete li;
1559 }
1560
1561 /*! \brief Sets up the work division over the threads. */
1562 static void lincs_thread_setup(Lincs* li, int natoms)
1563 {
1564     li->atf.resize(natoms);
1565
1566     gmx::ArrayRef<gmx_bitmask_t> atf = li->atf;
1567
1568     /* Clear the atom flags */
1569     for (gmx_bitmask_t& mask : atf)
1570     {
1571         bitmask_clear(&mask);
1572     }
1573
1574     if (li->ntask > BITMASK_SIZE)
1575     {
1576         gmx_fatal(FARGS, "More than %d threads is not supported for LINCS.", BITMASK_SIZE);
1577     }
1578
1579     for (int th = 0; th < li->ntask; th++)
1580     {
1581         const Task* li_task = &li->task[th];
1582
1583         /* For each atom set a flag for constraints from each */
1584         for (int b = li_task->b0; b < li_task->b1; b++)
1585         {
1586             bitmask_set_bit(&atf[li->atoms[b].index1], th);
1587             bitmask_set_bit(&atf[li->atoms[b].index2], th);
1588         }
1589     }
1590
1591 #pragma omp parallel for num_threads(li->ntask) schedule(static)
1592     for (int th = 0; th < li->ntask; th++)
1593     {
1594         try
1595         {
1596             Task*         li_task;
1597             gmx_bitmask_t mask;
1598             int           b;
1599
1600             li_task = &li->task[th];
1601
1602             bitmask_init_low_bits(&mask, th);
1603
1604             li_task->ind.clear();
1605             li_task->ind_r.clear();
1606             for (b = li_task->b0; b < li_task->b1; b++)
1607             {
1608                 /* We let the constraint with the lowest thread index
1609                  * operate on atoms with constraints from multiple threads.
1610                  */
1611                 if (bitmask_is_disjoint(atf[li->atoms[b].index1], mask)
1612                     && bitmask_is_disjoint(atf[li->atoms[b].index2], mask))
1613                 {
1614                     /* Add the constraint to the local atom update index */
1615                     li_task->ind.push_back(b);
1616                 }
1617                 else
1618                 {
1619                     /* Add the constraint to the rest block */
1620                     li_task->ind_r.push_back(b);
1621                 }
1622             }
1623         }
1624         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1625     }
1626
1627     /* We need to copy all constraints which have not be assigned
1628      * to a thread to a separate list which will be handled by one thread.
1629      */
1630     Task* li_m = &li->task[li->ntask];
1631
1632     li_m->ind.clear();
1633     for (int th = 0; th < li->ntask; th++)
1634     {
1635         const Task& li_task = li->task[th];
1636
1637         for (int ind_r : li_task.ind_r)
1638         {
1639             li_m->ind.push_back(ind_r);
1640         }
1641
1642         if (debug)
1643         {
1644             fprintf(debug, "LINCS thread %d: %zu constraints\n", th, li_task.ind.size());
1645         }
1646     }
1647
1648     if (debug)
1649     {
1650         fprintf(debug, "LINCS thread r: %zu constraints\n", li_m->ind.size());
1651     }
1652 }
1653
1654 //! Assign a constraint.
1655 static void assign_constraint(Lincs*                  li,
1656                               int                     constraint_index,
1657                               int                     a1,
1658                               int                     a2,
1659                               real                    lenA,
1660                               real                    lenB,
1661                               const ListOfLists<int>& at2con)
1662 {
1663     int con;
1664
1665     con = li->nc;
1666
1667     /* Make an mapping of local topology constraint index to LINCS index */
1668     li->con_index[constraint_index] = con;
1669
1670     li->bllen0[con] = lenA;
1671     li->ddist[con]  = lenB - lenA;
1672     /* Set the length to the topology A length */
1673     li->bllen[con]        = lenA;
1674     li->atoms[con].index1 = a1;
1675     li->atoms[con].index2 = a2;
1676
1677     /* Make space in the constraint connection matrix for constraints
1678      * connected to both end of the current constraint.
1679      */
1680     li->ncc += at2con[a1].ssize() - 1 + at2con[a2].ssize() - 1;
1681
1682     li->blnr[con + 1] = li->ncc;
1683
1684     /* Increase the constraint count */
1685     li->nc++;
1686 }
1687
1688 /*! \brief Check if constraint with topology index constraint_index is connected
1689  * to other constraints, and if so add those connected constraints to our task. */
1690 static void check_assign_connected(Lincs*                        li,
1691                                    gmx::ArrayRef<const int>      iatom,
1692                                    const InteractionDefinitions& idef,
1693                                    bool                          bDynamics,
1694                                    int                           a1,
1695                                    int                           a2,
1696                                    const ListOfLists<int>&       at2con)
1697 {
1698     /* Currently this function only supports constraint groups
1699      * in which all constraints share at least one atom
1700      * (e.g. H-bond constraints).
1701      * Check both ends of the current constraint for
1702      * connected constraints. We need to assign those
1703      * to the same task.
1704      */
1705     for (int end = 0; end < 2; end++)
1706     {
1707         const int a = (end == 0 ? a1 : a2);
1708
1709         for (const int cc : at2con[a])
1710         {
1711             /* Check if constraint cc has not yet been assigned */
1712             if (li->con_index[cc] == -1)
1713             {
1714                 const int  type = iatom[cc * 3];
1715                 const real lenA = idef.iparams[type].constr.dA;
1716                 const real lenB = idef.iparams[type].constr.dB;
1717
1718                 if (bDynamics || lenA != 0 || lenB != 0)
1719                 {
1720                     assign_constraint(li, cc, iatom[3 * cc + 1], iatom[3 * cc + 2], lenA, lenB, at2con);
1721                 }
1722             }
1723         }
1724     }
1725 }
1726
1727 /*! \brief Check if constraint with topology index constraint_index is involved
1728  * in a constraint triangle, and if so add the other two constraints
1729  * in the triangle to our task. */
1730 static void check_assign_triangle(Lincs*                        li,
1731                                   gmx::ArrayRef<const int>      iatom,
1732                                   const InteractionDefinitions& idef,
1733                                   bool                          bDynamics,
1734                                   int                           constraint_index,
1735                                   int                           a1,
1736                                   int                           a2,
1737                                   const ListOfLists<int>&       at2con)
1738 {
1739     int nca, cc[32], ca[32];
1740     int c_triangle[2] = { -1, -1 };
1741
1742     nca = 0;
1743     for (const int c : at2con[a1])
1744     {
1745         if (c != constraint_index)
1746         {
1747             int aa1, aa2;
1748
1749             aa1 = iatom[c * 3 + 1];
1750             aa2 = iatom[c * 3 + 2];
1751             if (aa1 != a1)
1752             {
1753                 cc[nca] = c;
1754                 ca[nca] = aa1;
1755                 nca++;
1756             }
1757             if (aa2 != a1)
1758             {
1759                 cc[nca] = c;
1760                 ca[nca] = aa2;
1761                 nca++;
1762             }
1763         }
1764     }
1765
1766     for (const int c : at2con[a2])
1767     {
1768         if (c != constraint_index)
1769         {
1770             int aa1, aa2, i;
1771
1772             aa1 = iatom[c * 3 + 1];
1773             aa2 = iatom[c * 3 + 2];
1774             if (aa1 != a2)
1775             {
1776                 for (i = 0; i < nca; i++)
1777                 {
1778                     if (aa1 == ca[i])
1779                     {
1780                         c_triangle[0] = cc[i];
1781                         c_triangle[1] = c;
1782                     }
1783                 }
1784             }
1785             if (aa2 != a2)
1786             {
1787                 for (i = 0; i < nca; i++)
1788                 {
1789                     if (aa2 == ca[i])
1790                     {
1791                         c_triangle[0] = cc[i];
1792                         c_triangle[1] = c;
1793                     }
1794                 }
1795             }
1796         }
1797     }
1798
1799     if (c_triangle[0] >= 0)
1800     {
1801         int end;
1802
1803         for (end = 0; end < 2; end++)
1804         {
1805             /* Check if constraint c_triangle[end] has not yet been assigned */
1806             if (li->con_index[c_triangle[end]] == -1)
1807             {
1808                 int  i, type;
1809                 real lenA, lenB;
1810
1811                 i    = c_triangle[end] * 3;
1812                 type = iatom[i];
1813                 lenA = idef.iparams[type].constr.dA;
1814                 lenB = idef.iparams[type].constr.dB;
1815
1816                 if (bDynamics || lenA != 0 || lenB != 0)
1817                 {
1818                     assign_constraint(li, c_triangle[end], iatom[i + 1], iatom[i + 2], lenA, lenB, at2con);
1819                 }
1820             }
1821         }
1822     }
1823 }
1824
1825 //! Sets matrix indices.
1826 static void set_matrix_indices(Lincs* li, const Task& li_task, const ListOfLists<int>& at2con, bool bSortMatrix)
1827 {
1828     for (int b = li_task.b0; b < li_task.b1; b++)
1829     {
1830         const int a1 = li->atoms[b].index1;
1831         const int a2 = li->atoms[b].index2;
1832
1833         int i = li->blnr[b];
1834         for (const int constraint : at2con[a1])
1835         {
1836             const int concon = li->con_index[constraint];
1837             if (concon != b)
1838             {
1839                 li->blbnb[i++] = concon;
1840             }
1841         }
1842         for (const int constraint : at2con[a2])
1843         {
1844             const int concon = li->con_index[constraint];
1845             if (concon != b)
1846             {
1847                 li->blbnb[i++] = concon;
1848             }
1849         }
1850
1851         if (bSortMatrix)
1852         {
1853             /* Order the blbnb matrix to optimize memory access */
1854             std::sort(li->blbnb.begin() + li->blnr[b], li->blbnb.begin() + li->blnr[b + 1]);
1855         }
1856     }
1857 }
1858
1859 void set_lincs(const InteractionDefinitions& idef,
1860                const int                     numAtoms,
1861                ArrayRef<const real>          invmass,
1862                const real                    lambda,
1863                bool                          bDynamics,
1864                const t_commrec*              cr,
1865                Lincs*                        li)
1866 {
1867     li->nc_real = 0;
1868     li->nc      = 0;
1869     li->ncc     = 0;
1870     /* Zero the thread index ranges.
1871      * Otherwise without local constraints we could return with old ranges.
1872      */
1873     for (int i = 0; i < li->ntask; i++)
1874     {
1875         li->task[i].b0 = 0;
1876         li->task[i].b1 = 0;
1877         li->task[i].ind.clear();
1878     }
1879     if (li->ntask > 1)
1880     {
1881         li->task[li->ntask].ind.clear();
1882     }
1883
1884     /* This is the local topology, so there are only F_CONSTR constraints */
1885     if (idef.il[F_CONSTR].empty())
1886     {
1887         /* There are no constraints,
1888          * we do not need to fill any data structures.
1889          */
1890         return;
1891     }
1892
1893     if (debug)
1894     {
1895         fprintf(debug, "Building the LINCS connectivity\n");
1896     }
1897
1898     int natoms;
1899     if (DOMAINDECOMP(cr))
1900     {
1901         if (cr->dd->constraints)
1902         {
1903             int start;
1904
1905             dd_get_constraint_range(*cr->dd, &start, &natoms);
1906         }
1907         else
1908         {
1909             natoms = dd_numHomeAtoms(*cr->dd);
1910         }
1911     }
1912     else
1913     {
1914         natoms = numAtoms;
1915     }
1916
1917     const ListOfLists<int> at2con =
1918             make_at2con(natoms, idef.il, idef.iparams, flexibleConstraintTreatment(bDynamics));
1919
1920     const int ncon_tot = idef.il[F_CONSTR].size() / 3;
1921
1922     /* Ensure we have enough padding for aligned loads for each thread */
1923     const int numEntries = ncon_tot + li->ntask * simd_width;
1924     li->con_index.resize(numEntries);
1925     li->bllen0.resize(numEntries);
1926     li->ddist.resize(numEntries);
1927     li->atoms.resize(numEntries);
1928     li->blc.resize(numEntries);
1929     li->blc1.resize(numEntries);
1930     li->blnr.resize(numEntries + 1);
1931     li->bllen.resize(numEntries);
1932     li->tmpv.resizeWithPadding(numEntries);
1933     if (DOMAINDECOMP(cr))
1934     {
1935         li->nlocat.resize(numEntries);
1936     }
1937     li->tmp1.resize(numEntries);
1938     li->tmp2.resize(numEntries);
1939     li->tmp3.resize(numEntries);
1940     li->tmp4.resize(numEntries);
1941     li->mlambda.resize(numEntries);
1942
1943     gmx::ArrayRef<const int> iatom = idef.il[F_CONSTR].iatoms;
1944
1945     li->blnr[0] = li->ncc;
1946
1947     /* Assign the constraints for li->ntask LINCS tasks.
1948      * We target a uniform distribution of constraints over the tasks.
1949      * Note that when flexible constraints are present, but are removed here
1950      * (e.g. because we are doing EM) we get imbalance, but since that doesn't
1951      * happen during normal MD, that's ok.
1952      */
1953
1954     /* Determine the number of constraints we need to assign here */
1955     int ncon_assign = ncon_tot;
1956     if (!bDynamics)
1957     {
1958         /* With energy minimization, flexible constraints are ignored
1959          * (and thus minimized, as they should be).
1960          */
1961         ncon_assign -= countFlexibleConstraints(idef.il, idef.iparams);
1962     }
1963
1964     /* Set the target constraint count per task to exactly uniform,
1965      * this might be overridden below.
1966      */
1967     int ncon_target = (ncon_assign + li->ntask - 1) / li->ntask;
1968
1969     /* Mark all constraints as unassigned by setting their index to -1 */
1970     for (int con = 0; con < ncon_tot; con++)
1971     {
1972         li->con_index[con] = -1;
1973     }
1974
1975     int con = 0;
1976     for (int th = 0; th < li->ntask; th++)
1977     {
1978         Task* li_task;
1979
1980         li_task = &li->task[th];
1981
1982 #if GMX_SIMD_HAVE_REAL
1983         /* With indepedent tasks we likely have H-bond constraints or constraint
1984          * pairs. The connected constraints will be pulled into the task, so the
1985          * constraints per task will often exceed ncon_target.
1986          * Triangle constraints can also increase the count, but there are
1987          * relatively few of those, so we usually expect to get ncon_target.
1988          */
1989         if (li->bTaskDep)
1990         {
1991             /* We round ncon_target to a multiple of GMX_SIMD_WIDTH,
1992              * since otherwise a lot of operations can be wasted.
1993              * There are several ways to round here, we choose the one
1994              * that alternates block sizes, which helps with Intel HT.
1995              */
1996             ncon_target = ((ncon_assign * (th + 1)) / li->ntask - li->nc_real + GMX_SIMD_REAL_WIDTH - 1)
1997                           & ~(GMX_SIMD_REAL_WIDTH - 1);
1998         }
1999 #endif // GMX_SIMD==2 && GMX_SIMD_HAVE_REAL
2000
2001         /* Continue filling the arrays where we left off with the previous task,
2002          * including padding for SIMD.
2003          */
2004         li_task->b0 = li->nc;
2005
2006         gmx::ArrayRef<const t_iparams> iparams = idef.iparams;
2007
2008         while (con < ncon_tot && li->nc - li_task->b0 < ncon_target)
2009         {
2010             if (li->con_index[con] == -1)
2011             {
2012                 int  type, a1, a2;
2013                 real lenA, lenB;
2014
2015                 type = iatom[3 * con];
2016                 a1   = iatom[3 * con + 1];
2017                 a2   = iatom[3 * con + 2];
2018                 lenA = iparams[type].constr.dA;
2019                 lenB = iparams[type].constr.dB;
2020                 /* Skip the flexible constraints when not doing dynamics */
2021                 if (bDynamics || lenA != 0 || lenB != 0)
2022                 {
2023                     assign_constraint(li, con, a1, a2, lenA, lenB, at2con);
2024
2025                     if (li->ntask > 1 && !li->bTaskDep)
2026                     {
2027                         /* We can generate independent tasks. Check if we
2028                          * need to assign connected constraints to our task.
2029                          */
2030                         check_assign_connected(li, iatom, idef, bDynamics, a1, a2, at2con);
2031                     }
2032                     if (li->ntask > 1 && li->ncg_triangle > 0)
2033                     {
2034                         /* Ensure constraints in one triangle are assigned
2035                          * to the same task.
2036                          */
2037                         check_assign_triangle(li, iatom, idef, bDynamics, con, a1, a2, at2con);
2038                     }
2039                 }
2040             }
2041
2042             con++;
2043         }
2044
2045         li_task->b1 = li->nc;
2046
2047         if (simd_width > 1)
2048         {
2049             /* Copy the last atom pair indices and lengths for constraints
2050              * up to a multiple of simd_width, such that we can do all
2051              * SIMD operations without having to worry about end effects.
2052              */
2053             int i, last;
2054
2055             li->nc = ((li_task->b1 + simd_width - 1) / simd_width) * simd_width;
2056             last   = li_task->b1 - 1;
2057             for (i = li_task->b1; i < li->nc; i++)
2058             {
2059                 li->atoms[i]    = li->atoms[last];
2060                 li->bllen0[i]   = li->bllen0[last];
2061                 li->ddist[i]    = li->ddist[last];
2062                 li->bllen[i]    = li->bllen[last];
2063                 li->blnr[i + 1] = li->blnr[last + 1];
2064             }
2065         }
2066
2067         /* Keep track of how many constraints we assigned */
2068         li->nc_real += li_task->b1 - li_task->b0;
2069
2070         if (debug)
2071         {
2072             fprintf(debug, "LINCS task %d constraints %d - %d\n", th, li_task->b0, li_task->b1);
2073         }
2074     }
2075
2076     assert(li->nc_real == ncon_assign);
2077
2078     bool bSortMatrix;
2079
2080     /* Without DD we order the blbnb matrix to optimize memory access.
2081      * With DD the overhead of sorting is more than the gain during access.
2082      */
2083     bSortMatrix = !DOMAINDECOMP(cr);
2084
2085     li->blbnb.resize(li->ncc);
2086
2087 #pragma omp parallel for num_threads(li->ntask) schedule(static)
2088     for (int th = 0; th < li->ntask; th++)
2089     {
2090         try
2091         {
2092             Task& li_task = li->task[th];
2093
2094             if (li->ncg_triangle > 0)
2095             {
2096                 /* This is allocating too much, but it is difficult to improve */
2097                 li_task.triangle.resize(li_task.b1 - li_task.b0);
2098                 li_task.tri_bits.resize(li_task.b1 - li_task.b0);
2099             }
2100
2101             set_matrix_indices(li, li_task, at2con, bSortMatrix);
2102         }
2103         GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
2104     }
2105
2106     if (cr->dd == nullptr)
2107     {
2108         /* Since the matrix is static, we should free some memory */
2109         li->blbnb.resize(li->ncc);
2110     }
2111
2112     li->blmf.resize(li->ncc);
2113     li->blmf1.resize(li->ncc);
2114     li->tmpncc.resize(li->ncc);
2115
2116     gmx::ArrayRef<const int> nlocat_dd = dd_constraints_nlocalatoms(cr->dd);
2117     if (!nlocat_dd.empty())
2118     {
2119         /* Convert nlocat from local topology to LINCS constraint indexing */
2120         for (con = 0; con < ncon_tot; con++)
2121         {
2122             li->nlocat[li->con_index[con]] = nlocat_dd[con];
2123         }
2124     }
2125     else
2126     {
2127         li->nlocat.clear();
2128     }
2129
2130     if (debug)
2131     {
2132         fprintf(debug, "Number of constraints is %d, padded %d, couplings %d\n", li->nc_real, li->nc, li->ncc);
2133     }
2134
2135     if (li->ntask > 1)
2136     {
2137         lincs_thread_setup(li, numAtoms);
2138     }
2139
2140     set_lincs_matrix(li, invmass, lambda);
2141
2142     li->rmsdData[0] = 0.0;
2143     li->rmsdData[1] = 0.0;
2144 }
2145
2146 //! Issues a warning when LINCS constraints cannot be satisfied.
2147 static void lincs_warning(gmx_domdec_t*                 dd,
2148                           ArrayRef<const RVec>          x,
2149                           ArrayRef<const RVec>          xprime,
2150                           t_pbc*                        pbc,
2151                           int                           ncons,
2152                           gmx::ArrayRef<const AtomPair> atoms,
2153                           gmx::ArrayRef<const real>     bllen,
2154                           real                          wangle,
2155                           int                           maxwarn,
2156                           int*                          warncount)
2157 {
2158     real wfac = std::cos(gmx::c_deg2Rad * wangle);
2159
2160     fprintf(stderr,
2161             "bonds that rotated more than %g degrees:\n"
2162             " atom 1 atom 2  angle  previous, current, constraint length\n",
2163             wangle);
2164
2165     for (int b = 0; b < ncons; b++)
2166     {
2167         const int i = atoms[b].index1;
2168         const int j = atoms[b].index2;
2169         rvec      v0;
2170         rvec      v1;
2171         if (pbc)
2172         {
2173             pbc_dx_aiuc(pbc, x[i], x[j], v0);
2174             pbc_dx_aiuc(pbc, xprime[i], xprime[j], v1);
2175         }
2176         else
2177         {
2178             rvec_sub(x[i], x[j], v0);
2179             rvec_sub(xprime[i], xprime[j], v1);
2180         }
2181         real d0     = norm(v0);
2182         real d1     = norm(v1);
2183         real cosine = ::iprod(v0, v1) / (d0 * d1);
2184         if (cosine < wfac)
2185         {
2186             fprintf(stderr,
2187                     " %6d %6d  %5.1f  %8.4f %8.4f    %8.4f\n",
2188                     ddglatnr(dd, i),
2189                     ddglatnr(dd, j),
2190                     gmx::c_rad2Deg * std::acos(cosine),
2191                     d0,
2192                     d1,
2193                     bllen[b]);
2194             if (!std::isfinite(d1))
2195             {
2196                 gmx_fatal(FARGS, "Bond length not finite.");
2197             }
2198
2199             (*warncount)++;
2200         }
2201     }
2202     if (*warncount > maxwarn)
2203     {
2204         too_many_constraint_warnings(ConstraintAlgorithm::Lincs, *warncount);
2205     }
2206 }
2207
2208 //! Status information about how well LINCS satisified the constraints in this domain
2209 struct LincsDeviations
2210 {
2211     //! The maximum over all bonds in this domain of the relative deviation in bond lengths
2212     real maxDeviation = 0;
2213     //! Sum over all bonds in this domain of the squared relative deviation
2214     real sumSquaredDeviation = 0;
2215     //! Index of bond with max deviation
2216     int indexOfMaxDeviation = -1;
2217     //! Number of bonds constrained in this domain
2218     int numConstraints = 0;
2219 };
2220
2221 //! Determine how well the constraints have been satisfied.
2222 static LincsDeviations makeLincsDeviations(const Lincs& lincsd, ArrayRef<const RVec> x, const t_pbc* pbc)
2223 {
2224     LincsDeviations                result;
2225     const ArrayRef<const AtomPair> atoms  = lincsd.atoms;
2226     const ArrayRef<const real>     bllen  = lincsd.bllen;
2227     const ArrayRef<const int>      nlocat = lincsd.nlocat;
2228
2229     for (int task = 0; task < lincsd.ntask; task++)
2230     {
2231         for (int b = lincsd.task[task].b0; b < lincsd.task[task].b1; b++)
2232         {
2233             rvec dx;
2234             if (pbc)
2235             {
2236                 pbc_dx_aiuc(pbc, x[atoms[b].index1], x[atoms[b].index2], dx);
2237             }
2238             else
2239             {
2240                 rvec_sub(x[atoms[b].index1], x[atoms[b].index2], dx);
2241             }
2242             real r2  = ::norm2(dx);
2243             real len = r2 * gmx::invsqrt(r2);
2244             real d   = std::abs(len / bllen[b] - 1.0_real);
2245             if (d > result.maxDeviation && (nlocat.empty() || nlocat[b]))
2246             {
2247                 result.maxDeviation        = d;
2248                 result.indexOfMaxDeviation = b;
2249             }
2250             if (nlocat.empty())
2251             {
2252                 result.sumSquaredDeviation += d * d;
2253                 result.numConstraints++;
2254             }
2255             else
2256             {
2257                 result.sumSquaredDeviation += nlocat[b] * d * d;
2258                 result.numConstraints += nlocat[b];
2259             }
2260         }
2261     }
2262
2263     if (!nlocat.empty())
2264     {
2265         result.numConstraints /= 2;
2266         result.sumSquaredDeviation *= 0.5;
2267     }
2268     return result;
2269 }
2270
2271 bool constrain_lincs(bool                            computeRmsd,
2272                      const t_inputrec&               ir,
2273                      int64_t                         step,
2274                      Lincs*                          lincsd,
2275                      ArrayRef<const real>            invmass,
2276                      const t_commrec*                cr,
2277                      const gmx_multisim_t*           ms,
2278                      ArrayRefWithPadding<const RVec> xPadded,
2279                      ArrayRefWithPadding<RVec>       xprimePadded,
2280                      ArrayRef<RVec>                  min_proj,
2281                      const matrix                    box,
2282                      t_pbc*                          pbc,
2283                      const bool                      hasMassPerturbed,
2284                      real                            lambda,
2285                      real*                           dvdlambda,
2286                      real                            invdt,
2287                      ArrayRef<RVec>                  v,
2288                      bool                            bCalcVir,
2289                      tensor                          vir_r_m_dr,
2290                      ConstraintVariable              econq,
2291                      t_nrnb*                         nrnb,
2292                      int                             maxwarn,
2293                      int*                            warncount)
2294 {
2295     bool bOK = TRUE;
2296
2297     /* This boolean should be set by a flag passed to this routine.
2298      * We can also easily check if any constraint length is changed,
2299      * if not dH/dlambda=0 and we can also set the boolean to FALSE.
2300      */
2301     bool bCalcDHDL = (ir.efep != FreeEnergyPerturbationType::No && dvdlambda != nullptr);
2302
2303     if (lincsd->nc == 0 && cr->dd == nullptr)
2304     {
2305         if (computeRmsd)
2306         {
2307             lincsd->rmsdData = { { 0 } };
2308         }
2309
2310         return bOK;
2311     }
2312
2313     ArrayRef<const RVec> x      = xPadded.unpaddedArrayRef();
2314     ArrayRef<RVec>       xprime = xprimePadded.unpaddedArrayRef();
2315
2316     if (econq == ConstraintVariable::Positions)
2317     {
2318         /* We can't use bCalcDHDL here, since NULL can be passed for dvdlambda
2319          * also with efep!=fepNO.
2320          */
2321         if (ir.efep != FreeEnergyPerturbationType::No)
2322         {
2323             if (hasMassPerturbed && lincsd->matlam != lambda)
2324             {
2325                 set_lincs_matrix(lincsd, invmass, lambda);
2326             }
2327
2328             for (int i = 0; i < lincsd->nc; i++)
2329             {
2330                 lincsd->bllen[i] = lincsd->bllen0[i] + lambda * lincsd->ddist[i];
2331             }
2332         }
2333
2334         if (lincsd->ncg_flex)
2335         {
2336             /* Set the flexible constraint lengths to the old lengths */
2337             if (pbc != nullptr)
2338             {
2339                 for (int i = 0; i < lincsd->nc; i++)
2340                 {
2341                     if (lincsd->bllen[i] == 0)
2342                     {
2343                         rvec dx;
2344                         pbc_dx_aiuc(pbc, x[lincsd->atoms[i].index1], x[lincsd->atoms[i].index2], dx);
2345                         lincsd->bllen[i] = norm(dx);
2346                     }
2347                 }
2348             }
2349             else
2350             {
2351                 for (int i = 0; i < lincsd->nc; i++)
2352                 {
2353                     if (lincsd->bllen[i] == 0)
2354                     {
2355                         lincsd->bllen[i] = std::sqrt(
2356                                 distance2(x[lincsd->atoms[i].index1], x[lincsd->atoms[i].index2]));
2357                     }
2358                 }
2359             }
2360         }
2361
2362         const bool printDebugOutput = ((debug != nullptr) && lincsd->nc > 0);
2363         if (printDebugOutput)
2364         {
2365             LincsDeviations deviations = makeLincsDeviations(*lincsd, xprime, pbc);
2366             fprintf(debug, "   Rel. Constraint Deviation:  RMS         MAX     between atoms\n");
2367             fprintf(debug,
2368                     "       Before LINCS          %.6f    %.6f %6d %6d\n",
2369                     std::sqrt(deviations.sumSquaredDeviation / deviations.numConstraints),
2370                     deviations.maxDeviation,
2371                     ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index1),
2372                     ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index2));
2373         }
2374
2375         /* This bWarn var can be updated by multiple threads
2376          * at the same time. But as we only need to detect
2377          * if a warning occurred or not, this is not an issue.
2378          */
2379         bool bWarn = FALSE;
2380
2381         /* The OpenMP parallel region of constrain_lincs for coords */
2382 #pragma omp parallel num_threads(lincsd->ntask)
2383         {
2384             try
2385             {
2386                 int th = gmx_omp_get_thread_num();
2387
2388                 clear_mat(lincsd->task[th].vir_r_m_dr);
2389
2390                 do_lincs(xPadded,
2391                          xprimePadded,
2392                          box,
2393                          pbc,
2394                          lincsd,
2395                          th,
2396                          invmass,
2397                          cr,
2398                          bCalcDHDL,
2399                          ir.LincsWarnAngle,
2400                          &bWarn,
2401                          invdt,
2402                          v,
2403                          bCalcVir,
2404                          th == 0 ? vir_r_m_dr : lincsd->task[th].vir_r_m_dr);
2405             }
2406             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
2407         }
2408
2409         if (computeRmsd || printDebugOutput || bWarn)
2410         {
2411             LincsDeviations deviations = makeLincsDeviations(*lincsd, xprime, pbc);
2412
2413             if (computeRmsd)
2414             {
2415                 // This is reduced across domains in compute_globals and
2416                 // reported to the log file.
2417                 lincsd->rmsdData[0] = deviations.numConstraints;
2418                 lincsd->rmsdData[1] = deviations.sumSquaredDeviation;
2419             }
2420             else
2421             {
2422                 // This is never read
2423                 lincsd->rmsdData = { { 0 } };
2424             }
2425             if (printDebugOutput)
2426             {
2427                 fprintf(debug,
2428                         "        After LINCS          %.6f    %.6f %6d %6d\n\n",
2429                         std::sqrt(deviations.sumSquaredDeviation / deviations.numConstraints),
2430                         deviations.maxDeviation,
2431                         ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index1),
2432                         ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index2));
2433             }
2434
2435             if (bWarn)
2436             {
2437                 if (maxwarn < INT_MAX)
2438                 {
2439                     std::string simMesg;
2440                     if (isMultiSim(ms))
2441                     {
2442                         simMesg += gmx::formatString(" in simulation %d", ms->simulationIndex_);
2443                     }
2444                     fprintf(stderr,
2445                             "\nStep %" PRId64
2446                             ", time %g (ps)  LINCS WARNING%s\n"
2447                             "relative constraint deviation after LINCS:\n"
2448                             "rms %.6f, max %.6f (between atoms %d and %d)\n",
2449                             step,
2450                             ir.init_t + step * ir.delta_t,
2451                             simMesg.c_str(),
2452                             std::sqrt(deviations.sumSquaredDeviation / deviations.numConstraints),
2453                             deviations.maxDeviation,
2454                             ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index1),
2455                             ddglatnr(cr->dd, lincsd->atoms[deviations.indexOfMaxDeviation].index2));
2456
2457                     lincs_warning(
2458                             cr->dd, x, xprime, pbc, lincsd->nc, lincsd->atoms, lincsd->bllen, ir.LincsWarnAngle, maxwarn, warncount);
2459                 }
2460                 bOK = (deviations.maxDeviation < 0.5);
2461             }
2462         }
2463
2464         if (lincsd->ncg_flex)
2465         {
2466             for (int i = 0; (i < lincsd->nc); i++)
2467             {
2468                 if (lincsd->bllen0[i] == 0 && lincsd->ddist[i] == 0)
2469                 {
2470                     lincsd->bllen[i] = 0;
2471                 }
2472             }
2473         }
2474     }
2475     else
2476     {
2477         /* The OpenMP parallel region of constrain_lincs for derivatives */
2478 #pragma omp parallel num_threads(lincsd->ntask)
2479         {
2480             try
2481             {
2482                 int th = gmx_omp_get_thread_num();
2483
2484                 do_lincsp(xPadded,
2485                           xprimePadded,
2486                           min_proj,
2487                           pbc,
2488                           lincsd,
2489                           th,
2490                           invmass,
2491                           econq,
2492                           bCalcDHDL,
2493                           bCalcVir,
2494                           th == 0 ? vir_r_m_dr : lincsd->task[th].vir_r_m_dr);
2495             }
2496             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
2497         }
2498     }
2499
2500     if (bCalcDHDL)
2501     {
2502         /* Reduce the dH/dlambda contributions over the threads */
2503         real dhdlambda;
2504         int  th;
2505
2506         dhdlambda = 0;
2507         for (th = 0; th < lincsd->ntask; th++)
2508         {
2509             dhdlambda += lincsd->task[th].dhdlambda;
2510         }
2511         if (econq == ConstraintVariable::Positions)
2512         {
2513             /* dhdlambda contains dH/dlambda*dt^2, correct for this */
2514             /* TODO This should probably use invdt, so that sd integrator scaling works properly */
2515             dhdlambda /= ir.delta_t * ir.delta_t;
2516         }
2517         *dvdlambda += dhdlambda;
2518     }
2519
2520     if (bCalcVir && lincsd->ntask > 1)
2521     {
2522         for (int i = 1; i < lincsd->ntask; i++)
2523         {
2524             m_add(vir_r_m_dr, lincsd->task[i].vir_r_m_dr, vir_r_m_dr);
2525         }
2526     }
2527
2528     /* count assuming nit=1 */
2529     inc_nrnb(nrnb, eNR_LINCS, lincsd->nc_real);
2530     inc_nrnb(nrnb, eNR_LINCSMAT, (2 + lincsd->nOrder) * lincsd->ncc);
2531     if (lincsd->ntriangle > 0)
2532     {
2533         inc_nrnb(nrnb, eNR_LINCSMAT, lincsd->nOrder * lincsd->ncc_triangle);
2534     }
2535     if (!v.empty())
2536     {
2537         inc_nrnb(nrnb, eNR_CONSTR_V, lincsd->nc_real * 2);
2538     }
2539     if (bCalcVir)
2540     {
2541         inc_nrnb(nrnb, eNR_CONSTR_VIR, lincsd->nc_real);
2542     }
2543
2544     return bOK;
2545 }
2546
2547 } // namespace gmx