Make do_constrain_first() independent of t_state
[alexxy/gromacs.git] / src / gromacs / domdec / domdec_constraints.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2006,2007,2008,2009,2010,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35
36 /*! \internal \file
37  *
38  * \brief This file implements functions for domdec to use
39  * while managing inter-atomic constraints.
40  *
41  * \author Berk Hess <hess@kth.se>
42  * \ingroup module_domdec
43  */
44
45 #include "gmxpre.h"
46
47 #include "domdec_constraints.h"
48
49 #include <cassert>
50
51 #include <algorithm>
52 #include <memory>
53
54 #include "gromacs/domdec/dlbtiming.h"
55 #include "gromacs/domdec/domdec.h"
56 #include "gromacs/domdec/domdec_struct.h"
57 #include "gromacs/domdec/ga2la.h"
58 #include "gromacs/domdec/hashedmap.h"
59 #include "gromacs/math/vec.h"
60 #include "gromacs/mdlib/constr.h"
61 #include "gromacs/mdlib/gmx_omp_nthreads.h"
62 #include "gromacs/mdtypes/commrec.h"
63 #include "gromacs/mdtypes/forcerec.h" // only for GET_CGINFO_*
64 #include "gromacs/pbcutil/ishift.h"
65 #include "gromacs/topology/ifunc.h"
66 #include "gromacs/topology/mtop_lookup.h"
67 #include "gromacs/utility/exceptions.h"
68 #include "gromacs/utility/fatalerror.h"
69 #include "gromacs/utility/gmxassert.h"
70 #include "gromacs/utility/smalloc.h"
71
72 #include "domdec_internal.h"
73 #include "domdec_specatomcomm.h"
74
75 /*! \brief Struct used during constraint setup with domain decomposition */
76 struct gmx_domdec_constraints_t
77 {
78     //! @cond Doxygen_Suppress
79     std::vector<int>  molb_con_offset; /**< Offset in the constraint array for each molblock */
80     std::vector<int>  molb_ncon_mol;   /**< The number of constraints per molecule for each molblock */
81
82     int               ncon;            /**< The fully local and conneced constraints */
83     /* The global constraint number, only required for clearing gc_req */
84     std::vector<int>  con_gl;          /**< Global constraint indices for local constraints */
85     std::vector<int>  con_nlocat;      /**< Number of local atoms (2/1/0) for each constraint */
86
87     std::vector<bool> gc_req;          /**< Boolean that tells if a global constraint index has been requested; note: size global #constraints */
88
89     /* Hash table for keeping track of requests */
90     std::unique_ptr < gmx::HashedMap < int>> ga2la; /**< Global to local communicated constraint atom only index */
91
92     /* Multi-threading stuff */
93     int                  nthread; /**< Number of threads used for DD constraint setup */
94     std::vector<t_ilist> ils;     /**< Constraint ilist working arrays, size \p nthread */
95
96     /* Buffers for requesting atoms */
97     std::vector < std::vector < int>> requestedGlobalAtomIndices; /**< Buffers for requesting global atom indices, one per thread */
98
99     //! @endcond
100 };
101
102 void dd_move_x_constraints(gmx_domdec_t *dd, const matrix box,
103                            rvec *x0, rvec *x1, gmx_bool bX1IsCoord)
104 {
105     if (dd->constraint_comm)
106     {
107         dd_move_x_specat(dd, dd->constraint_comm, box, x0, x1, bX1IsCoord);
108
109         ddReopenBalanceRegionCpu(dd);
110     }
111 }
112
113 gmx::ArrayRef<const int> dd_constraints_nlocalatoms(const gmx_domdec_t *dd)
114 {
115     if (dd && dd->constraints)
116     {
117         return dd->constraints->con_nlocat;
118     }
119     else
120     {
121         return {};
122     }
123 }
124
125 void dd_clear_local_constraint_indices(gmx_domdec_t *dd)
126 {
127     gmx_domdec_constraints_t *dc = dd->constraints;
128
129     std::fill(dc->gc_req.begin(), dc->gc_req.end(), false);
130
131     if (dd->constraint_comm)
132     {
133         dc->ga2la->clear();
134     }
135 }
136
137 /*! \brief Walks over the constraints out from the local atoms into the non-local atoms and adds them to a list */
138 static void walk_out(int con, int con_offset, int a, int offset, int nrec,
139                      gmx::ArrayRef<const int> ia1,
140                      gmx::ArrayRef<const int> ia2,
141                      const t_blocka *at2con,
142                      const gmx_ga2la_t &ga2la, gmx_bool bHomeConnect,
143                      gmx_domdec_constraints_t *dc,
144                      gmx_domdec_specat_comm_t *dcc,
145                      t_ilist *il_local,
146                      std::vector<int> *ireq)
147 {
148     int            a1_gl, a2_gl, i, coni, b;
149     const t_iatom *iap;
150
151     if (!dc->gc_req[con_offset + con])
152     {
153         /* Add this non-home constraint to the list */
154         dc->con_gl.push_back(con_offset + con);
155         dc->con_nlocat.push_back(bHomeConnect ? 1 : 0);
156         dc->gc_req[con_offset + con] = true;
157         if (il_local->nr + 3 > il_local->nalloc)
158         {
159             il_local->nalloc = over_alloc_dd(il_local->nr+3);
160             srenew(il_local->iatoms, il_local->nalloc);
161         }
162         iap = constr_iatomptr(ia1, ia2, con);
163         il_local->iatoms[il_local->nr++] = iap[0];
164         a1_gl = offset + iap[1];
165         a2_gl = offset + iap[2];
166         /* The following indexing code can probably be optizimed */
167         if (const int *a_loc = ga2la.findHome(a1_gl))
168         {
169             il_local->iatoms[il_local->nr++] = *a_loc;
170         }
171         else
172         {
173             /* We set this index later */
174             il_local->iatoms[il_local->nr++] = -a1_gl - 1;
175         }
176         if (const int *a_loc = ga2la.findHome(a2_gl))
177         {
178             il_local->iatoms[il_local->nr++] = *a_loc;
179         }
180         else
181         {
182             /* We set this index later */
183             il_local->iatoms[il_local->nr++] = -a2_gl - 1;
184         }
185         dc->ncon++;
186     }
187     /* Check to not ask for the same atom more than once */
188     if (!dc->ga2la->find(offset + a))
189     {
190         assert(dcc);
191         /* Add this non-home atom to the list */
192         ireq->push_back(offset + a);
193         /* Temporarily mark with -2, we get the index later */
194         dc->ga2la->insert(offset + a, -2);
195     }
196
197     if (nrec > 0)
198     {
199         for (i = at2con->index[a]; i < at2con->index[a+1]; i++)
200         {
201             coni = at2con->a[i];
202             if (coni != con)
203             {
204                 /* Walk further */
205                 iap = constr_iatomptr(ia1, ia2, coni);
206                 if (a == iap[1])
207                 {
208                     b = iap[2];
209                 }
210                 else
211                 {
212                     b = iap[1];
213                 }
214                 if (!ga2la.findHome(offset + b))
215                 {
216                     walk_out(coni, con_offset, b, offset, nrec-1,
217                              ia1, ia2, at2con,
218                              ga2la, FALSE, dc, dcc, il_local, ireq);
219                 }
220             }
221         }
222     }
223 }
224
225 /*! \brief Looks up SETTLE constraints for a range of charge-groups */
226 static void atoms_to_settles(gmx_domdec_t *dd,
227                              const gmx_mtop_t *mtop,
228                              const int *cginfo,
229                              gmx::ArrayRef < const std::vector < int>> at2settle_mt,
230                              int cg_start, int cg_end,
231                              t_ilist *ils_local,
232                              std::vector<int> *ireq)
233 {
234     const gmx_ga2la_t &ga2la = *dd->ga2la;
235     int                nral  = NRAL(F_SETTLE);
236
237     int                mb    = 0;
238     for (int a = cg_start; a < cg_end; a++)
239     {
240         if (GET_CGINFO_SETTLE(cginfo[a]))
241         {
242             int a_gl = dd->globalAtomIndices[a];
243             int a_mol;
244             mtopGetMolblockIndex(mtop, a_gl, &mb, nullptr, &a_mol);
245
246             const gmx_molblock_t *molb   = &mtop->molblock[mb];
247             int                   settle = at2settle_mt[molb->type][a_mol];
248
249             if (settle >= 0)
250             {
251                 int        offset  = a_gl - a_mol;
252
253                 const int *ia1     = mtop->moltype[molb->type].ilist[F_SETTLE].iatoms.data();
254
255                 int        a_gls[3];
256                 gmx_bool   bAssign = FALSE;
257                 int        nlocal  = 0;
258                 for (int sa = 0; sa < nral; sa++)
259                 {
260                     int a_glsa = offset + ia1[settle*(1+nral)+1+sa];
261                     a_gls[sa]  = a_glsa;
262                     if (ga2la.findHome(a_glsa))
263                     {
264                         if (nlocal == 0 && a_gl == a_glsa)
265                         {
266                             bAssign = TRUE;
267                         }
268                         nlocal++;
269                     }
270                 }
271
272                 if (bAssign)
273                 {
274                     if (ils_local->nr+1+nral > ils_local->nalloc)
275                     {
276                         ils_local->nalloc = over_alloc_dd(ils_local->nr+1+nral);
277                         srenew(ils_local->iatoms, ils_local->nalloc);
278                     }
279
280                     ils_local->iatoms[ils_local->nr++] = ia1[settle*4];
281
282                     for (int sa = 0; sa < nral; sa++)
283                     {
284                         if (const int *a_loc = ga2la.findHome(a_gls[sa]))
285                         {
286                             ils_local->iatoms[ils_local->nr++] = *a_loc;
287                         }
288                         else
289                         {
290                             ils_local->iatoms[ils_local->nr++] = -a_gls[sa] - 1;
291                             /* Add this non-home atom to the list */
292                             ireq->push_back(a_gls[sa]);
293                             /* A check on double atom requests is
294                              * not required for settle.
295                              */
296                         }
297                     }
298                 }
299             }
300         }
301     }
302 }
303
304 /*! \brief Looks up constraint for the local atoms */
305 static void atoms_to_constraints(gmx_domdec_t *dd,
306                                  const gmx_mtop_t *mtop,
307                                  const int *cginfo,
308                                  gmx::ArrayRef<const t_blocka> at2con_mt, int nrec,
309                                  t_ilist *ilc_local,
310                                  std::vector<int> *ireq)
311 {
312     const t_blocka             *at2con;
313     int                         b_lo, offset, b_mol, i, con, con_offset;
314
315     gmx_domdec_constraints_t   *dc     = dd->constraints;
316     gmx_domdec_specat_comm_t   *dcc    = dd->constraint_comm;
317
318     const gmx_ga2la_t          &ga2la  = *dd->ga2la;
319
320     dc->con_gl.clear();
321     dc->con_nlocat.clear();
322
323     int mb    = 0;
324     int nhome = 0;
325     for (int a = 0; a < dd->ncg_home; a++)
326     {
327         if (GET_CGINFO_CONSTR(cginfo[a]))
328         {
329             int a_gl = dd->globalAtomIndices[a];
330             int molnr, a_mol;
331             mtopGetMolblockIndex(mtop, a_gl, &mb, &molnr, &a_mol);
332
333             const gmx_molblock_t     &molb = mtop->molblock[mb];
334
335             gmx::ArrayRef<const int>  ia1 = mtop->moltype[molb.type].ilist[F_CONSTR].iatoms;
336             gmx::ArrayRef<const int>  ia2 = mtop->moltype[molb.type].ilist[F_CONSTRNC].iatoms;
337
338             /* Calculate the global constraint number offset for the molecule.
339              * This is only required for the global index to make sure
340              * that we use each constraint only once.
341              */
342             con_offset =
343                 dc->molb_con_offset[mb] + molnr*dc->molb_ncon_mol[mb];
344
345             /* The global atom number offset for this molecule */
346             offset = a_gl - a_mol;
347             at2con = &at2con_mt[molb.type];
348             for (i = at2con->index[a_mol]; i < at2con->index[a_mol+1]; i++)
349             {
350                 con            = at2con->a[i];
351                 const int *iap = constr_iatomptr(ia1, ia2, con);
352                 if (a_mol == iap[1])
353                 {
354                     b_mol = iap[2];
355                 }
356                 else
357                 {
358                     b_mol = iap[1];
359                 }
360                 if (const int *a_loc = ga2la.findHome(offset + b_mol))
361                 {
362                     /* Add this fully home constraint at the first atom */
363                     if (a_mol < b_mol)
364                     {
365                         dc->con_gl.push_back(con_offset + con);
366                         dc->con_nlocat.push_back(2);
367                         if (ilc_local->nr + 3 > ilc_local->nalloc)
368                         {
369                             ilc_local->nalloc = over_alloc_dd(ilc_local->nr + 3);
370                             srenew(ilc_local->iatoms, ilc_local->nalloc);
371                         }
372                         b_lo = *a_loc;
373                         ilc_local->iatoms[ilc_local->nr++] = iap[0];
374                         ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? a    : b_lo);
375                         ilc_local->iatoms[ilc_local->nr++] = (a_gl == iap[1] ? b_lo : a   );
376                         dc->ncon++;
377                         nhome++;
378                     }
379                 }
380                 else
381                 {
382                     /* We need the nrec constraints coupled to this constraint,
383                      * so we need to walk out of the home cell by nrec+1 atoms,
384                      * since already atom bg is not locally present.
385                      * Therefore we call walk_out with nrec recursions to go
386                      * after this first call.
387                      */
388                     walk_out(con, con_offset, b_mol, offset, nrec,
389                              ia1, ia2, at2con,
390                              ga2la, TRUE, dc, dcc, ilc_local, ireq);
391                 }
392             }
393         }
394     }
395
396     GMX_ASSERT(dc->con_gl.size() == static_cast<size_t>(dc->ncon), "con_gl size should match the number of constraints");
397     GMX_ASSERT(dc->con_nlocat.size() == static_cast<size_t>(dc->ncon), "con_nlocat size should match the number of constraints");
398
399     if (debug)
400     {
401         fprintf(debug,
402                 "Constraints: home %3d border %3d atoms: %3zu\n",
403                 nhome, dc->ncon-nhome,
404                 dd->constraint_comm ? ireq->size() : 0);
405     }
406 }
407
408 int dd_make_local_constraints(gmx_domdec_t *dd, int at_start,
409                               const struct gmx_mtop_t *mtop,
410                               const int *cginfo,
411                               gmx::Constraints *constr, int nrec,
412                               t_ilist *il_local)
413 {
414     gmx_domdec_constraints_t     *dc;
415     t_ilist                      *ilc_local, *ils_local;
416     std::vector<int>             *ireq;
417     gmx::ArrayRef<const t_blocka> at2con_mt;
418     gmx::HashedMap<int>          *ga2la_specat;
419     int at_end, i, j;
420     t_iatom                      *iap;
421
422     // This code should not be called unless this condition is true,
423     // because that's the only time init_domdec_constraints is
424     // called...
425     GMX_RELEASE_ASSERT(dd->comm->systemInfo.haveSplitConstraints ||
426                        dd->comm->systemInfo.haveSplitSettles,
427                        "dd_make_local_constraints called when there are no local constraints");
428     // ... and init_domdec_constraints always sets
429     // dd->constraint_comm...
430     GMX_RELEASE_ASSERT(dd->constraint_comm, "Invalid use of dd_make_local_constraints before construction of constraint_comm");
431     // ... which static analysis needs to be reassured about, because
432     // otherwise, when dd->splitSettles is
433     // true. dd->constraint_comm is unilaterally dereferenced before
434     // the call to atoms_to_settles.
435
436     dc = dd->constraints;
437
438     ilc_local = &il_local[F_CONSTR];
439     ils_local = &il_local[F_SETTLE];
440
441     dc->ncon      = 0;
442     ilc_local->nr = 0;
443     if (dd->constraint_comm)
444     {
445         // TODO Perhaps gmx_domdec_constraints_t should keep a valid constr?
446         GMX_RELEASE_ASSERT(constr != nullptr, "Must have valid constraints object");
447         at2con_mt = constr->atom2constraints_moltype();
448         ireq      = &dc->requestedGlobalAtomIndices[0];
449         ireq->clear();
450     }
451     else
452     {
453         // Currently unreachable
454         at2con_mt = {};
455         ireq      = nullptr;
456     }
457
458     gmx::ArrayRef < const std::vector < int>> at2settle_mt;
459     /* When settle works inside charge groups, we assigned them already */
460     if (dd->comm->systemInfo.haveSplitSettles)
461     {
462         // TODO Perhaps gmx_domdec_constraints_t should keep a valid constr?
463         GMX_RELEASE_ASSERT(constr != nullptr, "Must have valid constraints object");
464         at2settle_mt  = constr->atom2settle_moltype();
465         ils_local->nr = 0;
466     }
467
468     if (at2settle_mt.empty())
469     {
470         atoms_to_constraints(dd, mtop, cginfo, at2con_mt, nrec,
471                              ilc_local, ireq);
472     }
473     else
474     {
475         int t0_set;
476
477         /* Do the constraints, if present, on the first thread.
478          * Do the settles on all other threads.
479          */
480         t0_set = ((!at2con_mt.empty() && dc->nthread > 1) ? 1 : 0);
481
482 #pragma omp parallel for num_threads(dc->nthread) schedule(static)
483         for (int thread = 0; thread < dc->nthread; thread++)
484         {
485             try
486             {
487                 if (!at2con_mt.empty() && thread == 0)
488                 {
489                     atoms_to_constraints(dd, mtop, cginfo, at2con_mt, nrec,
490                                          ilc_local, ireq);
491                 }
492
493                 if (thread >= t0_set)
494                 {
495                     int        cg0, cg1;
496                     t_ilist   *ilst;
497
498                     /* Distribute the settle check+assignments over
499                      * dc->nthread or dc->nthread-1 threads.
500                      */
501                     cg0 = (dd->ncg_home*(thread-t0_set  ))/(dc->nthread-t0_set);
502                     cg1 = (dd->ncg_home*(thread-t0_set+1))/(dc->nthread-t0_set);
503
504                     if (thread == t0_set)
505                     {
506                         ilst = ils_local;
507                     }
508                     else
509                     {
510                         ilst = &dc->ils[thread];
511                     }
512                     ilst->nr = 0;
513
514                     std::vector<int> &ireqt = dc->requestedGlobalAtomIndices[thread];
515                     if (thread > 0)
516                     {
517                         ireqt.clear();
518                     }
519
520                     atoms_to_settles(dd, mtop, cginfo, at2settle_mt,
521                                      cg0, cg1,
522                                      ilst, &ireqt);
523                 }
524             }
525             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
526         }
527
528         /* Combine the generate settles and requested indices */
529         for (int thread = 1; thread < dc->nthread; thread++)
530         {
531             t_ilist   *ilst;
532             int        ia;
533
534             if (thread > t0_set)
535             {
536                 ilst = &dc->ils[thread];
537                 if (ils_local->nr + ilst->nr > ils_local->nalloc)
538                 {
539                     ils_local->nalloc = over_alloc_large(ils_local->nr + ilst->nr);
540                     srenew(ils_local->iatoms, ils_local->nalloc);
541                 }
542                 for (ia = 0; ia < ilst->nr; ia++)
543                 {
544                     ils_local->iatoms[ils_local->nr+ia] = ilst->iatoms[ia];
545                 }
546                 ils_local->nr += ilst->nr;
547             }
548
549             const std::vector<int> &ireqt = dc->requestedGlobalAtomIndices[thread];
550             ireq->insert(ireq->end(), ireqt.begin(), ireqt.end());
551         }
552
553         if (debug)
554         {
555             fprintf(debug, "Settles: total %3d\n", ils_local->nr/4);
556         }
557     }
558
559     if (dd->constraint_comm)
560     {
561         int nral1;
562
563         at_end =
564             setup_specat_communication(dd, ireq, dd->constraint_comm,
565                                        dd->constraints->ga2la.get(),
566                                        at_start, 2,
567                                        "constraint", " or lincs-order");
568
569         /* Fill in the missing indices */
570         ga2la_specat = dd->constraints->ga2la.get();
571
572         nral1 = 1 + NRAL(F_CONSTR);
573         for (i = 0; i < ilc_local->nr; i += nral1)
574         {
575             iap = ilc_local->iatoms + i;
576             for (j = 1; j < nral1; j++)
577             {
578                 if (iap[j] < 0)
579                 {
580                     const int *a = ga2la_specat->find(-iap[j] - 1);
581                     GMX_ASSERT(a, "We have checked before that this atom index has been set");
582                     iap[j] = *a;
583                 }
584             }
585         }
586
587         nral1 = 1 + NRAL(F_SETTLE);
588         for (i = 0; i < ils_local->nr; i += nral1)
589         {
590             iap = ils_local->iatoms + i;
591             for (j = 1; j < nral1; j++)
592             {
593                 if (iap[j] < 0)
594                 {
595                     const int *a = ga2la_specat->find(-iap[j] - 1);
596                     GMX_ASSERT(a, "We have checked before that this atom index has been set");
597                     iap[j] = *a;
598                 }
599             }
600         }
601     }
602     else
603     {
604         // Currently unreachable
605         at_end = at_start;
606     }
607
608     return at_end;
609 }
610
611 void init_domdec_constraints(gmx_domdec_t     *dd,
612                              const gmx_mtop_t *mtop)
613 {
614     gmx_domdec_constraints_t *dc;
615     const gmx_molblock_t     *molb;
616
617     if (debug)
618     {
619         fprintf(debug, "Begin init_domdec_constraints\n");
620     }
621
622     dd->constraints = new gmx_domdec_constraints_t;
623     dc              = dd->constraints;
624
625     dc->molb_con_offset.resize(mtop->molblock.size());
626     dc->molb_ncon_mol.resize(mtop->molblock.size());
627
628     int ncon = 0;
629     for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
630     {
631         molb                    = &mtop->molblock[mb];
632         dc->molb_con_offset[mb] = ncon;
633         dc->molb_ncon_mol[mb]   =
634             mtop->moltype[molb->type].ilist[F_CONSTR].size()/3 +
635             mtop->moltype[molb->type].ilist[F_CONSTRNC].size()/3;
636         ncon += molb->nmol*dc->molb_ncon_mol[mb];
637     }
638
639     if (ncon > 0)
640     {
641         dc->gc_req.resize(ncon);
642     }
643
644     /* Use a hash table for the global to local index.
645      * The number of keys is a rough estimate, it will be optimized later.
646      */
647     int numKeysEstimate = std::min(mtop->natoms/20,
648                                    mtop->natoms/(2*dd->nnodes));
649     dc->ga2la = std::make_unique < gmx::HashedMap < int>>(numKeysEstimate);
650
651     dc->nthread = gmx_omp_nthreads_get(emntDomdec);
652     dc->ils.resize(dc->nthread);
653
654     dd->constraint_comm = new gmx_domdec_specat_comm_t;
655
656     dc->requestedGlobalAtomIndices.resize(dc->nthread);
657 }