17fa1b19ff77b5009dfcec74f74c41c3e1a4738a
[alexxy/gromacs.git] / src / gromacs / mdlib / constr.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
7  * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 /*! \internal \file
39  * \brief Defines the high-level constraint 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 "constr.h"
48
49 #include <cassert>
50 #include <cmath>
51 #include <cstdlib>
52
53 #include <algorithm>
54
55 #include "gromacs/domdec/domdec.h"
56 #include "gromacs/domdec/domdec_struct.h"
57 #include "gromacs/essentialdynamics/edsam.h"
58 #include "gromacs/fileio/confio.h"
59 #include "gromacs/fileio/gmxfio.h"
60 #include "gromacs/fileio/pdbio.h"
61 #include "gromacs/gmxlib/nrnb.h"
62 #include "gromacs/math/arrayrefwithpadding.h"
63 #include "gromacs/math/utilities.h"
64 #include "gromacs/math/vec.h"
65 #include "gromacs/mdlib/gmx_omp_nthreads.h"
66 #include "gromacs/mdlib/lincs.h"
67 #include "gromacs/mdlib/settle.h"
68 #include "gromacs/mdlib/shake.h"
69 #include "gromacs/mdtypes/commrec.h"
70 #include "gromacs/mdtypes/inputrec.h"
71 #include "gromacs/mdtypes/md_enums.h"
72 #include "gromacs/mdtypes/state.h"
73 #include "gromacs/pbcutil/pbc.h"
74 #include "gromacs/pulling/pull.h"
75 #include "gromacs/timing/wallcycle.h"
76 #include "gromacs/topology/ifunc.h"
77 #include "gromacs/topology/mtop_lookup.h"
78 #include "gromacs/topology/mtop_util.h"
79 #include "gromacs/utility/arrayref.h"
80 #include "gromacs/utility/exceptions.h"
81 #include "gromacs/utility/fatalerror.h"
82 #include "gromacs/utility/gmxassert.h"
83 #include "gromacs/utility/listoflists.h"
84 #include "gromacs/utility/pleasecite.h"
85 #include "gromacs/utility/txtdump.h"
86
87 namespace gmx
88 {
89
90 /* \brief Impl class for Constraints
91  *
92  * \todo Members like md, idef are valid only for the lifetime of a
93  * domain, which would be good to make clearer in the structure of the
94  * code. It should not be possible to call apply() if setConstraints()
95  * has not been called. For example, this could be achieved if
96  * setConstraints returned a valid object with such a method.  That
97  * still requires that we manage the lifetime of that object
98  * correctly, however. */
99 class Constraints::Impl
100 {
101 public:
102     Impl(const gmx_mtop_t&     mtop_p,
103          const t_inputrec&     ir_p,
104          pull_t*               pull_work,
105          FILE*                 log_p,
106          const t_commrec*      cr_p,
107          bool                  useUpdateGroups,
108          const gmx_multisim_t* ms,
109          t_nrnb*               nrnb,
110          gmx_wallcycle*        wcycle_p,
111          bool                  pbcHandlingRequired,
112          int                   numConstraints,
113          int                   numSettles);
114     ~Impl();
115     void setConstraints(gmx_localtop_t*                     top,
116                         int                                 numAtoms,
117                         int                                 numHomeAtoms,
118                         gmx::ArrayRef<const real>           masses,
119                         gmx::ArrayRef<const real>           inverseMasses,
120                         bool                                hasMassPerturbedAtoms,
121                         real                                lambda,
122                         gmx::ArrayRef<const unsigned short> cFREEZE);
123     bool apply(bool                      bLog,
124                bool                      bEner,
125                int64_t                   step,
126                int                       delta_step,
127                real                      step_scaling,
128                ArrayRefWithPadding<RVec> x,
129                ArrayRefWithPadding<RVec> xprime,
130                ArrayRef<RVec>            min_proj,
131                const matrix              box,
132                real                      lambda,
133                real*                     dvdlambda,
134                ArrayRefWithPadding<RVec> v,
135                bool                      computeVirial,
136                tensor                    constraintsVirial,
137                ConstraintVariable        econq);
138     //! The total number of constraints.
139     int ncon_tot = 0;
140     //! The number of flexible constraints.
141     int nflexcon = 0;
142     //! A list of atoms to constraints for each moleculetype.
143     std::vector<ListOfLists<int>> at2con_mt;
144     //! A list of atoms to settles for each moleculetype
145     std::vector<std::vector<int>> at2settle_mt;
146     //! LINCS data.
147     Lincs* lincsd = nullptr; // TODO this should become a unique_ptr
148     //! SHAKE data.
149     std::unique_ptr<shakedata> shaked;
150     //! SETTLE data.
151     std::unique_ptr<SettleData> settled;
152     //! The maximum number of warnings.
153     int maxwarn = 0;
154     //! The number of warnings for LINCS.
155     int warncount_lincs = 0;
156     //! The number of warnings for SETTLE.
157     int warncount_settle = 0;
158     //! The essential dynamics data.
159     gmx_edsam* ed = nullptr;
160
161     //! Thread-local virial contribution.
162     tensor* threadConstraintsVirial = { nullptr };
163     //! Did a settle error occur?
164     bool* bSettleErrorHasOccurred = nullptr;
165
166     //! Pointer to the global topology - only used for printing warnings.
167     const gmx_mtop_t& mtop;
168     //! Parameters for the interactions in this domain.
169     const InteractionDefinitions* idef = nullptr;
170     //! Total number of atoms.
171     int numAtoms_ = 0;
172     //! Number of local atoms.
173     int numHomeAtoms_ = 0;
174     //! Atoms masses.
175     gmx::ArrayRef<const real> masses_;
176     //! Inverse masses.
177     gmx::ArrayRef<const real> inverseMasses_;
178     //! If there are atoms with perturbed mass (for FEP).
179     bool hasMassPerturbedAtoms_;
180     //! FEP lambda value.
181     real lambda_;
182     //! Freeze groups data
183     gmx::ArrayRef<const unsigned short> cFREEZE_;
184     //! Whether we need to do pbc for handling bonds.
185     bool pbcHandlingRequired_ = false;
186
187     //! Logging support.
188     FILE* log = nullptr;
189     //! Communication support.
190     const t_commrec* cr = nullptr;
191     //! Multi-sim support.
192     const gmx_multisim_t* ms = nullptr;
193     //! Pulling code object, if any.
194     pull_t* pull_work = nullptr;
195     /*!\brief Input options.
196      *
197      * \todo Replace with IMdpOptions */
198     const t_inputrec& ir;
199     //! Flop counting support.
200     t_nrnb* nrnb = nullptr;
201     //! Tracks wallcycle usage.
202     gmx_wallcycle* wcycle;
203 };
204
205 Constraints::~Constraints() = default;
206
207 int Constraints::numFlexibleConstraints() const
208 {
209     return impl_->nflexcon;
210 }
211
212 bool Constraints::havePerturbedConstraints() const
213 {
214     const gmx_ffparams_t& ffparams = impl_->mtop.ffparams;
215
216     for (size_t i = 0; i < ffparams.functype.size(); i++)
217     {
218         if ((ffparams.functype[i] == F_CONSTR || ffparams.functype[i] == F_CONSTRNC)
219             && ffparams.iparams[i].constr.dA != ffparams.iparams[i].constr.dB)
220         {
221             return true;
222         }
223     }
224
225     return false;
226 }
227
228 //! Clears constraint quantities for atoms in nonlocal region.
229 static void clear_constraint_quantity_nonlocal(const gmx_domdec_t& dd, ArrayRef<RVec> q)
230 {
231     int nonlocal_at_start, nonlocal_at_end;
232
233     dd_get_constraint_range(dd, &nonlocal_at_start, &nonlocal_at_end);
234
235     for (int at = nonlocal_at_start; at < nonlocal_at_end; at++)
236     {
237         clear_rvec(q[at]);
238     }
239 }
240
241 void too_many_constraint_warnings(ConstraintAlgorithm eConstrAlg, int warncount)
242 {
243     gmx_fatal(FARGS,
244               "Too many %s warnings (%d)\n"
245               "If you know what you are doing you can %s"
246               "set the environment variable GMX_MAXCONSTRWARN to -1,\n"
247               "but normally it is better to fix the problem",
248               (eConstrAlg == ConstraintAlgorithm::Lincs) ? "LINCS" : "SETTLE",
249               warncount,
250               (eConstrAlg == ConstraintAlgorithm::Lincs)
251                       ? "adjust the lincs warning threshold in your mdp file\nor "
252                       : "\n");
253 }
254
255 //! Writes out coordinates.
256 static void write_constr_pdb(const char*          fn,
257                              const char*          title,
258                              const gmx_mtop_t&    mtop,
259                              int                  start,
260                              int                  homenr,
261                              const t_commrec*     cr,
262                              ArrayRef<const RVec> x,
263                              const matrix         box)
264 {
265     char          fname[STRLEN];
266     FILE*         out;
267     int           dd_ac0 = 0, dd_ac1 = 0, i, ii, resnr;
268     gmx_domdec_t* dd;
269     const char *  anm, *resnm;
270
271     dd = nullptr;
272     if (DOMAINDECOMP(cr))
273     {
274         dd = cr->dd;
275         dd_get_constraint_range(*dd, &dd_ac0, &dd_ac1);
276         start  = 0;
277         homenr = dd_ac1;
278     }
279
280     if (PAR(cr))
281     {
282         sprintf(fname, "%s_n%d.pdb", fn, cr->sim_nodeid);
283     }
284     else
285     {
286         sprintf(fname, "%s.pdb", fn);
287     }
288
289     out = gmx_fio_fopen(fname, "w");
290
291     fprintf(out, "TITLE     %s\n", title);
292     gmx_write_pdb_box(out, PbcType::Unset, box);
293     int molb = 0;
294     for (i = start; i < start + homenr; i++)
295     {
296         if (dd != nullptr)
297         {
298             if (i >= dd_numHomeAtoms(*dd) && i < dd_ac0)
299             {
300                 continue;
301             }
302             ii = dd->globalAtomIndices[i];
303         }
304         else
305         {
306             ii = i;
307         }
308         mtopGetAtomAndResidueName(mtop, ii, &molb, &anm, &resnr, &resnm, nullptr);
309         gmx_fprintf_pdb_atomline(out,
310                                  PdbRecordType::Atom,
311                                  ii + 1,
312                                  anm,
313                                  ' ',
314                                  resnm,
315                                  ' ',
316                                  resnr,
317                                  ' ',
318                                  10 * x[i][XX],
319                                  10 * x[i][YY],
320                                  10 * x[i][ZZ],
321                                  1.0,
322                                  0.0,
323                                  "");
324     }
325     fprintf(out, "TER\n");
326
327     gmx_fio_fclose(out);
328 }
329
330 //! Writes out domain contents to help diagnose crashes.
331 static void dump_confs(FILE*                log,
332                        int64_t              step,
333                        const gmx_mtop_t&    mtop,
334                        int                  start,
335                        int                  homenr,
336                        const t_commrec*     cr,
337                        ArrayRef<const RVec> x,
338                        ArrayRef<const RVec> xprime,
339                        const matrix         box)
340 {
341     char buf[STRLEN], buf2[22];
342
343     char* env = getenv("GMX_SUPPRESS_DUMP");
344     if (env)
345     {
346         return;
347     }
348
349     sprintf(buf, "step%sb", gmx_step_str(step, buf2));
350     write_constr_pdb(buf, "initial coordinates", mtop, start, homenr, cr, x, box);
351     sprintf(buf, "step%sc", gmx_step_str(step, buf2));
352     write_constr_pdb(buf, "coordinates after constraining", mtop, start, homenr, cr, xprime, box);
353     if (log)
354     {
355         fprintf(log, "Wrote pdb files with previous and current coordinates\n");
356     }
357     fprintf(stderr, "Wrote pdb files with previous and current coordinates\n");
358 }
359
360 bool Constraints::apply(bool                      bLog,
361                         bool                      bEner,
362                         int64_t                   step,
363                         int                       delta_step,
364                         real                      step_scaling,
365                         ArrayRefWithPadding<RVec> x,
366                         ArrayRefWithPadding<RVec> xprime,
367                         ArrayRef<RVec>            min_proj,
368                         const matrix              box,
369                         real                      lambda,
370                         real*                     dvdlambda,
371                         ArrayRefWithPadding<RVec> v,
372                         bool                      computeVirial,
373                         tensor                    constraintsVirial,
374                         ConstraintVariable        econq)
375 {
376     return impl_->apply(bLog,
377                         bEner,
378                         step,
379                         delta_step,
380                         step_scaling,
381                         std::move(x),
382                         std::move(xprime),
383                         min_proj,
384                         box,
385                         lambda,
386                         dvdlambda,
387                         std::move(v),
388                         computeVirial,
389                         constraintsVirial,
390                         econq);
391 }
392
393 bool Constraints::Impl::apply(bool                      bLog,
394                               bool                      bEner,
395                               int64_t                   step,
396                               int                       delta_step,
397                               real                      step_scaling,
398                               ArrayRefWithPadding<RVec> x,
399                               ArrayRefWithPadding<RVec> xprime,
400                               ArrayRef<RVec>            min_proj,
401                               const matrix              box,
402                               real                      lambda,
403                               real*                     dvdlambda,
404                               ArrayRefWithPadding<RVec> v,
405                               bool                      computeVirial,
406                               tensor                    constraintsVirial,
407                               ConstraintVariable        econq)
408 {
409     bool  bOK, bDump;
410     int   start;
411     real  scaled_delta_t;
412     real  invdt, vir_fac = 0, t;
413     int   nsettle;
414     t_pbc pbc, *pbc_null;
415     char  buf[22];
416     int   nth;
417
418     wallcycle_start(wcycle, WallCycleCounter::Constr);
419
420     if (econq == ConstraintVariable::ForceDispl && !EI_ENERGY_MINIMIZATION(ir.eI))
421     {
422         gmx_incons(
423                 "constrain called for forces displacements while not doing energy minimization, "
424                 "can not do this while the LINCS and SETTLE constraint connection matrices are "
425                 "mass weighted");
426     }
427
428     bOK   = TRUE;
429     bDump = FALSE;
430
431     start = 0;
432
433     scaled_delta_t = step_scaling * ir.delta_t;
434
435     /* Prepare time step for use in constraint implementations, and
436        avoid generating inf when ir.delta_t = 0. */
437     if (ir.delta_t == 0)
438     {
439         invdt = 0.0;
440     }
441     else
442     {
443         invdt = 1.0 / scaled_delta_t;
444     }
445
446     if (ir.efep != FreeEnergyPerturbationType::No && EI_DYNAMICS(ir.eI))
447     {
448         /* Set the constraint lengths for the step at which this configuration
449          * is meant to be. The invmasses should not be changed.
450          */
451         lambda += delta_step * ir.fepvals->delta_lambda;
452     }
453
454     if (computeVirial)
455     {
456         clear_mat(constraintsVirial);
457     }
458     const InteractionList& settle = idef->il[F_SETTLE];
459     nsettle                       = settle.size() / (1 + NRAL(F_SETTLE));
460
461     if (nsettle > 0)
462     {
463         nth = gmx_omp_nthreads_get(ModuleMultiThread::Settle);
464     }
465     else
466     {
467         nth = 1;
468     }
469
470     /* We do not need full pbc when constraints do not cross update groups
471      * i.e. when dd->constraint_comm==NULL.
472      * Note that PBC for constraints is different from PBC for bondeds.
473      * For constraints there is both forward and backward communication.
474      */
475     if (ir.pbcType != PbcType::No && (cr->dd || pbcHandlingRequired_)
476         && !(cr->dd && cr->dd->constraint_comm == nullptr))
477     {
478         /* With pbc=screw the screw has been changed to a shift
479          * by the constraint coordinate communication routine,
480          * so that here we can use normal pbc.
481          */
482         pbc_null = set_pbc_dd(&pbc, ir.pbcType, DOMAINDECOMP(cr) ? cr->dd->numCells : nullptr, FALSE, box);
483     }
484     else
485     {
486         pbc_null = nullptr;
487     }
488
489     /* Communicate the coordinates required for the non-local constraints
490      * for LINCS and/or SETTLE.
491      */
492     if (cr->dd)
493     {
494         dd_move_x_constraints(cr->dd,
495                               box,
496                               x.unpaddedArrayRef(),
497                               xprime.unpaddedArrayRef(),
498                               econq == ConstraintVariable::Positions);
499
500         if (!v.empty())
501         {
502             /* We need to initialize the non-local components of v.
503              * We never actually use these values, but we do increment them,
504              * so we should avoid uninitialized variables and overflows.
505              */
506             clear_constraint_quantity_nonlocal(*cr->dd, v.unpaddedArrayRef());
507         }
508     }
509
510     if (lincsd != nullptr)
511     {
512         bOK = constrain_lincs(bLog || bEner,
513                               ir,
514                               step,
515                               lincsd,
516                               inverseMasses_,
517                               cr,
518                               ms,
519                               x,
520                               xprime,
521                               min_proj,
522                               box,
523                               pbc_null,
524                               hasMassPerturbedAtoms_,
525                               lambda,
526                               dvdlambda,
527                               invdt,
528                               v.unpaddedArrayRef(),
529                               computeVirial,
530                               constraintsVirial,
531                               econq,
532                               nrnb,
533                               maxwarn,
534                               &warncount_lincs);
535         if (!bOK && maxwarn < INT_MAX)
536         {
537             if (log != nullptr)
538             {
539                 fprintf(log,
540                         "Constraint error in algorithm %s at step %s\n",
541                         enumValueToString(ConstraintAlgorithm::Lincs),
542                         gmx_step_str(step, buf));
543             }
544             bDump = TRUE;
545         }
546     }
547
548     if (shaked != nullptr)
549     {
550         bOK = constrain_shake(log,
551                               shaked.get(),
552                               inverseMasses_,
553                               *idef,
554                               ir,
555                               x.unpaddedArrayRef(),
556                               xprime.unpaddedArrayRef(),
557                               min_proj,
558                               pbc_null,
559                               nrnb,
560                               lambda,
561                               dvdlambda,
562                               invdt,
563                               v.unpaddedArrayRef(),
564                               computeVirial,
565                               constraintsVirial,
566                               maxwarn < INT_MAX,
567                               econq);
568
569         if (!bOK && maxwarn < INT_MAX)
570         {
571             if (log != nullptr)
572             {
573                 fprintf(log,
574                         "Constraint error in algorithm %s at step %s\n",
575                         enumValueToString(ConstraintAlgorithm::Shake),
576                         gmx_step_str(step, buf));
577             }
578             bDump = TRUE;
579         }
580     }
581
582     if (nsettle > 0)
583     {
584         bool bSettleErrorHasOccurred0 = false;
585
586         switch (econq)
587         {
588             case ConstraintVariable::Positions:
589 #pragma omp parallel for num_threads(nth) schedule(static)
590                 for (int th = 0; th < nth; th++)
591                 {
592                     try
593                     {
594                         if (th > 0)
595                         {
596                             clear_mat(threadConstraintsVirial[th]);
597                         }
598
599                         csettle(*settled,
600                                 nth,
601                                 th,
602                                 pbc_null,
603                                 x,
604                                 xprime,
605                                 invdt,
606                                 v,
607                                 computeVirial,
608                                 th == 0 ? constraintsVirial : threadConstraintsVirial[th],
609                                 th == 0 ? &bSettleErrorHasOccurred0 : &bSettleErrorHasOccurred[th]);
610                     }
611                     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
612                 }
613                 inc_nrnb(nrnb, eNR_SETTLE, nsettle);
614                 if (!v.empty())
615                 {
616                     inc_nrnb(nrnb, eNR_CONSTR_V, nsettle * 3);
617                 }
618                 if (computeVirial)
619                 {
620                     inc_nrnb(nrnb, eNR_CONSTR_VIR, nsettle * 3);
621                 }
622                 break;
623             case ConstraintVariable::Velocities:
624             case ConstraintVariable::Derivative:
625             case ConstraintVariable::Force:
626             case ConstraintVariable::ForceDispl:
627 #pragma omp parallel for num_threads(nth) schedule(static)
628                 for (int th = 0; th < nth; th++)
629                 {
630                     try
631                     {
632                         int calcvir_atom_end;
633
634                         if (!computeVirial)
635                         {
636                             calcvir_atom_end = 0;
637                         }
638                         else
639                         {
640                             calcvir_atom_end = numHomeAtoms_;
641                         }
642
643                         if (th > 0)
644                         {
645                             clear_mat(threadConstraintsVirial[th]);
646                         }
647
648                         int start_th = (nsettle * th) / nth;
649                         int end_th   = (nsettle * (th + 1)) / nth;
650
651                         if (start_th >= 0 && end_th - start_th > 0)
652                         {
653                             settle_proj(*settled,
654                                         econq,
655                                         end_th - start_th,
656                                         settle.iatoms.data() + start_th * (1 + NRAL(F_SETTLE)),
657                                         pbc_null,
658                                         x.unpaddedArrayRef(),
659                                         xprime.unpaddedArrayRef(),
660                                         min_proj,
661                                         calcvir_atom_end,
662                                         th == 0 ? constraintsVirial : threadConstraintsVirial[th]);
663                         }
664                     }
665                     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
666                 }
667                 /* This is an overestimate */
668                 inc_nrnb(nrnb, eNR_SETTLE, nsettle);
669                 break;
670             case ConstraintVariable::Deriv_FlexCon:
671                 /* Nothing to do, since the are no flexible constraints in settles */
672                 break;
673             default: gmx_incons("Unknown constraint quantity for settle");
674         }
675
676         if (computeVirial)
677         {
678             /* Reduce the virial contributions over the threads */
679             for (int th = 1; th < nth; th++)
680             {
681                 m_add(constraintsVirial, threadConstraintsVirial[th], constraintsVirial);
682             }
683         }
684
685         if (econq == ConstraintVariable::Positions)
686         {
687             for (int th = 1; th < nth; th++)
688             {
689                 bSettleErrorHasOccurred0 = bSettleErrorHasOccurred0 || bSettleErrorHasOccurred[th];
690             }
691
692             if (bSettleErrorHasOccurred0)
693             {
694                 char buf[STRLEN];
695                 sprintf(buf,
696                         "\nstep "
697                         "%" PRId64
698                         ": One or more water molecules can not be settled.\n"
699                         "Check for bad contacts and/or reduce the timestep if appropriate.\n",
700                         step);
701                 if (log)
702                 {
703                     fprintf(log, "%s", buf);
704                 }
705                 fprintf(stderr, "%s", buf);
706                 warncount_settle++;
707                 if (warncount_settle > maxwarn)
708                 {
709                     too_many_constraint_warnings(ConstraintAlgorithm::Count, warncount_settle);
710                 }
711                 bDump = TRUE;
712
713                 bOK = FALSE;
714             }
715         }
716     }
717
718     if (computeVirial)
719     {
720         /* The normal uses of constrain() pass step_scaling = 1.0.
721          * The call to constrain() for SD1 that passes step_scaling =
722          * 0.5 also passes vir = NULL, so cannot reach this
723          * assertion. This assertion should remain until someone knows
724          * that this path works for their intended purpose, and then
725          * they can use scaled_delta_t instead of ir.delta_t
726          * below. */
727         assert(gmx_within_tol(step_scaling, 1.0, GMX_REAL_EPS));
728         switch (econq)
729         {
730             case ConstraintVariable::Positions: vir_fac = 0.5 / (ir.delta_t * ir.delta_t); break;
731             case ConstraintVariable::Velocities: vir_fac = 0.5 / ir.delta_t; break;
732             case ConstraintVariable::Force:
733             case ConstraintVariable::ForceDispl: vir_fac = 0.5; break;
734             default: gmx_incons("Unsupported constraint quantity for virial");
735         }
736
737         if (EI_VV(ir.eI))
738         {
739             vir_fac *= 2; /* only constraining over half the distance here */
740         }
741         for (int i = 0; i < DIM; i++)
742         {
743             for (int j = 0; j < DIM; j++)
744             {
745                 constraintsVirial[i][j] *= vir_fac;
746             }
747         }
748     }
749
750     if (bDump)
751     {
752         dump_confs(log, step, mtop, start, numHomeAtoms_, cr, x.unpaddedArrayRef(), xprime.unpaddedArrayRef(), box);
753     }
754
755     if (econq == ConstraintVariable::Positions)
756     {
757         if (ir.bPull && pull_have_constraint(*pull_work))
758         {
759             if (EI_DYNAMICS(ir.eI))
760             {
761                 t = ir.init_t + (step + delta_step) * ir.delta_t;
762             }
763             else
764             {
765                 t = ir.init_t;
766             }
767             set_pbc(&pbc, ir.pbcType, box);
768             pull_constraint(pull_work,
769                             masses_,
770                             pbc,
771                             cr,
772                             ir.delta_t,
773                             t,
774                             x.unpaddedArrayRef(),
775                             xprime.unpaddedArrayRef(),
776                             v.unpaddedArrayRef(),
777                             constraintsVirial);
778         }
779         if (ed && delta_step > 0)
780         {
781             /* apply the essential dynamics constraints here */
782             do_edsam(&ir, step, cr, xprime.unpaddedArrayRef(), v.unpaddedArrayRef(), box, ed);
783         }
784     }
785     wallcycle_stop(wcycle, WallCycleCounter::Constr);
786
787     const bool haveVelocities = (!v.empty() || econq == ConstraintVariable::Velocities);
788     if (haveVelocities && !cFREEZE_.empty())
789     {
790         /* Set the velocities of frozen dimensions to zero */
791         ArrayRef<RVec> vRef;
792         if (econq == ConstraintVariable::Velocities)
793         {
794             vRef = xprime.unpaddedArrayRef();
795         }
796         else
797         {
798             vRef = v.unpaddedArrayRef();
799         }
800
801         int gmx_unused numThreads = gmx_omp_nthreads_get(ModuleMultiThread::Update);
802
803 #pragma omp parallel for num_threads(numThreads) schedule(static)
804         for (int i = 0; i < numHomeAtoms_; i++)
805         {
806             int freezeGroup = cFREEZE_[i];
807
808             for (int d = 0; d < DIM; d++)
809             {
810                 if (ir.opts.nFreeze[freezeGroup][d])
811                 {
812                     vRef[i][d] = 0;
813                 }
814             }
815         }
816     }
817
818     return bOK;
819 } // namespace gmx
820
821 ArrayRef<real> Constraints::rmsdData() const
822 {
823     if (impl_->lincsd)
824     {
825         return lincs_rmsdData(impl_->lincsd);
826     }
827     else
828     {
829         return {};
830     }
831 }
832
833 real Constraints::rmsd() const
834 {
835     if (impl_->lincsd)
836     {
837         return lincs_rmsd(impl_->lincsd);
838     }
839     else
840     {
841         return 0;
842     }
843 }
844
845 int Constraints::numConstraintsTotal()
846 {
847     return impl_->ncon_tot;
848 }
849
850 FlexibleConstraintTreatment flexibleConstraintTreatment(bool haveDynamicsIntegrator)
851 {
852     if (haveDynamicsIntegrator)
853     {
854         return FlexibleConstraintTreatment::Include;
855     }
856     else
857     {
858         return FlexibleConstraintTreatment::Exclude;
859     }
860 }
861
862 /*! \brief Returns a block struct to go from atoms to constraints
863  *
864  * The block struct will contain constraint indices with lower indices
865  * directly matching the order in F_CONSTR and higher indices matching
866  * the order in F_CONSTRNC offset by the number of constraints in F_CONSTR.
867  *
868  * \param[in]  numAtoms  The number of atoms to construct the list for
869  * \param[in]  ilists    The interaction lists, size F_NRE
870  * \param[in]  iparams   Interaction parameters, can be null when
871  *                       \p flexibleConstraintTreatment==Include
872  * \param[in]  flexibleConstraintTreatment  The flexible constraint treatment,
873  *                                          see enum above
874  *
875  * \returns a block struct with all constraints for each atom
876  */
877 static ListOfLists<int> makeAtomsToConstraintsList(int                             numAtoms,
878                                                    ArrayRef<const InteractionList> ilists,
879                                                    ArrayRef<const t_iparams>       iparams,
880                                                    FlexibleConstraintTreatment flexibleConstraintTreatment)
881 {
882     GMX_ASSERT(flexibleConstraintTreatment == FlexibleConstraintTreatment::Include || !iparams.empty(),
883                "With flexible constraint detection we need valid iparams");
884
885     std::vector<int> count(numAtoms);
886
887     for (int ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
888     {
889         const InteractionList& ilist  = ilists[ftype];
890         const int              stride = 1 + NRAL(ftype);
891         for (int i = 0; i < ilist.size(); i += stride)
892         {
893             if (flexibleConstraintTreatment == FlexibleConstraintTreatment::Include
894                 || !isConstraintFlexible(iparams, ilist.iatoms[i]))
895             {
896                 for (int j = 1; j < 3; j++)
897                 {
898                     int a = ilist.iatoms[i + j];
899                     count[a]++;
900                 }
901             }
902         }
903     }
904
905     std::vector<int> listRanges(numAtoms + 1);
906     for (int a = 0; a < numAtoms; a++)
907     {
908         listRanges[a + 1] = listRanges[a] + count[a];
909         count[a]          = 0;
910     }
911     std::vector<int> elements(listRanges[numAtoms]);
912
913     /* The F_CONSTRNC constraints have constraint numbers
914      * that continue after the last F_CONSTR constraint.
915      */
916     int numConstraints = 0;
917     for (int ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
918     {
919         const InteractionList& ilist  = ilists[ftype];
920         const int              stride = 1 + NRAL(ftype);
921         for (int i = 0; i < ilist.size(); i += stride)
922         {
923             if (flexibleConstraintTreatment == FlexibleConstraintTreatment::Include
924                 || !isConstraintFlexible(iparams, ilist.iatoms[i]))
925             {
926                 for (int j = 1; j < 3; j++)
927                 {
928                     const int a                          = ilist.iatoms[i + j];
929                     elements[listRanges[a] + count[a]++] = numConstraints;
930                 }
931             }
932             numConstraints++;
933         }
934     }
935
936     return ListOfLists<int>(std::move(listRanges), std::move(elements));
937 }
938
939 ListOfLists<int> make_at2con(int                             numAtoms,
940                              ArrayRef<const InteractionList> ilist,
941                              ArrayRef<const t_iparams>       iparams,
942                              FlexibleConstraintTreatment     flexibleConstraintTreatment)
943 {
944     return makeAtomsToConstraintsList(numAtoms, ilist, iparams, flexibleConstraintTreatment);
945 }
946
947 ListOfLists<int> make_at2con(const gmx_moltype_t&           moltype,
948                              gmx::ArrayRef<const t_iparams> iparams,
949                              FlexibleConstraintTreatment    flexibleConstraintTreatment)
950 {
951     return makeAtomsToConstraintsList(
952             moltype.atoms.nr, makeConstArrayRef(moltype.ilist), iparams, flexibleConstraintTreatment);
953 }
954
955 //! Return the number of flexible constraints in the \c ilist and \c iparams.
956 int countFlexibleConstraints(ArrayRef<const InteractionList> ilist, ArrayRef<const t_iparams> iparams)
957 {
958     int nflexcon = 0;
959     for (int ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
960     {
961         const int numIatomsPerConstraint = 3;
962         for (int i = 0; i < ilist[ftype].size(); i += numIatomsPerConstraint)
963         {
964             const int type = ilist[ftype].iatoms[i];
965             if (iparams[type].constr.dA == 0 && iparams[type].constr.dB == 0)
966             {
967                 nflexcon++;
968             }
969         }
970     }
971
972     return nflexcon;
973 }
974
975 //! Returns the index of the settle to which each atom belongs.
976 static std::vector<int> make_at2settle(int natoms, const InteractionList& ilist)
977 {
978     /* Set all to no settle */
979     std::vector<int> at2s(natoms, -1);
980
981     const int stride = 1 + NRAL(F_SETTLE);
982
983     for (int s = 0; s < ilist.size(); s += stride)
984     {
985         at2s[ilist.iatoms[s + 1]] = s / stride;
986         at2s[ilist.iatoms[s + 2]] = s / stride;
987         at2s[ilist.iatoms[s + 3]] = s / stride;
988     }
989
990     return at2s;
991 }
992
993 void Constraints::Impl::setConstraints(gmx_localtop_t*                     top,
994                                        int                                 numAtoms,
995                                        int                                 numHomeAtoms,
996                                        gmx::ArrayRef<const real>           masses,
997                                        gmx::ArrayRef<const real>           inverseMasses,
998                                        const bool                          hasMassPerturbedAtoms,
999                                        const real                          lambda,
1000                                        gmx::ArrayRef<const unsigned short> cFREEZE)
1001 {
1002     numAtoms_              = numAtoms;
1003     numHomeAtoms_          = numHomeAtoms;
1004     masses_                = masses;
1005     inverseMasses_         = inverseMasses;
1006     hasMassPerturbedAtoms_ = hasMassPerturbedAtoms;
1007     lambda_                = lambda;
1008     cFREEZE_               = cFREEZE;
1009
1010     idef = &top->idef;
1011
1012     if (ncon_tot > 0)
1013     {
1014         /* With DD we might also need to call LINCS on a domain no constraints for
1015          * communicating coordinates to other nodes that do have constraints.
1016          */
1017         if (ir.eConstrAlg == ConstraintAlgorithm::Lincs)
1018         {
1019             set_lincs(*idef, numAtoms_, inverseMasses_, lambda_, EI_DYNAMICS(ir.eI), cr, lincsd);
1020         }
1021         if (ir.eConstrAlg == ConstraintAlgorithm::Shake)
1022         {
1023             if (cr->dd)
1024             {
1025                 // We are using the local topology, so there are only
1026                 // F_CONSTR constraints.
1027                 GMX_RELEASE_ASSERT(idef->il[F_CONSTRNC].empty(),
1028                                    "Here we should not have no-connect constraints");
1029                 make_shake_sblock_dd(shaked.get(), idef->il[F_CONSTR]);
1030             }
1031             else
1032             {
1033                 make_shake_sblock_serial(shaked.get(), &top->idef, numAtoms_);
1034             }
1035         }
1036     }
1037
1038     if (settled)
1039     {
1040         settled->setConstraints(idef->il[F_SETTLE], numHomeAtoms_, masses_, inverseMasses_);
1041     }
1042
1043     /* Make a selection of the local atoms for essential dynamics */
1044     if (ed && cr->dd)
1045     {
1046         dd_make_local_ed_indices(cr->dd, ed);
1047     }
1048 }
1049
1050 void Constraints::setConstraints(gmx_localtop_t*                     top,
1051                                  const int                           numAtoms,
1052                                  const int                           numHomeAtoms,
1053                                  gmx::ArrayRef<const real>           masses,
1054                                  gmx::ArrayRef<const real>           inverseMasses,
1055                                  const bool                          hasMassPerturbedAtoms,
1056                                  const real                          lambda,
1057                                  gmx::ArrayRef<const unsigned short> cFREEZE)
1058 {
1059     impl_->setConstraints(
1060             top, numAtoms, numHomeAtoms, masses, inverseMasses, hasMassPerturbedAtoms, lambda, cFREEZE);
1061 }
1062
1063 /*! \brief Makes a per-moleculetype container of mappings from atom
1064  * indices to constraint indices.
1065  *
1066  * Note that flexible constraints are only enabled with a dynamical integrator. */
1067 static std::vector<ListOfLists<int>> makeAtomToConstraintMappings(const gmx_mtop_t& mtop,
1068                                                                   FlexibleConstraintTreatment flexibleConstraintTreatment)
1069 {
1070     std::vector<ListOfLists<int>> mapping;
1071     mapping.reserve(mtop.moltype.size());
1072     for (const gmx_moltype_t& moltype : mtop.moltype)
1073     {
1074         mapping.push_back(make_at2con(moltype, mtop.ffparams.iparams, flexibleConstraintTreatment));
1075     }
1076     return mapping;
1077 }
1078
1079 Constraints::Constraints(const gmx_mtop_t&     mtop,
1080                          const t_inputrec&     ir,
1081                          pull_t*               pull_work,
1082                          FILE*                 log,
1083                          const t_commrec*      cr,
1084                          const bool            useUpdateGroups,
1085                          const gmx_multisim_t* ms,
1086                          t_nrnb*               nrnb,
1087                          gmx_wallcycle*        wcycle,
1088                          bool                  pbcHandlingRequired,
1089                          int                   numConstraints,
1090                          int                   numSettles) :
1091     impl_(new Impl(mtop, ir, pull_work, log, cr, useUpdateGroups, ms, nrnb, wcycle, pbcHandlingRequired, numConstraints, numSettles))
1092 {
1093 }
1094
1095 Constraints::Impl::Impl(const gmx_mtop_t&     mtop_p,
1096                         const t_inputrec&     ir_p,
1097                         pull_t*               pull_work,
1098                         FILE*                 log_p,
1099                         const t_commrec*      cr_p,
1100                         const bool            useUpdateGroups,
1101                         const gmx_multisim_t* ms_p,
1102                         t_nrnb*               nrnb_p,
1103                         gmx_wallcycle*        wcycle_p,
1104                         bool                  pbcHandlingRequired,
1105                         int                   numConstraints,
1106                         int                   numSettles) :
1107     ncon_tot(numConstraints),
1108     mtop(mtop_p),
1109     pbcHandlingRequired_(pbcHandlingRequired),
1110     log(log_p),
1111     cr(cr_p),
1112     ms(ms_p),
1113     pull_work(pull_work),
1114     ir(ir_p),
1115     nrnb(nrnb_p),
1116     wcycle(wcycle_p)
1117 {
1118     if (numConstraints + numSettles > 0 && ir.epc == PressureCoupling::Mttk)
1119     {
1120         gmx_fatal(FARGS, "Constraints are not implemented with MTTK pressure control.");
1121     }
1122
1123     nflexcon = 0;
1124     if (numConstraints > 0)
1125     {
1126         at2con_mt = makeAtomToConstraintMappings(mtop, flexibleConstraintTreatment(EI_DYNAMICS(ir.eI)));
1127
1128         for (const gmx_molblock_t& molblock : mtop.molblock)
1129         {
1130             int count = countFlexibleConstraints(mtop.moltype[molblock.type].ilist, mtop.ffparams.iparams);
1131             nflexcon += molblock.nmol * count;
1132         }
1133
1134         if (nflexcon > 0)
1135         {
1136             if (log)
1137             {
1138                 fprintf(log, "There are %d flexible constraints\n", nflexcon);
1139                 if (ir.fc_stepsize == 0)
1140                 {
1141                     fprintf(log,
1142                             "\n"
1143                             "WARNING: step size for flexible constraining = 0\n"
1144                             "         All flexible constraints will be rigid.\n"
1145                             "         Will try to keep all flexible constraints at their original "
1146                             "length,\n"
1147                             "         but the lengths may exhibit some drift.\n\n");
1148                     nflexcon = 0;
1149                 }
1150             }
1151             if (nflexcon > 0)
1152             {
1153                 please_cite(log, "Hess2002");
1154             }
1155         }
1156
1157         // When there are multiple PP domains and update groups are
1158         // not in use, the constraints might be split across the
1159         // domains, needing particular handling.
1160         const bool mayHaveSplitConstraints = DOMAINDECOMP(cr) && !useUpdateGroups;
1161
1162         if (ir.eConstrAlg == ConstraintAlgorithm::Lincs)
1163         {
1164             lincsd = init_lincs(
1165                     log, mtop, nflexcon, at2con_mt, mayHaveSplitConstraints, ir.nLincsIter, ir.nProjOrder);
1166         }
1167
1168         if (ir.eConstrAlg == ConstraintAlgorithm::Shake)
1169         {
1170             if (mayHaveSplitConstraints)
1171             {
1172                 gmx_fatal(FARGS,
1173                           "SHAKE is not supported with domain decomposition and constraints that "
1174                           "cross domain boundaries, use LINCS");
1175             }
1176             if (nflexcon)
1177             {
1178                 gmx_fatal(FARGS,
1179                           "For this system also velocities and/or forces need to be constrained, "
1180                           "this can not be done with SHAKE, you should select LINCS");
1181             }
1182             please_cite(log, "Ryckaert77a");
1183             if (ir.bShakeSOR)
1184             {
1185                 please_cite(log, "Barth95a");
1186             }
1187
1188             shaked = std::make_unique<shakedata>();
1189         }
1190     }
1191
1192     if (numSettles > 0)
1193     {
1194         please_cite(log, "Miyamoto92a");
1195
1196         settled = std::make_unique<SettleData>(mtop);
1197
1198         // SETTLE with perturbed masses is not implemented. grompp now checks
1199         // for this, but old .tpr files that did this might still exist.
1200         if (haveFepPerturbedMassesInSettles(mtop))
1201         {
1202             gmx_fatal(FARGS,
1203                       "SETTLE is not implemented for atoms whose mass is perturbed. "
1204                       "You might\ninstead use normal constraints.");
1205         }
1206
1207         /* Make an atom to settle index for use in domain decomposition */
1208         for (size_t mt = 0; mt < mtop.moltype.size(); mt++)
1209         {
1210             at2settle_mt.emplace_back(
1211                     make_at2settle(mtop.moltype[mt].atoms.nr, mtop.moltype[mt].ilist[F_SETTLE]));
1212         }
1213
1214         /* Allocate thread-local work arrays */
1215         int nthreads = gmx_omp_nthreads_get(ModuleMultiThread::Settle);
1216         if (nthreads > 1 && threadConstraintsVirial == nullptr)
1217         {
1218             snew(threadConstraintsVirial, nthreads);
1219             snew(bSettleErrorHasOccurred, nthreads);
1220         }
1221     }
1222
1223     maxwarn   = 999;
1224     char* env = getenv("GMX_MAXCONSTRWARN");
1225     if (env)
1226     {
1227         maxwarn = 0;
1228         sscanf(env, "%8d", &maxwarn);
1229         if (maxwarn < 0)
1230         {
1231             maxwarn = INT_MAX;
1232         }
1233         if (log)
1234         {
1235             fprintf(log, "Setting the maximum number of constraint warnings to %d\n", maxwarn);
1236         }
1237         if (MASTER(cr))
1238         {
1239             fprintf(stderr, "Setting the maximum number of constraint warnings to %d\n", maxwarn);
1240         }
1241     }
1242     warncount_lincs  = 0;
1243     warncount_settle = 0;
1244 }
1245
1246 Constraints::Impl::~Impl()
1247 {
1248     if (bSettleErrorHasOccurred != nullptr)
1249     {
1250         sfree(bSettleErrorHasOccurred);
1251     }
1252     if (threadConstraintsVirial != nullptr)
1253     {
1254         sfree(threadConstraintsVirial);
1255     }
1256     done_lincs(lincsd);
1257 }
1258
1259 void Constraints::saveEdsamPointer(gmx_edsam* ed)
1260 {
1261     impl_->ed = ed;
1262 }
1263
1264 ArrayRef<const ListOfLists<int>> Constraints::atom2constraints_moltype() const
1265 {
1266     return impl_->at2con_mt;
1267 }
1268
1269 ArrayRef<const std::vector<int>> Constraints::atom2settle_moltype() const
1270 {
1271     return impl_->at2settle_mt;
1272 }
1273
1274 void do_constrain_first(FILE*                     fplog,
1275                         gmx::Constraints*         constr,
1276                         const t_inputrec*         ir,
1277                         int                       numAtoms,
1278                         int                       numHomeAtoms,
1279                         ArrayRefWithPadding<RVec> x,
1280                         ArrayRefWithPadding<RVec> v,
1281                         const matrix              box,
1282                         real                      lambda)
1283 {
1284     int     i, m, start, end;
1285     int64_t step;
1286     real    dt = ir->delta_t;
1287     real    dvdl_dum;
1288
1289     PaddedVector<RVec> savex(numAtoms);
1290
1291     start = 0;
1292     end   = numHomeAtoms;
1293
1294     if (debug)
1295     {
1296         fprintf(debug, "vcm: start=%d, homenr=%d, end=%d\n", start, numHomeAtoms, end);
1297     }
1298     /* Do a first constrain to reset particles... */
1299     step = ir->init_step;
1300     if (fplog)
1301     {
1302         char buf[STEPSTRSIZE];
1303         fprintf(fplog, "\nConstraining the starting coordinates (step %s)\n", gmx_step_str(step, buf));
1304     }
1305     dvdl_dum = 0;
1306
1307     bool needsLogging  = true;
1308     bool computeEnergy = false;
1309     bool computeVirial = false;
1310     /* constrain the current position */
1311     constr->apply(needsLogging,
1312                   computeEnergy,
1313                   step,
1314                   0,
1315                   1.0,
1316                   x,
1317                   x,
1318                   {},
1319                   box,
1320                   lambda,
1321                   &dvdl_dum,
1322                   {},
1323                   computeVirial,
1324                   nullptr,
1325                   gmx::ConstraintVariable::Positions);
1326     if (EI_VV(ir->eI))
1327     {
1328         /* constrain the inital velocity, and save it */
1329         /* also may be useful if we need the ekin from the halfstep for velocity verlet */
1330         constr->apply(needsLogging,
1331                       computeEnergy,
1332                       step,
1333                       0,
1334                       1.0,
1335                       x,
1336                       v,
1337                       v.unpaddedArrayRef(),
1338                       box,
1339                       lambda,
1340                       &dvdl_dum,
1341                       {},
1342                       computeVirial,
1343                       nullptr,
1344                       gmx::ConstraintVariable::Velocities);
1345     }
1346     /* constrain the inital velocities at t-dt/2 */
1347     if (EI_STATE_VELOCITY(ir->eI) && ir->eI != IntegrationAlgorithm::VV)
1348     {
1349         auto subX = x.paddedArrayRef().subArray(start, end);
1350         auto subV = v.paddedArrayRef().subArray(start, end);
1351         for (i = start; (i < end); i++)
1352         {
1353             for (m = 0; (m < DIM); m++)
1354             {
1355                 /* Reverse the velocity */
1356                 subV[i][m] = -subV[i][m];
1357                 /* Store the position at t-dt in buf */
1358                 savex[i][m] = subX[i][m] + dt * subV[i][m];
1359             }
1360         }
1361         /* Shake the positions at t=-dt with the positions at t=0
1362          * as reference coordinates.
1363          */
1364         if (fplog)
1365         {
1366             char buf[STEPSTRSIZE];
1367             fprintf(fplog, "\nConstraining the coordinates at t0-dt (step %s)\n", gmx_step_str(step, buf));
1368         }
1369         dvdl_dum = 0;
1370         constr->apply(needsLogging,
1371                       computeEnergy,
1372                       step,
1373                       -1,
1374                       1.0,
1375                       x,
1376                       savex.arrayRefWithPadding(),
1377                       {},
1378                       box,
1379                       lambda,
1380                       &dvdl_dum,
1381                       v,
1382                       computeVirial,
1383                       nullptr,
1384                       gmx::ConstraintVariable::Positions);
1385
1386         for (i = start; i < end; i++)
1387         {
1388             for (m = 0; m < DIM; m++)
1389             {
1390                 /* Re-reverse the velocities */
1391                 subV[i][m] = -subV[i][m];
1392             }
1393         }
1394     }
1395 }
1396
1397 void constrain_velocities(gmx::Constraints* constr,
1398                           bool              do_log,
1399                           bool              do_ene,
1400                           int64_t           step,
1401                           t_state*          state,
1402                           real*             dvdlambda,
1403                           gmx_bool          computeVirial,
1404                           tensor            constraintsVirial)
1405 {
1406     if (constr != nullptr)
1407     {
1408         constr->apply(do_log,
1409                       do_ene,
1410                       step,
1411                       1,
1412                       1.0,
1413                       state->x.arrayRefWithPadding(),
1414                       state->v.arrayRefWithPadding(),
1415                       state->v.arrayRefWithPadding().unpaddedArrayRef(),
1416                       state->box,
1417                       state->lambda[FreeEnergyPerturbationCouplingType::Bonded],
1418                       dvdlambda,
1419                       ArrayRefWithPadding<RVec>(),
1420                       computeVirial,
1421                       constraintsVirial,
1422                       ConstraintVariable::Velocities);
1423     }
1424 }
1425
1426 void constrain_coordinates(gmx::Constraints*         constr,
1427                            bool                      do_log,
1428                            bool                      do_ene,
1429                            int64_t                   step,
1430                            t_state*                  state,
1431                            ArrayRefWithPadding<RVec> xp,
1432                            real*                     dvdlambda,
1433                            gmx_bool                  computeVirial,
1434                            tensor                    constraintsVirial)
1435 {
1436     if (constr != nullptr)
1437     {
1438         constr->apply(do_log,
1439                       do_ene,
1440                       step,
1441                       1,
1442                       1.0,
1443                       state->x.arrayRefWithPadding(),
1444                       std::move(xp),
1445                       ArrayRef<RVec>(),
1446                       state->box,
1447                       state->lambda[FreeEnergyPerturbationCouplingType::Bonded],
1448                       dvdlambda,
1449                       state->v.arrayRefWithPadding(),
1450                       computeVirial,
1451                       constraintsVirial,
1452                       ConstraintVariable::Positions);
1453     }
1454 }
1455
1456 } // namespace gmx