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