Fixed bug in gmx disres indexing arrays.
[gromacs.git] / src / gromacs / listed-forces / position-restraints.cpp
blob3706f4016e56965cc6c8b1ee62927fca77d66bf7
1 /*
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.
35 /*! \internal \file
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
45 #include "gmxpre.h"
47 #include "position-restraints.h"
49 #include <assert.h>
51 #include <cmath>
53 #include "gromacs/gmxlib/nrnb.h"
54 #include "gromacs/math/functions.h"
55 #include "gromacs/math/vec.h"
56 #include "gromacs/mdtypes/forcerec.h"
57 #include "gromacs/mdtypes/inputrec.h"
58 #include "gromacs/mdtypes/md_enums.h"
59 #include "gromacs/pbcutil/pbc.h"
60 #include "gromacs/timing/wallcycle.h"
61 #include "gromacs/topology/idef.h"
62 #include "gromacs/utility/basedefinitions.h"
63 #include "gromacs/utility/fatalerror.h"
65 struct gmx_wallcycle;
67 namespace
70 /*! \brief returns dx, rdist, and dpdl for functions posres() and fbposres()
72 void posres_dx(const rvec x, const rvec pos0A, const rvec pos0B,
73 const rvec comA_sc, const rvec comB_sc,
74 real lambda,
75 const t_pbc *pbc, int refcoord_scaling, int npbcdim,
76 rvec dx, rvec rdist, rvec dpdl)
78 int m, d;
79 real posA, posB, L1, ref = 0.;
80 rvec pos;
82 L1 = 1.0-lambda;
84 for (m = 0; m < DIM; m++)
86 posA = pos0A[m];
87 posB = pos0B[m];
88 if (m < npbcdim)
90 switch (refcoord_scaling)
92 case erscNO:
93 ref = 0;
94 rdist[m] = L1*posA + lambda*posB;
95 dpdl[m] = posB - posA;
96 break;
97 case erscALL:
98 /* Box relative coordinates are stored for dimensions with pbc */
99 posA *= pbc->box[m][m];
100 posB *= pbc->box[m][m];
101 assert(npbcdim <= DIM);
102 for (d = m+1; d < npbcdim; d++)
104 posA += pos0A[d]*pbc->box[d][m];
105 posB += pos0B[d]*pbc->box[d][m];
107 ref = L1*posA + lambda*posB;
108 rdist[m] = 0;
109 dpdl[m] = posB - posA;
110 break;
111 case erscCOM:
112 ref = L1*comA_sc[m] + lambda*comB_sc[m];
113 rdist[m] = L1*posA + lambda*posB;
114 dpdl[m] = comB_sc[m] - comA_sc[m] + posB - posA;
115 break;
116 default:
117 gmx_fatal(FARGS, "No such scaling method implemented");
120 else
122 ref = L1*posA + lambda*posB;
123 rdist[m] = 0;
124 dpdl[m] = posB - posA;
127 /* We do pbc_dx with ref+rdist,
128 * since with only ref we can be up to half a box vector wrong.
130 pos[m] = ref + rdist[m];
133 if (pbc)
135 pbc_dx(pbc, x, pos, dx);
137 else
139 rvec_sub(x, pos, dx);
143 /*! \brief Computes forces and potential for flat-bottom cylindrical restraints.
144 * Returns the flat-bottom potential. */
145 real do_fbposres_cylinder(int fbdim, rvec fm, rvec dx, real rfb, real kk, gmx_bool bInvert)
147 int d;
148 real dr, dr2, invdr, v, rfb2;
150 dr2 = 0.0;
151 rfb2 = gmx::square(rfb);
152 v = 0.0;
154 for (d = 0; d < DIM; d++)
156 if (d != fbdim)
158 dr2 += gmx::square(dx[d]);
162 if (dr2 > 0.0 &&
163 ( (dr2 > rfb2 && bInvert == FALSE ) || (dr2 < rfb2 && bInvert == TRUE ) )
166 dr = std::sqrt(dr2);
167 invdr = 1./dr;
168 v = 0.5*kk*gmx::square(dr - rfb);
169 for (d = 0; d < DIM; d++)
171 if (d != fbdim)
173 fm[d] = -kk*(dr-rfb)*dx[d]*invdr; /* Force pointing to the center */
178 return v;
181 /*! \brief Adds forces of flat-bottomed positions restraints to f[]
182 * and fixes vir_diag.
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[],
188 const rvec x[], rvec f[], rvec vir_diag,
189 const t_pbc *pbc,
190 int refcoord_scaling, int ePBC, rvec com)
191 /* compute flat-bottomed positions restraints */
193 int i, ai, m, d, type, npbcdim = 0, fbdim;
194 const t_iparams *pr;
195 real vtot, kk, v;
196 real dr, dr2, rfb, rfb2, fact;
197 rvec com_sc, rdist, dx, dpdl, fm;
198 gmx_bool bInvert;
200 npbcdim = ePBC2npbcdim(ePBC);
202 if (refcoord_scaling == erscCOM)
204 clear_rvec(com_sc);
205 for (m = 0; m < npbcdim; m++)
207 assert(npbcdim <= DIM);
208 for (d = m; d < npbcdim; d++)
210 com_sc[m] += com[d]*pbc->box[d][m];
215 vtot = 0.0;
216 for (i = 0; (i < nbonds); )
218 type = forceatoms[i++];
219 ai = forceatoms[i++];
220 pr = &forceparams[type];
222 /* same calculation as for normal posres, but with identical A and B states, and lambda==0 */
223 posres_dx(x[ai], forceparams[type].fbposres.pos0, forceparams[type].fbposres.pos0,
224 com_sc, com_sc, 0.0,
225 pbc, refcoord_scaling, npbcdim,
226 dx, rdist, dpdl);
228 clear_rvec(fm);
229 v = 0.0;
231 kk = pr->fbposres.k;
232 rfb = pr->fbposres.r;
233 rfb2 = gmx::square(rfb);
235 /* with rfb<0, push particle out of the sphere/cylinder/layer */
236 bInvert = FALSE;
237 if (rfb < 0.)
239 bInvert = TRUE;
240 rfb = -rfb;
243 switch (pr->fbposres.geom)
245 case efbposresSPHERE:
246 /* spherical flat-bottom posres */
247 dr2 = norm2(dx);
248 if (dr2 > 0.0 &&
249 ( (dr2 > rfb2 && bInvert == FALSE ) || (dr2 < rfb2 && bInvert == TRUE ) )
252 dr = std::sqrt(dr2);
253 v = 0.5*kk*gmx::square(dr - rfb);
254 fact = -kk*(dr-rfb)/dr; /* Force pointing to the center pos0 */
255 svmul(fact, dx, fm);
257 break;
258 case efbposresCYLINDERX:
259 /* cylindrical flat-bottom posres in y-z plane. fm[XX] = 0. */
260 fbdim = XX;
261 v = do_fbposres_cylinder(fbdim, fm, dx, rfb, kk, bInvert);
262 break;
263 case efbposresCYLINDERY:
264 /* cylindrical flat-bottom posres in x-z plane. fm[YY] = 0. */
265 fbdim = YY;
266 v = do_fbposres_cylinder(fbdim, fm, dx, rfb, kk, bInvert);
267 break;
268 case efbposresCYLINDER:
269 /* equivalent to efbposresCYLINDERZ for backwards compatibility */
270 case efbposresCYLINDERZ:
271 /* cylindrical flat-bottom posres in x-y plane. fm[ZZ] = 0. */
272 fbdim = ZZ;
273 v = do_fbposres_cylinder(fbdim, fm, dx, rfb, kk, bInvert);
274 break;
275 case efbposresX: /* fbdim=XX */
276 case efbposresY: /* fbdim=YY */
277 case efbposresZ: /* fbdim=ZZ */
278 /* 1D flat-bottom potential */
279 fbdim = pr->fbposres.geom - efbposresX;
280 dr = dx[fbdim];
281 if ( ( dr > rfb && bInvert == FALSE ) || ( 0 < dr && dr < rfb && bInvert == TRUE ) )
283 v = 0.5*kk*gmx::square(dr - rfb);
284 fm[fbdim] = -kk*(dr - rfb);
286 else if ( (dr < (-rfb) && bInvert == FALSE ) || ( (-rfb) < dr && dr < 0 && bInvert == TRUE ))
288 v = 0.5*kk*gmx::square(dr + rfb);
289 fm[fbdim] = -kk*(dr + rfb);
291 break;
294 vtot += v;
296 for (m = 0; (m < DIM); m++)
298 f[ai][m] += fm[m];
299 /* Here we correct for the pbc_dx which included rdist */
300 vir_diag[m] -= 0.5*(dx[m] + rdist[m])*fm[m];
304 return vtot;
308 /*! \brief Compute energies and forces for position restraints
310 * Note that position restraints require a different pbc treatment
311 * from other bondeds */
312 real posres(int nbonds,
313 const t_iatom forceatoms[], const t_iparams forceparams[],
314 const rvec x[], rvec f[], rvec vir_diag,
315 const struct t_pbc *pbc,
316 real lambda, real *dvdlambda,
317 int refcoord_scaling, int ePBC, rvec comA, rvec comB)
319 int i, ai, m, d, type, npbcdim = 0;
320 const t_iparams *pr;
321 real L1;
322 real vtot, kk, fm;
323 rvec comA_sc, comB_sc, rdist, dpdl, dx;
324 gmx_bool bForceValid = TRUE;
326 if ((f == nullptr) || (vir_diag == nullptr)) /* should both be null together! */
328 bForceValid = FALSE;
331 npbcdim = ePBC2npbcdim(ePBC);
333 if (refcoord_scaling == erscCOM)
335 clear_rvec(comA_sc);
336 clear_rvec(comB_sc);
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 L1 = 1.0 - lambda;
350 vtot = 0.0;
351 for (i = 0; (i < nbonds); )
353 type = forceatoms[i++];
354 ai = forceatoms[i++];
355 pr = &forceparams[type];
357 /* return dx, rdist, and dpdl */
358 posres_dx(x[ai], forceparams[type].posres.pos0A, forceparams[type].posres.pos0B,
359 comA_sc, comB_sc, lambda,
360 pbc, refcoord_scaling, npbcdim,
361 dx, rdist, dpdl);
363 for (m = 0; (m < DIM); m++)
365 kk = L1*pr->posres.fcA[m] + lambda*pr->posres.fcB[m];
366 fm = -kk*dx[m];
367 vtot += 0.5*kk*dx[m]*dx[m];
368 *dvdlambda +=
369 0.5*(pr->posres.fcB[m] - pr->posres.fcA[m])*dx[m]*dx[m]
370 + fm*dpdl[m];
372 /* Here we correct for the pbc_dx which included rdist */
373 if (bForceValid)
375 f[ai][m] += fm;
376 vir_diag[m] -= 0.5*(dx[m] + rdist[m])*fm;
381 return vtot;
384 } // namespace
386 void
387 posres_wrapper(t_nrnb *nrnb,
388 const t_idef *idef,
389 const struct t_pbc *pbc,
390 const rvec x[],
391 gmx_enerdata_t *enerd,
392 real *lambda,
393 t_forcerec *fr)
395 real v, dvdl;
397 dvdl = 0;
398 v = posres(idef->il[F_POSRES].nr, idef->il[F_POSRES].iatoms,
399 idef->iparams_posres,
400 x, as_rvec_array(fr->f_novirsum->data()), fr->vir_diag_posres,
401 fr->ePBC == epbcNONE ? nullptr : pbc,
402 lambda[efptRESTRAINT], &dvdl,
403 fr->rc_scaling, fr->ePBC, fr->posres_com, fr->posres_comB);
404 enerd->term[F_POSRES] += v;
405 /* If just the force constant changes, the FEP term is linear,
406 * but if k changes, it is not.
408 enerd->dvdl_nonlin[efptRESTRAINT] += dvdl;
409 inc_nrnb(nrnb, eNR_POSRES, idef->il[F_POSRES].nr/2);
412 void
413 posres_wrapper_lambda(struct gmx_wallcycle *wcycle,
414 const t_lambda *fepvals,
415 const t_idef *idef,
416 const struct t_pbc *pbc,
417 const rvec x[],
418 gmx_enerdata_t *enerd,
419 real *lambda,
420 t_forcerec *fr)
422 real v;
423 int i;
425 if (0 == idef->il[F_POSRES].nr)
427 return;
430 wallcycle_sub_start_nocount(wcycle, ewcsRESTRAINTS);
431 for (i = 0; i < enerd->n_lambda; i++)
433 real dvdl_dum = 0, lambda_dum;
435 lambda_dum = (i == 0 ? lambda[efptRESTRAINT] : fepvals->all_lambda[efptRESTRAINT][i-1]);
436 v = posres(idef->il[F_POSRES].nr, idef->il[F_POSRES].iatoms,
437 idef->iparams_posres,
438 x, nullptr, nullptr,
439 fr->ePBC == epbcNONE ? nullptr : pbc, lambda_dum, &dvdl_dum,
440 fr->rc_scaling, fr->ePBC, fr->posres_com, fr->posres_comB);
441 enerd->enerpart_lambda[i] += v;
443 wallcycle_sub_stop(wcycle, ewcsRESTRAINTS);
446 /*! \brief Helper function that wraps calls to fbposres for
447 free-energy perturbation */
448 void fbposres_wrapper(t_nrnb *nrnb,
449 const t_idef *idef,
450 const struct t_pbc *pbc,
451 const rvec x[],
452 gmx_enerdata_t *enerd,
453 t_forcerec *fr)
455 real v;
457 v = fbposres(idef->il[F_FBPOSRES].nr, idef->il[F_FBPOSRES].iatoms,
458 idef->iparams_fbposres,
459 x, as_rvec_array(fr->f_novirsum->data()), fr->vir_diag_posres,
460 fr->ePBC == epbcNONE ? nullptr : pbc,
461 fr->rc_scaling, fr->ePBC, fr->posres_com);
462 enerd->term[F_FBPOSRES] += v;
463 inc_nrnb(nrnb, eNR_FBPOSRES, idef->il[F_FBPOSRES].nr/2);