2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2014,2015,2016,2017, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * \brief This file defines low-level functions necessary for
38 * computing energies and forces for position restraints.
40 * \author Mark Abraham <mark.j.abraham@gmail.com>
42 * \ingroup module_listed-forces
47 #include "position-restraints.h"
53 #include "gromacs/gmxlib/nrnb.h"
54 #include "gromacs/math/functions.h"
55 #include "gromacs/math/vec.h"
56 #include "gromacs/mdtypes/forceoutput.h"
57 #include "gromacs/mdtypes/forcerec.h"
58 #include "gromacs/mdtypes/inputrec.h"
59 #include "gromacs/mdtypes/md_enums.h"
60 #include "gromacs/pbcutil/pbc.h"
61 #include "gromacs/timing/wallcycle.h"
62 #include "gromacs/topology/idef.h"
63 #include "gromacs/utility/basedefinitions.h"
64 #include "gromacs/utility/fatalerror.h"
71 /*! \brief returns dx, rdist, and dpdl for functions posres() and fbposres()
73 void posres_dx(const rvec x
, const rvec pos0A
, const rvec pos0B
,
74 const rvec comA_sc
, const rvec comB_sc
,
76 const t_pbc
*pbc
, int refcoord_scaling
, int npbcdim
,
77 rvec dx
, rvec rdist
, rvec dpdl
)
80 real posA
, posB
, L1
, ref
= 0.;
85 for (m
= 0; m
< DIM
; m
++)
91 switch (refcoord_scaling
)
95 rdist
[m
] = L1
*posA
+ lambda
*posB
;
96 dpdl
[m
] = posB
- posA
;
99 /* Box relative coordinates are stored for dimensions with pbc */
100 posA
*= pbc
->box
[m
][m
];
101 posB
*= pbc
->box
[m
][m
];
102 assert(npbcdim
<= DIM
);
103 for (d
= m
+1; d
< npbcdim
; d
++)
105 posA
+= pos0A
[d
]*pbc
->box
[d
][m
];
106 posB
+= pos0B
[d
]*pbc
->box
[d
][m
];
108 ref
= L1
*posA
+ lambda
*posB
;
110 dpdl
[m
] = posB
- posA
;
113 ref
= L1
*comA_sc
[m
] + lambda
*comB_sc
[m
];
114 rdist
[m
] = L1
*posA
+ lambda
*posB
;
115 dpdl
[m
] = comB_sc
[m
] - comA_sc
[m
] + posB
- posA
;
118 gmx_fatal(FARGS
, "No such scaling method implemented");
123 ref
= L1
*posA
+ lambda
*posB
;
125 dpdl
[m
] = posB
- posA
;
128 /* We do pbc_dx with ref+rdist,
129 * since with only ref we can be up to half a box vector wrong.
131 pos
[m
] = ref
+ rdist
[m
];
136 pbc_dx(pbc
, x
, pos
, dx
);
140 rvec_sub(x
, pos
, dx
);
144 /*! \brief Computes forces and potential for flat-bottom cylindrical restraints.
145 * Returns the flat-bottom potential. */
146 real
do_fbposres_cylinder(int fbdim
, rvec fm
, rvec dx
, real rfb
, real kk
, gmx_bool bInvert
)
149 real dr
, dr2
, invdr
, v
, rfb2
;
152 rfb2
= gmx::square(rfb
);
155 for (d
= 0; d
< DIM
; d
++)
159 dr2
+= gmx::square(dx
[d
]);
164 ( (dr2
> rfb2
&& bInvert
== FALSE
) || (dr2
< rfb2
&& bInvert
== TRUE
) )
169 v
= 0.5*kk
*gmx::square(dr
- rfb
);
170 for (d
= 0; d
< DIM
; d
++)
174 fm
[d
] = -kk
*(dr
-rfb
)*dx
[d
]*invdr
; /* Force pointing to the center */
182 /*! \brief Compute energies and forces for flat-bottomed position restraints
184 * Returns the flat-bottomed potential. Same PBC treatment as in
185 * normal position restraints */
186 real
fbposres(int nbonds
,
187 const t_iatom forceatoms
[], const t_iparams forceparams
[],
189 gmx::ForceWithVirial
*forceWithVirial
,
191 int refcoord_scaling
, int ePBC
, const rvec com
)
192 /* compute flat-bottomed positions restraints */
194 int i
, ai
, m
, d
, type
, npbcdim
= 0, fbdim
;
197 real dr
, dr2
, rfb
, rfb2
, fact
;
198 rvec com_sc
, rdist
, dx
, dpdl
, fm
;
201 npbcdim
= ePBC2npbcdim(ePBC
);
203 if (refcoord_scaling
== erscCOM
)
206 for (m
= 0; m
< npbcdim
; m
++)
208 assert(npbcdim
<= DIM
);
209 for (d
= m
; d
< npbcdim
; d
++)
211 com_sc
[m
] += com
[d
]*pbc
->box
[d
][m
];
216 rvec
*f
= as_rvec_array(forceWithVirial
->force_
.data());
219 for (i
= 0; (i
< nbonds
); )
221 type
= forceatoms
[i
++];
222 ai
= forceatoms
[i
++];
223 pr
= &forceparams
[type
];
225 /* same calculation as for normal posres, but with identical A and B states, and lambda==0 */
226 posres_dx(x
[ai
], forceparams
[type
].fbposres
.pos0
, forceparams
[type
].fbposres
.pos0
,
228 pbc
, refcoord_scaling
, npbcdim
,
235 rfb
= pr
->fbposres
.r
;
236 rfb2
= gmx::square(rfb
);
238 /* with rfb<0, push particle out of the sphere/cylinder/layer */
246 switch (pr
->fbposres
.geom
)
248 case efbposresSPHERE
:
249 /* spherical flat-bottom posres */
252 ( (dr2
> rfb2
&& bInvert
== FALSE
) || (dr2
< rfb2
&& bInvert
== TRUE
) )
256 v
= 0.5*kk
*gmx::square(dr
- rfb
);
257 fact
= -kk
*(dr
-rfb
)/dr
; /* Force pointing to the center pos0 */
261 case efbposresCYLINDERX
:
262 /* cylindrical flat-bottom posres in y-z plane. fm[XX] = 0. */
264 v
= do_fbposres_cylinder(fbdim
, fm
, dx
, rfb
, kk
, bInvert
);
266 case efbposresCYLINDERY
:
267 /* cylindrical flat-bottom posres in x-z plane. fm[YY] = 0. */
269 v
= do_fbposres_cylinder(fbdim
, fm
, dx
, rfb
, kk
, bInvert
);
271 case efbposresCYLINDER
:
272 /* equivalent to efbposresCYLINDERZ for backwards compatibility */
273 case efbposresCYLINDERZ
:
274 /* cylindrical flat-bottom posres in x-y plane. fm[ZZ] = 0. */
276 v
= do_fbposres_cylinder(fbdim
, fm
, dx
, rfb
, kk
, bInvert
);
278 case efbposresX
: /* fbdim=XX */
279 case efbposresY
: /* fbdim=YY */
280 case efbposresZ
: /* fbdim=ZZ */
281 /* 1D flat-bottom potential */
282 fbdim
= pr
->fbposres
.geom
- efbposresX
;
284 if ( ( dr
> rfb
&& bInvert
== FALSE
) || ( 0 < dr
&& dr
< rfb
&& bInvert
== TRUE
) )
286 v
= 0.5*kk
*gmx::square(dr
- rfb
);
287 fm
[fbdim
] = -kk
*(dr
- rfb
);
289 else if ( (dr
< (-rfb
) && bInvert
== FALSE
) || ( (-rfb
) < dr
&& dr
< 0 && bInvert
== TRUE
))
291 v
= 0.5*kk
*gmx::square(dr
+ rfb
);
292 fm
[fbdim
] = -kk
*(dr
+ rfb
);
299 for (m
= 0; (m
< DIM
); m
++)
302 /* Here we correct for the pbc_dx which included rdist */
303 virial
[m
] -= 0.5*(dx
[m
] + rdist
[m
])*fm
[m
];
307 forceWithVirial
->addVirialContribution(virial
);
313 /*! \brief Compute energies and forces, when requested, for position restraints
315 * Note that position restraints require a different pbc treatment
316 * from other bondeds */
317 template<bool computeForce
>
318 real
posres(int nbonds
,
319 const t_iatom forceatoms
[], const t_iparams forceparams
[],
321 gmx::ForceWithVirial
*forceWithVirial
,
322 const struct t_pbc
*pbc
,
323 real lambda
, real
*dvdlambda
,
324 int refcoord_scaling
, int ePBC
, const rvec comA
, const rvec comB
)
326 int i
, ai
, m
, d
, type
, npbcdim
= 0;
329 rvec comA_sc
, comB_sc
, rdist
, dpdl
, dx
;
331 npbcdim
= ePBC2npbcdim(ePBC
);
333 if (refcoord_scaling
== erscCOM
)
337 for (m
= 0; m
< npbcdim
; m
++)
339 assert(npbcdim
<= DIM
);
340 for (d
= m
; d
< npbcdim
; d
++)
342 comA_sc
[m
] += comA
[d
]*pbc
->box
[d
][m
];
343 comB_sc
[m
] += comB
[d
]*pbc
->box
[d
][m
];
348 const real L1
= 1.0 - lambda
;
353 GMX_ASSERT(forceWithVirial
!= nullptr, "When forces are requested we need a force object");
354 f
= as_rvec_array(forceWithVirial
->force_
.data());
357 /* Use intermediate virial buffer to reduce reduction rounding errors */
359 for (i
= 0; (i
< nbonds
); )
361 type
= forceatoms
[i
++];
362 ai
= forceatoms
[i
++];
363 pr
= &forceparams
[type
];
365 /* return dx, rdist, and dpdl */
366 posres_dx(x
[ai
], forceparams
[type
].posres
.pos0A
, forceparams
[type
].posres
.pos0B
,
367 comA_sc
, comB_sc
, lambda
,
368 pbc
, refcoord_scaling
, npbcdim
,
371 for (m
= 0; (m
< DIM
); m
++)
373 kk
= L1
*pr
->posres
.fcA
[m
] + lambda
*pr
->posres
.fcB
[m
];
375 vtot
+= 0.5*kk
*dx
[m
]*dx
[m
];
377 0.5*(pr
->posres
.fcB
[m
] - pr
->posres
.fcA
[m
])*dx
[m
]*dx
[m
]
380 /* Here we correct for the pbc_dx which included rdist */
384 virial
[m
] -= 0.5*(dx
[m
] + rdist
[m
])*fm
;
391 forceWithVirial
->addVirialContribution(virial
);
400 posres_wrapper(t_nrnb
*nrnb
,
402 const struct t_pbc
*pbc
,
404 gmx_enerdata_t
*enerd
,
406 const t_forcerec
*fr
,
407 gmx::ForceWithVirial
*forceWithVirial
)
412 v
= posres
<true>(idef
->il
[F_POSRES
].nr
, idef
->il
[F_POSRES
].iatoms
,
413 idef
->iparams_posres
,
416 fr
->ePBC
== epbcNONE
? nullptr : pbc
,
417 lambda
[efptRESTRAINT
], &dvdl
,
418 fr
->rc_scaling
, fr
->ePBC
, fr
->posres_com
, fr
->posres_comB
);
419 enerd
->term
[F_POSRES
] += v
;
420 /* If just the force constant changes, the FEP term is linear,
421 * but if k changes, it is not.
423 enerd
->dvdl_nonlin
[efptRESTRAINT
] += dvdl
;
424 inc_nrnb(nrnb
, eNR_POSRES
, idef
->il
[F_POSRES
].nr
/2);
428 posres_wrapper_lambda(struct gmx_wallcycle
*wcycle
,
429 const t_lambda
*fepvals
,
431 const struct t_pbc
*pbc
,
433 gmx_enerdata_t
*enerd
,
435 const t_forcerec
*fr
)
440 if (0 == idef
->il
[F_POSRES
].nr
)
445 wallcycle_sub_start_nocount(wcycle
, ewcsRESTRAINTS
);
446 for (i
= 0; i
< enerd
->n_lambda
; i
++)
448 real dvdl_dum
= 0, lambda_dum
;
450 lambda_dum
= (i
== 0 ? lambda
[efptRESTRAINT
] : fepvals
->all_lambda
[efptRESTRAINT
][i
-1]);
451 v
= posres
<false>(idef
->il
[F_POSRES
].nr
, idef
->il
[F_POSRES
].iatoms
,
452 idef
->iparams_posres
,
454 fr
->ePBC
== epbcNONE
? nullptr : pbc
, lambda_dum
, &dvdl_dum
,
455 fr
->rc_scaling
, fr
->ePBC
, fr
->posres_com
, fr
->posres_comB
);
456 enerd
->enerpart_lambda
[i
] += v
;
458 wallcycle_sub_stop(wcycle
, ewcsRESTRAINTS
);
461 /*! \brief Helper function that wraps calls to fbposres for
462 free-energy perturbation */
463 void fbposres_wrapper(t_nrnb
*nrnb
,
465 const struct t_pbc
*pbc
,
467 gmx_enerdata_t
*enerd
,
468 const t_forcerec
*fr
,
469 gmx::ForceWithVirial
*forceWithVirial
)
473 v
= fbposres(idef
->il
[F_FBPOSRES
].nr
, idef
->il
[F_FBPOSRES
].iatoms
,
474 idef
->iparams_fbposres
,
477 fr
->ePBC
== epbcNONE
? nullptr : pbc
,
478 fr
->rc_scaling
, fr
->ePBC
, fr
->posres_com
);
479 enerd
->term
[F_FBPOSRES
] += v
;
480 inc_nrnb(nrnb
, eNR_FBPOSRES
, idef
->il
[F_FBPOSRES
].nr
/2);