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