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