Enforced rotation: Another minor optimization in do_flex[1,2]_lowlevel
[gromacs.git] / src / mdlib / pull_rotation.c
blobaae42def045ee4fefc4a51972773a5ea00b344ab
1 /*
2 *
3 * This source code is part of
4 *
5 * G R O M A C S
6 *
7 * GROningen MAchine for Chemical Simulations
8 *
9 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11 * Copyright (c) 2001-2008, The GROMACS development team,
12 * check out http://www.gromacs.org for more information.
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * If you want to redistribute modifications, please consider that
20 * scientific software is very special. Version control is crucial -
21 * bugs must be traceable. We will be happy to consider code for
22 * inclusion in the official distribution, but derived work must not
23 * be called official GROMACS. Details are found in the README & COPYING
24 * files - if they are missing, get the official version at www.gromacs.org.
26 * To help us fund GROMACS development, we humbly ask that you cite
27 * the papers on the package - you can find them in the top README file.
29 * For more info, check our website at http://www.gromacs.org
31 * And Hey:
32 * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include "domdec.h"
41 #include "gmx_wallcycle.h"
42 #include "trnio.h"
43 #include "smalloc.h"
44 #include "network.h"
45 #include "pbc.h"
46 #include "futil.h"
47 #include "mdrun.h"
48 #include "txtdump.h"
49 #include "names.h"
50 #include "mtop_util.h"
51 #include "names.h"
52 #include "nrjac.h"
53 #include "vec.h"
54 #include "gmx_ga2la.h"
55 #include "edsam.h"
56 #include "xvgr.h"
57 #include "gmxfio.h"
60 /* Enforce rotation / flexible: determine the angle of each slab */
61 typedef struct gmx_slabdata
63 int nat; /* The number of coordinates belonging to this slab */
64 rvec *x; /* The coordinates belonging to this slab. In general, this should be all
65 * rotation group coordinates, but we can leave a few of them away if they have
66 * small enough weights. */
67 rvec *ref; /* Same for reference */
68 real *weight; /* The weight for each atom */
69 } t_gmx_slabdata;
72 static double** allocate_square_matrix(int dim)
74 int i;
75 double** mat = NULL;
78 snew(mat, dim);
79 for(i=0; i<dim; i++)
80 snew(mat[i], dim);
82 return mat;
86 static void free_square_matrix(double** mat, int dim)
88 int i;
91 for (i=0; i<dim; i++)
92 sfree(mat[i]);
93 sfree(mat);
97 /* Output rotation energy and torque for each rotation group */
98 static void reduce_output(t_commrec *cr, t_rot *rot, real t)
100 int g,i,islab,k,nslabs=0;
101 int count; /* MPI element counter */
102 t_rotgrp *rotg;
105 /* Fill the MPI buffer with stuff to reduce: */
106 if (PAR(cr))
108 count=0;
109 for (g=0; g < rot->ngrp; g++)
111 rotg = &rot->grp[g];
112 nslabs = 2*rotg->slab_max_nr+1;
113 rot->inbuf[count++] = rotg->V;
114 switch (rotg->eType)
116 case erotgFIXED:
117 case erotgFIXED_PLANE:
118 case erotgFOLLOW_PLANE:
119 rot->inbuf[count++] = rotg->fix_torque_v;
120 rot->inbuf[count++] = rotg->fix_angles_v;
121 rot->inbuf[count++] = rotg->fix_weight_v;
122 break;
123 case erotgFLEX1:
124 case erotgFLEX2:
125 /* (Re-)allocate memory for MPI buffer: */
126 if (rot->bufsize < count+nslabs)
128 rot->bufsize = count+nslabs;
129 srenew(rot->inbuf , rot->bufsize);
130 srenew(rot->outbuf, rot->bufsize);
132 for (i=0; i<nslabs; i++)
133 rot->inbuf[count++] = rotg->slab_torque_v[i];
134 break;
135 default:
136 break;
139 #ifdef GMX_MPI
140 MPI_Reduce(rot->inbuf, rot->outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
141 #endif
142 /* Copy back the reduced data from the buffer on the master */
143 if (MASTER(cr))
145 count=0;
146 for (g=0; g < rot->ngrp; g++)
148 rotg = &rot->grp[g];
149 nslabs = 2*rotg->slab_max_nr+1;
150 rotg->V = rot->outbuf[count++];
151 switch (rotg->eType)
153 case erotgFIXED:
154 case erotgFIXED_PLANE:
155 case erotgFOLLOW_PLANE:
156 rotg->fix_torque_v = rot->outbuf[count++];
157 rotg->fix_angles_v = rot->outbuf[count++];
158 rotg->fix_weight_v = rot->outbuf[count++];
159 break;
160 case erotgFLEX1:
161 case erotgFLEX2:
162 for (i=0; i<nslabs; i++)
163 rotg->slab_torque_v[i] = rot->outbuf[count++];
164 break;
165 default:
166 break;
172 /* Output */
173 if (MASTER(cr))
175 /* Av. angle and total torque for each rotation group */
176 for (g=0; g < rot->ngrp; g++)
178 rotg=&rot->grp[g];
180 /* Output to main rotation log file: */
181 if (rotg->eType == erotgFIXED || rotg->eType == erotgFIXED_PLANE || rotg->eType == erotgFOLLOW_PLANE)
183 fprintf(rot->out_rot, "%12.4f%12.3e",
184 (rotg->fix_angles_v/rotg->fix_weight_v)*180.0*M_1_PI,
185 rotg->fix_torque_v);
187 fprintf(rot->out_rot, "%12.3e", rotg->V);
189 /* Output to torque log file: */
190 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
192 fprintf(rot->out_torque, "%12.3e%6d", t, g);
193 k = rotg->slab_max_nr;
194 for (i=-k; i <= k; i++)
196 islab = i + rotg->slab_max_nr; /* slab index */
197 /* Only output if enough weight is in slab */
198 if (rotg->slab_weights[islab] > rotg->min_gaussian)
199 fprintf(rot->out_torque, "%6d%12.3e", i, rotg->slab_torque_v[islab]);
201 fprintf(rot->out_torque , "\n");
204 fprintf(rot->out_rot, "\n");
209 /* Add the forces from enforced rotation potential to the local forces.
210 * Should be called after the SR forces have been evaluated */
211 extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, int step, real t)
213 int g,l,ii;
214 t_rotgrp *rotg;
217 GMX_MPE_LOG(ev_add_rot_forces_start);
219 /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
220 * on the master and output these values to file. */
221 if (do_per_step(step, rot->nsttout))
222 reduce_output(cr, rot, t);
224 /* Total rotation potential is the sum over all rotation groups */
225 rot->Vrot = 0.0;
227 /* Loop over enforced rotation groups (usually 1, though)
228 * Apply the forces from rotation potentials */
229 for (g=0; g<rot->ngrp; g++)
231 rotg = &rot->grp[g];
232 rot->Vrot += rotg->V;
233 for (l=0; l<rotg->nat_loc; l++)
235 /* Get the right index of the local force */
236 ii = rotg->ind_loc[l];
237 /* Add */
238 rvec_inc(f[ii],rotg->f_rot_loc[l]);
242 GMX_MPE_LOG(ev_add_rot_forces_finish);
244 return (MASTER(cr)? rot->Vrot : 0.0);
248 /* Calculate the box diagonal length */
249 static real diagonal_length(matrix box)
251 rvec diag;
254 copy_rvec(box[XX],diag);
255 rvec_inc(diag,box[YY]);
256 rvec_inc(diag,box[ZZ]);
258 return norm(diag);
262 static inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
264 return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
268 static inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
270 /* norm is chosen such that the sum of the gaussians
271 * over the slabs is approximately 1.0 everywhere */
272 const real norm = 0.5698457353514458216; /* = 1/1.7548609 */
273 real sigma;
276 /* Define the sigma value */
277 sigma = 0.7*rotg->slab_dist;
278 /* Calculate the Gaussian value of slab n for coordinate curr_x */
279 return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
283 static void get_slab_centers(
284 t_rotgrp *rotg, /* The rotation group information */
285 rvec *xc, /* The rotation group coordinates; will typically be pgrp->xc,
286 * but at first call it is pgrp->xc_ref */
287 matrix box, /* The box coordinates */
288 t_commrec *cr, /* Communication record */
289 int g, /* The rotation group number */
290 bool bDynBox, /* Is the box dynamic? */
291 real time, /* Used for output only */
292 FILE *out_slabs, /* For outputting COG per slab information */
293 bool bOutStep, /* Is this an output step? */
294 bool bReference) /* If this routine is called from
295 * init_rotation_group_rot we need to store
296 * the reference slab COGs */
298 rvec curr_x; /* The coordinate of an atom */
299 rvec curr_x_weighted; /* The gaussian-weighted coordinate */
300 real gaussian; /* The gaussian */
301 int i,j,k,islab,nslabs;
302 real box_d; /* The box diagonal */
303 bool bFirstSet;
306 /* If the box grows during the simulation, we might need more memory */
307 if (bDynBox)
309 box_d = diagonal_length(box);
311 /* The slab indices run from [-pgrp->slab_max_nr, -1, 0, +1, ..., +pgrp->slab_max_nr] */
312 nslabs = 2*rotg->slab_max_nr + 1;
314 /* The box diagonal divided by the slab distance gives the maximum number of slabs in positive direction: */
315 if ( (int)ceil(box_d/rotg->slab_dist) > rotg->slab_max_nr )
317 while ( (int)ceil(box_d/rotg->slab_dist) > rotg->slab_max_nr )
318 rotg->slab_max_nr++;
319 /* TODO: it could still be that the rotation group diffuses out of the
320 * box. Then we would have to allocate more slabs than fit in a box!
323 nslabs = 2*rotg->slab_max_nr + 1;
324 fprintf(stdout, "Node %d reallocates memory to hold data for %d slabs (rotation group %d).\n", cr->nodeid,nslabs,g);
325 srenew(rotg->slab_center , nslabs);
326 srenew(rotg->slab_weights , nslabs);
327 srenew(rotg->slab_torque_v, nslabs);
328 srenew(rotg->slab_data , nslabs);
329 for (i=0; i<nslabs; i++)
331 srenew(rotg->slab_data[i].x , rotg->nat);
332 srenew(rotg->slab_data[i].ref , rotg->nat);
333 srenew(rotg->slab_data[i].weight, rotg->nat);
338 /* Loop over slabs */
339 bFirstSet = FALSE;
340 k = rotg->slab_max_nr;
341 rotg->slab_first = 1;
342 rotg->slab_last = 0;
343 for (j = -k; j <= k; j++)
345 islab = j+rotg->slab_max_nr; /* slab index */
346 /* Initialize data for this slab: */
347 clear_rvec(rotg->slab_center[islab]);
348 rotg->slab_weights[islab] = 0.0;
350 /* loop over all atoms in the rotation group */
351 for(i=0; i<rotg->nat;i++)
353 copy_rvec(xc[i], curr_x);
354 gaussian = gaussian_weight(curr_x, rotg, j);
355 svmul(gaussian, curr_x, curr_x_weighted);
356 rvec_add(rotg->slab_center[islab], curr_x_weighted, rotg->slab_center[islab]);
357 rotg->slab_weights[islab] += gaussian;
358 } /* END of loop over rotation group atoms */
360 /* Do the calculations ONLY if there is enough weight in the slab! */
361 if (rotg->slab_weights[islab] > rotg->min_gaussian)
363 svmul(1.0/rotg->slab_weights[islab], rotg->slab_center[islab], rotg->slab_center[islab]);
364 /* Remember which slabs to calculate for the low-level routines */
365 if (!bFirstSet)
367 rotg->slab_first = j;
368 bFirstSet = TRUE;
370 rotg->slab_last = j;
372 /* At first time step: save the COGs of the reference structure */
373 if(bReference)
374 copy_rvec(rotg->slab_center[islab], rotg->slab_center_ref[islab]);
375 } /* END of loop over slabs */
377 /* Output on the master */
378 if (MASTER(cr) && bOutStep)
380 fprintf(out_slabs, "%12.3e%6d", time, g);
381 for (j = rotg->slab_first; j <= rotg->slab_last; j++)
383 islab = j+rotg->slab_max_nr; /* slab index */
384 fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
385 j,rotg->slab_center[islab][XX],rotg->slab_center[islab][YY],rotg->slab_center[islab][ZZ]);
387 if (!bFirstSet)
388 fprintf(out_slabs, "WARNING: no weight in any of the slabs - nothing to calculate!");
389 fprintf(out_slabs, "\n");
394 static void calc_rotmat(
395 rvec vec,
396 real degangle, /* Angle alpha of rotation at time t in degrees */
397 matrix rotmat) /* Rotation matrix */
399 real radangle; /* Rotation angle in radians */
400 real cosa; /* cosine alpha */
401 real sina; /* sine alpha */
402 real OMcosa; /* 1 - cos(alpha) */
403 real dumxy, dumxz, dumyz; /* save computations */
404 rvec rot_vec; /* Rotate around rot_vec ... */
407 radangle = degangle * M_PI/180.0;
408 copy_rvec(vec , rot_vec );
410 /* Precompute some variables: */
411 cosa = cos(radangle);
412 sina = sin(radangle);
413 OMcosa = 1.0 - cosa;
414 dumxy = rot_vec[XX]*rot_vec[YY]*OMcosa;
415 dumxz = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
416 dumyz = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
418 /* Construct the rotation matrix for this rotation group: */
419 /* 1st column: */
420 rotmat[XX][XX] = cosa + rot_vec[XX]*rot_vec[XX]*OMcosa;
421 rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
422 rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
423 /* 2nd column: */
424 rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
425 rotmat[YY][YY] = cosa + rot_vec[YY]*rot_vec[YY]*OMcosa;
426 rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
427 /* 3rd column: */
428 rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
429 rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
430 rotmat[ZZ][ZZ] = cosa + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
432 #ifdef PRINTMATRIX
433 int iii,jjj;
435 for (iii=0; iii<3; iii++) {
436 for (jjj=0; jjj<3; jjj++)
437 fprintf(stderr, " %10.8f ", rotmat[iii][jjj]);
438 fprintf(stderr, "\n");
440 #endif
444 /* Calculates torque on the rotation axis
445 * tau = coord x force */
446 static inline real torque(
447 rvec rotvec, /* rotation vector; MUST be normalized! */
448 rvec force, /* force */
449 rvec x, /* coordinate of atom on which the force acts */
450 rvec offset) /* piercing point of rotation axis
451 * (COG of the slab for the flexible types) */
453 rvec vectmp, tau;
456 /* Subtract offset */
457 rvec_sub(x,offset,vectmp);
459 /* coord x force */
460 cprod(vectmp, force, tau);
462 /* Return the part of the torque which is parallel to the rotation vector */
463 return iprod(tau, rotvec);
467 /* Right-aligned output of value with standard width */
468 static void print_aligned(FILE *fp, char *str)
470 fprintf(fp, "%12s", str);
474 /* Right-aligned output of value with standard short width */
475 static void print_aligned_short(FILE *fp, char *str)
477 fprintf(fp, "%6s", str);
481 /* Right-aligned output of value with standard width */
482 static void print_aligned_group(FILE *fp, char *str, int g)
484 char sbuf[STRLEN];
487 sprintf(sbuf, "%s%d", str, g);
488 fprintf(fp, "%12s", sbuf);
492 static FILE *open_output_file(const char *fn, int steps)
494 FILE *fp;
497 fp = ffopen(fn, "w");
499 fprintf(fp, "# Output is written every %d time steps.\n\n", steps);
501 return fp;
505 /* Open output file for slab COG data. Call on master only */
506 static FILE *open_slab_out(t_rot *rot, const char *fn)
508 FILE *fp=NULL;
509 int g;
510 t_rotgrp *rotg;
513 for (g=0; g<rot->ngrp; g++)
515 rotg = &rot->grp[g];
516 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
518 if (NULL == fp)
519 fp = open_output_file(fn, rot->nsttout);
520 fprintf(fp, "# Rotation group %d (%s), slab distance %f nm\n", g, erotg_names[rotg->eType], rotg->slab_dist);
524 if (fp != NULL)
526 fprintf(fp, "# The following columns will have the syntax: (COG = center of geometry, gaussian weighted)\n");
527 fprintf(fp, "# ");
528 print_aligned_short(fp, "t");
529 print_aligned_short(fp, "grp");
530 print_aligned_short(fp, "slab");
531 print_aligned(fp, "COG-X");
532 print_aligned(fp, "COG-Y");
533 print_aligned(fp, "COG-Z");
534 print_aligned_short(fp, "slab");
535 print_aligned(fp, "COG-X");
536 print_aligned(fp, "COG-Y");
537 print_aligned(fp, "COG-Z");
538 print_aligned_short(fp, "slab");
539 fprintf(fp, " ...\n");
540 fflush(fp);
543 return fp;
547 /* Open output file and print some general information about the rotation groups.
548 * Call on master only */
549 static FILE *open_rot_out(const char *fn, t_rot *rot, const output_env_t oenv,
550 unsigned long Flags)
552 FILE *fp;
553 int g,nsets;
554 t_rotgrp *rotg;
555 char **setname,buf[50];
558 if (Flags & MD_APPENDFILES)
560 fp = gmx_fio_fopen(fn,"a");
562 else
564 fp = xvgropen(fn, "Rotation angles and energy", "Time (ps)", "angles and energies", oenv);
565 fprintf(fp, "# The scalar tau is the torque in the direction of the rotation vector v.\n");
566 fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot_vec.\n");
567 fprintf(fp, "# Torques are given in [kJ/mol], anlges in degrees, time in ps.\n");
569 for (g=0; g<rot->ngrp; g++)
571 rotg = &rot->grp[g];
572 fprintf(fp, "# Rotation group %d (%s):\n", g, erotg_names[rotg->eType]);
573 fprintf(fp, "# rot_vec%d %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
574 fprintf(fp, "# rot_rate%d %10.3e degree/ps\n", g, rotg->rate);
575 fprintf(fp, "# rot_k%d %10.3e kJ/(mol*nm^2)\n", g, rotg->k);
577 switch (rotg->eType)
579 case erotgFIXED:
580 case erotgFIXED_PLANE:
581 fprintf(fp, "# rot_offset%d %10.3e %10.3e %10.3e\n", g, rotg->offset[XX], rotg->offset[YY], rotg->offset[ZZ]);
582 break;
583 case erotgFOLLOW_PLANE:
584 fprintf(fp, "# COM of ref. grp. %d %10.3e %10.3e %10.3e\n", g, rotg->xc_ref_center[XX], rotg->xc_ref_center[YY], rotg->xc_ref_center[ZZ]);
585 break;
586 case erotgFLEX1:
587 case erotgFLEX2:
588 fprintf(fp, "# rot_slab_distance%d %f nm\n", g, rotg->slab_dist);
589 fprintf(fp, "# rot_min_gaussian%d %10.3e\n", g, rotg->min_gaussian);
590 fprintf(fp, "# COG of ref. grp. %d %10.3e %10.3e %10.3e (only needed for fit)\n",
591 g, rotg->xc_ref_center[XX], rotg->xc_ref_center[YY], rotg->xc_ref_center[ZZ]);
592 break;
593 default:
594 break;
598 fprintf(fp, "#\n# Legend for the following data columns:\n");
599 fprintf(fp, "# ");
600 print_aligned_short(fp, "t");
601 nsets = 0;
602 snew(setname, 4*rot->ngrp);
604 for (g=0; g<rot->ngrp; g++)
606 rotg = &rot->grp[g];
607 sprintf(buf, "theta_ref%d (degree)", g);
608 print_aligned_group(fp, "theta_ref", g);
609 setname[nsets] = strdup(buf);
610 nsets++;
612 for (g=0; g<rot->ngrp; g++)
614 rotg = &rot->grp[g];
615 if (rotg->eType==erotgFIXED || rotg->eType==erotgFIXED_PLANE || rotg->eType==erotgFOLLOW_PLANE)
617 sprintf(buf, "theta-av%d (degree)", g);
618 print_aligned_group(fp, "theta_av", g);
619 setname[nsets] = strdup(buf);
620 nsets++;
621 sprintf(buf, "tau%d (kJ/mol)", g);
622 print_aligned_group(fp, "tau", g);
623 setname[nsets] = strdup(buf);
624 nsets++;
626 sprintf(buf, "energy%d (kJ/mol)", g);
627 print_aligned_group(fp, "energy", g);
628 setname[nsets] = strdup(buf);
629 nsets++;
631 fprintf(fp, "\n#\n");
633 if (nsets > 1)
634 xvgr_legend(fp, nsets, setname, oenv);
635 for(g=0; g<nsets; g++)
636 sfree(setname[g]);
637 sfree(setname);
639 fflush(fp);
642 return fp;
646 /* Call on master only */
647 static FILE *open_angles_out(t_rot *rot, const char *fn)
649 int g;
650 FILE *fp=NULL;
651 t_rotgrp *rotg;
654 /* Open output file and write some information about it's structure: */
655 fp = open_output_file(fn, rot->nstrout);
656 fprintf(fp, "# All angles given in degrees, time in ps\n");
657 for (g=0; g<rot->ngrp; g++)
659 rotg = &rot->grp[g];
660 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
661 fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, fit type %s\n",
662 g, erotg_names[rotg->eType], rotg->slab_dist, erotg_fitnames[rotg->eFittype]);
664 fprintf(fp, "# The following columns will have the syntax:\n");
665 fprintf(fp, "# ");
666 print_aligned_short(fp, "t");
667 print_aligned_short(fp, "grp");
668 print_aligned(fp, "theta_ref");
669 print_aligned(fp, "theta_fit");
670 print_aligned_short(fp, "slab");
671 print_aligned_short(fp, "atoms");
672 print_aligned(fp, "theta_fit");
673 print_aligned_short(fp, "slab");
674 print_aligned_short(fp, "atoms");
675 print_aligned(fp, "theta_fit");
676 fprintf(fp, " ...\n");
677 fflush(fp);
678 return fp;
682 /* Open torque output file and write some information about it's structure.
683 * Call on master only */
684 static FILE *open_torque_out(t_rot *rot, const char *fn)
686 FILE *fp;
687 int g;
688 t_rotgrp *rotg;
691 fp = open_output_file(fn, rot->nsttout);
693 for (g=0; g<rot->ngrp; g++)
695 rotg = &rot->grp[g];
696 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
698 fprintf(fp, "# Rotation group %d (%s), slab distance %f nm\n", g, erotg_names[rotg->eType], rotg->slab_dist);
699 fprintf(fp, "# The scalar tau is the torque [kJ/mol] in the direction of the rotation vector.\n");
700 fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
701 fprintf(fp, "# rot_vec%d %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
702 fprintf(fp, "#\n");
705 fprintf(fp, "# The following columns will have the syntax (tau=torque for that slab):\n");
706 fprintf(fp, "# ");
707 print_aligned_short(fp, "t");
708 print_aligned_short(fp, "grp");
709 print_aligned_short(fp, "slab");
710 print_aligned(fp, "tau");
711 print_aligned_short(fp, "slab");
712 print_aligned(fp, "tau");
713 fprintf(fp, " ...\n");
714 fflush(fp);
716 return fp;
720 /* Determine center of structure with coordinates x */
721 static void get_center(rvec x[], real weight[], int nat, rvec center)
723 int i;
724 rvec coord;
725 double weight_sum = 0.0;
728 /* Zero out the center of mass */
729 clear_rvec(center);
731 /* Loop over all atoms and add their weighted position vectors */
732 if (weight)
734 for (i=0; i<nat; i++)
736 weight_sum += weight[i];
737 svmul(weight[i], x[i], coord);
738 rvec_inc(center, coord);
741 /* Divide by the sum of weight */
742 svmul(1.0/weight_sum, center, center);
744 else
746 for (i=0; i<nat; i++)
747 rvec_inc(center, x[i]);
749 /* Divide by the number of atoms */
750 svmul(1.0/nat, center, center);
756 static void swap_val(double* vec, int i, int j)
758 double tmp = vec[j];
761 vec[j]=vec[i];
762 vec[i]=tmp;
766 static void swap_col(double **mat, int i, int j)
768 double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
771 mat[0][j]=mat[0][i];
772 mat[1][j]=mat[1][i];
773 mat[2][j]=mat[2][i];
775 mat[0][i]=tmp[0];
776 mat[1][i]=tmp[1];
777 mat[2][i]=tmp[2];
781 /* Eigenvectors are stored in columns of eigen_vec */
782 static void diagonalize_symmetric(
783 double **matrix,
784 double **eigen_vec,
785 double eigenval[3])
787 int n_rot;
790 jacobi(matrix,3,eigenval,eigen_vec,&n_rot);
792 /* sort in ascending order */
793 if (eigenval[0] > eigenval[1])
795 swap_val(eigenval, 0, 1);
796 swap_col(eigen_vec, 0, 1);
798 if (eigenval[1] > eigenval[2])
800 swap_val(eigenval, 1, 2);
801 swap_col(eigen_vec, 1, 2);
803 if (eigenval[0] > eigenval[1])
805 swap_val(eigenval, 0, 1);
806 swap_col(eigen_vec, 0, 1);
811 static void align_with_z(
812 rvec* s, /* Structure to align */
813 int natoms,
814 rvec axis)
816 int i, j, k;
817 rvec zet = {0.0, 0.0, 1.0};
818 rvec rot_axis={0.0, 0.0, 0.0};
819 rvec *rotated_str=NULL;
820 real ooanorm;
821 real angle;
822 matrix rotmat;
825 snew(rotated_str, natoms);
827 /* Normalize the axis */
828 ooanorm = 1.0/norm(axis);
829 svmul(ooanorm, axis, axis);
831 /* Calculate the angle for the fitting procedure */
832 cprod(axis, zet, rot_axis);
833 angle = acos(axis[2]);
834 if (angle < 0.0)
835 angle += M_PI;
837 /* Calculate the rotation matrix */
838 calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
840 /* Apply the rotation matrix to s */
841 for (i=0; i<natoms; i++)
843 for(j=0; j<3; j++)
845 for(k=0; k<3; k++)
847 rotated_str[i][j] += rotmat[j][k]*s[i][k];
852 /* Rewrite the rotated structure to s */
853 for(i=0; i<natoms; i++)
855 for(j=0; j<3; j++)
857 s[i][j]=rotated_str[i][j];
861 sfree(rotated_str);
865 static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
867 int i, j, k;
870 for (i=0; i<3; i++)
871 for (j=0; j<3; j++)
872 Rmat[i][j] = 0.0;
874 for (i=0; i<3; i++)
875 for (j=0; j<3; j++)
876 for (k=0; k<natoms; k++)
877 Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
881 static void weight_coords(rvec* str, real* weight, int natoms)
883 int i, j;
886 for(i=0; i<natoms; i++)
888 for(j=0; j<3; j++)
889 str[i][j] *= sqrt(weight[i]);
894 static void trans(rvec* x, int natoms, double* vec)
896 int i;
899 for(i=0; i<natoms; i++)
901 x[i][0] += vec[0];
902 x[i][1] += vec[1];
903 x[i][2] += vec[2];
908 static double opt_angle_analytic(
909 rvec* ref_s,
910 rvec* act_s,
911 real* weight,
912 int natoms,
913 rvec ref_com,
914 rvec act_com,
915 rvec axis)
917 int i, j, k;
918 rvec *ref_s_1=NULL;
919 rvec *act_s_1=NULL;
920 double shift[3];
921 double **Rmat, **RtR, **eigvec;
922 double eigval[3];
923 double V[3][3], WS[3][3];
924 double rot_matrix[3][3];
925 double opt_angle;
928 /* Do not change the original coordinates */
929 snew(ref_s_1, natoms);
930 snew(act_s_1, natoms);
931 for(i=0; i<natoms; i++)
933 copy_rvec(ref_s[i], ref_s_1[i]);
934 copy_rvec(act_s[i], act_s_1[i]);
937 /* Translate the structures to the origin */
938 for (i=0; i<3; i++)
939 shift[i] = (-1.0)*ref_com[i];
940 trans(ref_s_1, natoms, shift);
942 for (i=0; i<3; i++)
943 shift[i] = (-1.0)*act_com[i];
944 trans(act_s_1, natoms, shift);
946 /* Align rotation axis with z */
947 align_with_z(ref_s_1, natoms, axis);
948 align_with_z(act_s_1, natoms, axis);
950 /* Correlation matrix */
951 Rmat = allocate_square_matrix(3);
953 for (i=0; i<natoms; i++)
955 ref_s_1[i][2]=0.0;
956 act_s_1[i][2]=0.0;
959 /* Weight coordinates with sqrt(weight) */
960 if (weight)
962 weight_coords(ref_s_1, weight, natoms);
963 weight_coords(act_s_1, weight, natoms);
966 /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
967 calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
969 /* Calculate RtR */
970 RtR = allocate_square_matrix(3);
971 for (i=0; i<3; i++)
973 for (j=0; j<3; j++)
975 for (k=0; k<3; k++)
977 RtR[i][j] += Rmat[k][i] * Rmat[k][j];
981 /* Diagonalize RtR */
982 snew(eigvec,3);
983 for (i=0; i<3; i++)
984 snew(eigvec[i],3);
986 diagonalize_symmetric(RtR, eigvec, eigval);
987 swap_col(eigvec,0,1);
988 swap_col(eigvec,1,2);
989 swap_val(eigval,0,1);
990 swap_val(eigval,1,2);
992 /* Calculate V */
993 for(i=0; i<3; i++)
995 for(j=0; j<3; j++)
997 V[i][j] = 0.0;
998 WS[i][j] = 0.0;
1002 for (i=0; i<2; i++)
1003 for (j=0; j<2; j++)
1004 WS[i][j] = eigvec[i][j] / sqrt(eigval[j]);
1006 for (i=0; i<3; i++)
1008 for (j=0; j<3; j++)
1010 for (k=0; k<3; k++)
1012 V[i][j] += Rmat[i][k]*WS[k][j];
1016 free_square_matrix(Rmat, 3);
1018 /* Calculate optimal rotation matrix */
1019 for (i=0; i<3; i++)
1020 for (j=0; j<3; j++)
1021 rot_matrix[i][j] = 0.0;
1023 for (i=0; i<3; i++)
1025 for(j=0; j<3; j++)
1027 for(k=0; k<3; k++){
1028 rot_matrix[i][j] += eigvec[i][k]*V[j][k];
1032 rot_matrix[2][2] = 1.0;
1034 /* Determine the optimal rotation angle: */
1035 opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
1036 if (rot_matrix[0][1] < 0.0)
1037 opt_angle = (-1.0)*opt_angle;
1039 /* Give back some memory */
1040 free_square_matrix(RtR, 3);
1041 sfree(ref_s_1);
1042 sfree(act_s_1);
1043 for (i=0; i<3; i++)
1044 sfree(eigvec[i]);
1045 sfree(eigvec);
1047 return opt_angle;
1051 /* Determine actual angle of this slab by RMSD fit */
1052 /* Not parallelized, call this routine only on the master */
1053 static void flex_fit_angle(
1054 int g,
1055 t_rotgrp *rotg,
1056 double t,
1057 real degangle,
1058 FILE *fp)
1060 int i,l,n,islab,ind;
1061 rvec curr_x, ref_x;
1062 rvec *fitcoords=NULL;
1063 real gaussian;
1064 rvec act_center; /* Center of actual coordinates that are passed to the fit routine */
1065 rvec ref_center; /* Same for the reference coordinates */
1066 double fitangle; /* This will be the actual angle of the rotation group derived
1067 * from an RMSD fit to the reference structure at t=0 */
1068 gmx_slabdata_t sd;
1069 rvec coord;
1070 real scal;
1073 /**********************************/
1074 /* First collect the data we need */
1075 /**********************************/
1077 /* Clear number of relevant atoms in all slabs */
1078 for (l=0; l<2*rotg->slab_max_nr+1; l++)
1079 rotg->slab_data[l].nat = 0;
1081 /* Loop over ALL rotation group atoms in all slabs */
1082 for(l=0; l<rotg->nat; l++)
1084 for(n = -rotg->slab_first; n <= rotg->slab_last; n++)
1086 islab = n+rotg->slab_max_nr; /* slab index */
1087 /* Current coordinate of this atom: x[ii][XX/YY/ZZ] */
1088 copy_rvec(rotg->xc[l], curr_x);
1089 /* Calculate the Gaussian value of curr_slab for curr_x */
1090 gaussian = gaussian_weight(curr_x, rotg, n);
1091 /* Only do the calculation for this slab if the gaussian is large enough: */
1092 if(gaussian > rotg->min_gaussian)
1094 /* The (unrotated) reference coordinate of this atom is copied to ref_x: */
1095 copy_rvec(rotg->xc_ref[l], ref_x);
1096 /* Save data for doing angular RMSD fit later */
1097 sd = &(rotg->slab_data[islab]);
1098 ind = sd->nat;
1099 /* Save the current atom coordinate */
1100 copy_rvec(curr_x, sd->x[ind]);
1101 /* Save the corresponding reference coordinate */
1102 copy_rvec(ref_x , sd->ref[ind]);
1103 /* Save the weight for this atom in this slab */
1104 sd->weight[ind] = gaussian;
1105 /* Don't forget to keep track of the number of relevant atoms we found in this slab */
1106 sd->nat++;
1111 /* Get the center of all rotation group coordinates: */
1112 get_center(rotg->xc, NULL, rotg->nat, act_center);
1115 /******************************/
1116 /* Now do the fit calculation */
1117 /******************************/
1119 /* === Determine the optimal fit angle for the whole rotation group === */
1120 if (rotg->eFittype == erotgFitNORM)
1122 /* Normalize every coordinate to it's reference coordinate length
1123 * prior to performing the fit */
1124 for (i=0; i<rotg->nat; i++)
1126 /* First put center of coordinates into origin */
1127 rvec_sub(rotg->xc[i], act_center, coord);
1128 /* Determine the scaling factor for the coordinate: */
1129 scal = rotg->xc_ref_length[i] / norm(coord);
1130 /* Get coordinate, multiply with the scaling factor and save in buf[i] */
1131 svmul(scal, coord, rotg->xc_norm[i]);
1133 fitcoords = rotg->xc_norm;
1135 else
1137 fitcoords = rotg->xc;
1139 /* Note that from the point of view of the current coordinates, the reference has rotated backwards,
1140 * but we want to output the angle relative to the fixed reference, therefore the minus sign. */
1141 fitangle = -opt_angle_analytic(rotg->xc_ref, fitcoords, NULL, rotg->nat, rotg->xc_ref_center, act_center, rotg->vec);
1142 fprintf(fp, "%12.3e%6d%12.3f%12.3lf", t, g, degangle, fitangle);
1145 /* === Now do RMSD fitting for each slab === */
1146 /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
1147 #define SLAB_MIN_ATOMS 9
1149 for (n = -rotg->slab_first; n <= rotg->slab_last; n++)
1151 islab = n+rotg->slab_max_nr; /* slab index */
1152 sd = &(rotg->slab_data[islab]);
1153 if (sd->nat >= SLAB_MIN_ATOMS)
1155 /* Get the center of the slabs reference and current coordinates */
1156 get_center(sd->ref, sd->weight, sd->nat, ref_center);
1157 get_center(sd->x , sd->weight, sd->nat, act_center);
1158 if (rotg->eFittype == erotgFitNORM)
1160 /* Normalize every coordinate to it's reference coordinate length
1161 * prior to performing the fit */
1162 for (i=0; i<sd->nat;i++) /* Center */
1164 rvec_dec(sd->ref[i], ref_center);
1165 rvec_dec(sd->x[i] , act_center);
1166 /* Normalize x_i such that it gets the same length as ref_i */
1167 svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
1169 /* We already subtracted the centers */
1170 clear_rvec(ref_center);
1171 clear_rvec(act_center);
1173 fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat, ref_center, act_center, rotg->vec);
1174 fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
1177 fprintf(fp , "\n");
1179 #undef SLAB_MIN_ATOMS
1183 /* Get the shifts such that each atom is within closest
1184 * distance to its position at the last NS time step after shifting.
1185 * If we start with a whole structure, and always keep track of
1186 * shift changes, the structure will stay whole this way */
1187 static void get_extra_shifts(
1188 t_rotgrp *rotg,
1189 int npbcdim,
1190 matrix box,
1191 rvec *xc)
1193 int i,m,d;
1194 rvec dx;
1197 for (i=0; i < rotg->nat; i++)
1198 clear_ivec(rotg->xc_eshifts[i]);
1200 for (i=0; i<rotg->nat; i++)
1202 /* The distance this atom moved since the last time step */
1203 /* If this is more than just a bit, it has changed its home pbc box */
1204 rvec_sub(xc[i],rotg->xc_old[i],dx);
1206 for(m=npbcdim-1; m>=0; m--)
1208 while (dx[m] < -0.5*box[m][m])
1210 for(d=0; d<DIM; d++)
1211 dx[d] += box[m][d];
1212 rotg->xc_eshifts[i][m]++;
1214 while (dx[m] >= 0.5*box[m][m])
1216 for(d=0; d<DIM; d++)
1217 dx[d] -= box[m][d];
1218 rotg->xc_eshifts[i][m]--;
1225 /* Assemble the coordinates such that every node has all of them */
1226 static void get_coordinates(
1227 t_commrec *cr,
1228 t_rotgrp *rotg,
1229 rvec x[],
1230 bool bNeedShiftsUpdate, /* NS step, the shifts have changed */
1231 matrix box)
1233 int i, ii, j, l;
1236 GMX_MPE_LOG(ev_get_coords_start);
1238 /* Zero out the collective coordinate array */
1239 clear_rvecs(rotg->nat, rotg->xc);
1241 /* Put the local coordinates that this node has into the right place of
1242 * the collective array. */
1243 for (l=0; l<rotg->nat_loc; l++)
1245 /* Local index of a rotation group atom */
1246 ii = rotg->ind_loc[l];
1247 /* Index of this rotation group atom w.r.t. the whole rotation group */
1248 j = rotg->xc_ref_ind[l];
1249 /* Sort the current x-coordinates into the right place of the array: */
1250 copy_rvec(x[ii], rotg->xc[j]);
1252 /* Add the arrays from all nodes together */
1253 if (PAR(cr))
1254 gmx_sum(DIM*rotg->nat, rotg->xc[0], cr);
1256 /* To make the molecule whole, start with a whole structure and each
1257 * step move the assembled coordinates at closest distance to the positions
1258 * from the last step. First shift the coordinates with the saved shift
1259 * vectors (these are 0 when this routine is called for the first time!) */
1260 ed_shift_coords(box, rotg->xc, rotg->xc_shifts, rotg->nat);
1262 /* Now check if some shifts changed since the last step.
1263 * This only needs to be done when the shifts are expected to have changed,
1264 * i.e. after neighboursearching */
1265 if (bNeedShiftsUpdate)
1267 get_extra_shifts(rotg, 3, box, rotg->xc);
1269 /* Shift with the additional shifts such that we get a whole molecule now */
1270 ed_shift_coords(box, rotg->xc, rotg->xc_eshifts, rotg->nat);
1272 /* Add the shift vectors together for the next time step */
1273 for (i=0; i<rotg->nat; i++)
1275 rotg->xc_shifts[i][XX] += rotg->xc_eshifts[i][XX];
1276 rotg->xc_shifts[i][YY] += rotg->xc_eshifts[i][YY];
1277 rotg->xc_shifts[i][ZZ] += rotg->xc_eshifts[i][ZZ];
1280 /* Store current correctly-shifted coordinates for comparison in the next NS time step */
1281 for (i=0; i<rotg->nat; i++)
1282 copy_rvec(rotg->xc[i],rotg->xc_old[i]);
1285 GMX_MPE_LOG(ev_get_coords_finish);
1289 static inline void shift_single_coord(matrix box, rvec x, const ivec is)
1291 int tx,ty,tz;
1294 tx=is[XX];
1295 ty=is[YY];
1296 tz=is[ZZ];
1298 if(TRICLINIC(box))
1300 x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
1301 x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
1302 x[ZZ] += tz*box[ZZ][ZZ];
1303 } else
1305 x[XX] += tx*box[XX][XX];
1306 x[YY] += ty*box[YY][YY];
1307 x[ZZ] += tz*box[ZZ][ZZ];
1312 static real do_flex2_lowlevel(
1313 t_rotgrp *rotg,
1314 matrix rotmat,
1315 real sigma, /* The Gaussian width sigma */
1316 rvec x[],
1317 bool bCalcTorque,
1318 matrix box,
1319 t_commrec *cr)
1321 int i,ii,l,m,n,islab,ipgrp;
1322 rvec dr; /* difference vector between actual and reference coordinate */
1323 real V; /* The rotation potential energy */
1324 real gaussian; /* Gaussian weight */
1325 real gaussian_xi;
1326 real beta;
1327 rvec curr_x; /* particle coordinate */
1328 rvec xi;
1329 rvec curr_x_rel; /* particle coordinate relative to COG */
1330 rvec curr_COG; /* the current slab's center of geometry (COG) */
1331 rvec ref_x, ref_x_cpy; /* the reference particle coordinate */
1332 rvec ref_COG; /* the reference slab's COG */
1333 rvec r, yi, tmp;
1334 rvec force_n; /* Single force from slab n on one atom */
1335 rvec s_ii;
1336 real inv_norm_ii;
1337 real sdotr_ii; /* s_ii.r_ii */
1338 real tmp_s;
1339 rvec tmp_v, tmp_n1_v, tmp_n2_v, tmp_n3_v, tmp_n4_v;
1340 rvec sum_i1;
1341 real sum_i2;
1342 rvec s_i;
1343 real inv_norm_i;
1344 real sdotr_i; /* s_i.r_i */
1345 real gauss_ratio; /* g_n(x_ii)/Sum_i(g_n(x_i)*/
1346 real beta_sigma; /* beta/sigma^2 */
1347 rvec sum_f_ii; /* force on one atom summed over all slabs */
1350 /********************************************************/
1351 /* Main loop over all local atoms of the rotation group */
1352 /********************************************************/
1353 V = 0.0;
1354 for (l=0; l<rotg->nat_loc; l++)
1356 /* Local index of a rotation group atom */
1357 ii = rotg->ind_loc[l];
1358 /* Index of this rotation group atom with respect to the whole rotation group */
1359 ipgrp = rotg->xc_ref_ind[l];
1361 /* Current coordinate of this atom: x[ii][XX/YY/ZZ] */
1362 copy_rvec(x[ii], curr_x);
1363 /* Shift this atom such that it is near its reference */
1364 shift_single_coord(box, curr_x, rotg->xc_shifts[ipgrp]);
1366 /* For each atom, loop over all slabs. We could have contributions from any slab */
1367 clear_rvec(sum_f_ii);
1368 for (n = -rotg->slab_first; n <= rotg->slab_last; n++)
1370 /* Calculate the Gaussian value of curr_slab for curr_x */
1371 gaussian = gaussian_weight(curr_x, rotg, n);
1373 /* Only do the calculation for this slab if the Gaussian is large enough: */
1374 if (gaussian > rotg->min_gaussian)
1376 islab = n+rotg->slab_max_nr; /* slab index */
1378 /* The (unrotated) reference coordinate of this atom is copied to ref_x: */
1379 copy_rvec(rotg->xc_ref[ipgrp], ref_x);
1380 beta = calc_beta(curr_x, rotg,n);
1381 /* The center of geometry (COG) of this slab is copied to curr_COG: */
1382 copy_rvec(rotg->slab_center[islab], curr_COG);
1383 /* The reference COG of this slab is copied to ref_COG: */
1384 copy_rvec(rotg->slab_center_ref[islab], ref_COG);
1386 /* Rotate the reference coordinate around the rotation axis through this slab's reference COG */
1387 /* 1. Subtract the reference slab COG from the reference coordinate i */
1388 rvec_sub(ref_x, ref_COG, ref_x); /* ref_x =y_ii-y_0 */
1389 /* 2. Rotate reference coordinate around origin: */
1390 copy_rvec(ref_x, ref_x_cpy);
1391 mvmul(rotmat, ref_x_cpy, ref_x); /* ref_x = r_ii = Omega.(y_ii-y_0) */
1393 /* Now subtract the slab COG from current coordinate i */
1394 rvec_sub(curr_x, curr_COG, curr_x_rel); /* curr_x_rel = x_ii-x_0 */
1396 /* Force on atom i is based on difference vector between current coordinate and rotated reference coordinate */
1397 /* Difference vector between current and reference coordinate: */
1398 rvec_sub(curr_x_rel, ref_x, dr); /* dr=(x_ii-x_0)-Omega.(y_ii-y_0) */
1400 cprod(rotg->vec, curr_x_rel, s_ii); /* s_ii = v x (x_ii-x_0) */
1401 inv_norm_ii=1.0/norm(s_ii); /* inv_norm_ii = 1/|v x (x_ii-x_0)| */
1402 unitv(s_ii, s_ii); /* s_ii = v x (x_ii-x_0)/|v x (x_ii-x_0)| */
1403 sdotr_ii=iprod(s_ii,ref_x); /* sdotr_ii = ((v x (x_ii-x_0)/|v x (x_ii-x_0)|).Omega.(y_ii-y_0) */
1405 /*********************************/
1406 /* Add to the rotation potential */
1407 /*********************************/
1408 V += 0.5*rotg->k*gaussian*sqr(sdotr_ii);
1410 tmp_s=gaussian*sdotr_ii*inv_norm_ii;
1411 svmul(sdotr_ii, s_ii, tmp_n1_v);
1412 rvec_sub(ref_x, tmp_n1_v, tmp_n1_v);
1413 svmul(tmp_s, tmp_n1_v, tmp_n1_v);
1415 clear_rvec(sum_i1);
1416 sum_i2=0.0;
1418 for(i=0; i<rotg->nat; i++)
1420 /* Coordinate xi of this atom */
1421 copy_rvec(rotg->xc[i],xi);
1423 gaussian_xi = gaussian_weight(xi,rotg,n); /* g_n(xi)*/
1424 if (gaussian_xi > rotg->min_gaussian)
1426 /* Calculate r_i for atom i and slab n: */
1427 /* Unrotated reference coordinate y_i: */
1428 copy_rvec(rotg->xc_ref[i],yi);
1430 /* COG y0 for this slab: */
1431 /* The reference COG of this slab is still in ref_COG */
1432 rvec_sub(yi, ref_COG, tmp); /* tmp = y_i - y_0 */
1433 mvmul(rotmat, tmp, r); /* r = Omega*(y_i - y_0) */
1434 rvec_sub(xi, curr_COG, tmp); /* tmp = (x_i - x_0) */
1435 cprod(rotg->vec, tmp, s_i); /* s_i = v x (x_i - x_0) */
1436 inv_norm_i=1.0/norm(s_i); /* 1/|v x (x_i - x_0)| */
1437 unitv(s_i, s_i); /* s_i = (v x (x_i-x_0))/|v x (x_i-x_0)| */
1438 sdotr_i=iprod(s_i,r); /* sdotr_i = (s_i.r_i) */
1440 tmp_s=gaussian_xi*sdotr_i*inv_norm_i; /* tmp_s = g_n(xi)*(s_i.r_i)*1/norm */
1441 /* sum_11 */
1442 svmul(sdotr_i, s_i, tmp_v);
1443 rvec_sub(tmp_v, r, tmp_v);
1444 svmul(tmp_s, tmp_v, tmp_v); /* tmp_v = g_n(xi)*(s_i.r_i)*1/norm *(-r_i+(r_i.s_i)s_i) */
1445 rvec_add(sum_i1, tmp_v, sum_i1); /* n2 */
1446 sum_i2 += tmp_s*(iprod(r,s_ii)-(iprod(s_i,s_ii)*sdotr_i)); /* n3 */
1448 } /* now we have the sum-over-atoms (over i) for the ii-th atom in the n-th slab */
1450 gauss_ratio=gaussian/rotg->slab_weights[islab];
1451 beta_sigma=beta/sqr(sigma);
1452 svmul(gauss_ratio, sum_i1, tmp_n2_v); /* tmp_n2_v = gauss_ratio_ii*Sum_i(g_n(xi)*(s_i.r_i)*1/norm *(-r_i+(r_i.s_i)s_i)) */
1454 rvec_add(tmp_n1_v, tmp_n2_v, force_n); /* adding up the terms perpendicular to v and to x_ii-x0 */
1455 cprod(force_n, rotg->vec, tmp_v); /* now these are indeed perpendicular */
1456 svmul((-1.0*rotg->k), tmp_v, force_n); /* multiplying by -k* we've got the final tangent contribution */
1457 /* calculating the parallel contribution */
1458 svmul((-1.0)*rotg->k*gauss_ratio*beta_sigma*sum_i2,rotg->vec,tmp_n3_v);
1459 svmul(0.5*rotg->k*beta_sigma*gaussian*sqr(sdotr_ii),rotg->vec,tmp_n4_v);
1460 rvec_add(tmp_n3_v, tmp_n4_v, tmp_n3_v); /* tmp_n3_v contains the final parallel contribution */
1461 rvec_add(tmp_n3_v, force_n, force_n); /* force_n is the total force from slab n */
1462 /* sum the forces over slabs */
1463 rvec_add(sum_f_ii,force_n,sum_f_ii);
1465 /* Calculate the torque: */
1466 if (bCalcTorque)
1468 /* The force on atom ii from slab n only: */
1469 rotg->slab_torque_v[islab] += torque(rotg->vec, force_n, curr_x, curr_COG);
1471 } /* END of "if gaussian > pg->min_gaussian" */
1472 } /* END of loop over slabs */
1474 /* Store the additional force so that it can be added to the force
1475 * array after the normal forces have been evaluated */
1476 for (m=0; m<DIM; m++)
1477 rotg->f_rot_loc[l][m] = sum_f_ii[m];
1479 #ifdef INFOF
1480 fprintf(stderr," FORCE on ATOM %d/%d = (%15.8f %15.8f %15.8f) \n",
1481 l,ii,rotg->sum_f_ii[XX], rotg->sum_f_ii[YY], rotg->sum_f_ii[ZZ]5);
1482 #endif
1483 } /* END of loop over local atoms */
1485 return V;
1489 static real do_flex_lowlevel(
1490 t_rotgrp *rotg,
1491 matrix rotmat,
1492 real sigma, /* The Gaussian width sigma */
1493 rvec x[],
1494 bool bCalcTorque,
1495 matrix box,
1496 t_commrec *cr)
1498 int i,ii,iii,l,m,n,islab,ipgrp;
1499 rvec direction; /* the direction for the force on atom i */
1500 rvec sum_n1,sum_n2; /* Two contributions to the rotation force */
1501 rvec sum_i; /* Inner part of sum_n2 */
1502 rvec dummy1,tmp_f,s,tmp2;
1503 real u; /* direction*dr */
1504 real V; /* The rotation potential energy */
1505 real gaussian; /* Gaussian weight */
1506 real gaussian_xi;
1507 real fac,beta;
1508 rvec curr_x; /* particle coordinate */
1509 rvec xi;
1510 rvec curr_x_rel; /* particle coordinate relative to COG */
1511 rvec curr_COG; /* the current slab's center of geometry (COG) */
1512 rvec ref_x, ref_x_cpy; /* the reference particle coordinate */
1513 rvec ref_COG; /* the reference slab's COG */
1514 rvec r, yi, tmp;
1515 rvec force_n; /* Single force from slab n on one atom */
1518 /********************************************************/
1519 /* Main loop over all local atoms of the rotation group */
1520 /********************************************************/
1521 V = 0.0;
1522 for (l=0; l<rotg->nat_loc; l++)
1524 /* Local index of a rotation group atom */
1525 ii = rotg->ind_loc[l];
1526 /* Index of this rotation group atom with respect to the whole rotation group */
1527 ipgrp = rotg->xc_ref_ind[l];
1529 /* Current coordinate of this atom: x[ii][XX/YY/ZZ] */
1530 copy_rvec(x[ii], curr_x);
1531 /* Shift this atom such that it is near its reference */
1532 shift_single_coord(box, curr_x, rotg->xc_shifts[ipgrp]);
1534 /* For each atom, loop over all slabs. We could have contributions from any slab */
1535 clear_rvec(sum_n1);
1536 clear_rvec(sum_n2);
1537 for (n = -rotg->slab_first; n <= rotg->slab_last; n++)
1539 /* Calculate the Gaussian value of curr_slab for curr_x */
1540 gaussian = gaussian_weight(curr_x, rotg, n);
1542 /* Only do the calculation for this slab if the Gaussian is large enough: */
1543 if (gaussian > rotg->min_gaussian)
1545 islab = n+rotg->slab_max_nr; /* slab index */
1547 /* The (unrotated) reference coordinate of this atom is copied to ref_x: */
1548 copy_rvec(rotg->xc_ref[ipgrp], ref_x);
1549 beta = calc_beta(curr_x, rotg,n);
1550 /* The center of geometry (COG) of this slab is copied to curr_COG: */
1551 copy_rvec(rotg->slab_center[islab], curr_COG);
1552 /* The reference COG of this slab is copied to ref_COG: */
1553 copy_rvec(rotg->slab_center_ref[islab], ref_COG);
1555 /* Rotate the reference coordinate around the rotation axis through this slab's reference COG */
1556 /* 1. Subtract the reference slab COG from the reference coordinate i */
1557 rvec_sub(ref_x, ref_COG, ref_x); /* ref_x =y_ii-y_0 */
1558 /* 2. Rotate reference coordinate around origin: */
1559 copy_rvec(ref_x, ref_x_cpy);
1560 mvmul(rotmat, ref_x_cpy, ref_x); /* ref_x = r_ii = Omega.(y_ii-y_0) */
1562 /* Now subtract the slab COG from current coordinate i */
1563 rvec_sub(curr_x, curr_COG, curr_x_rel); /* curr_x_rel = x_ii-x_0 */
1565 /* Calculate the direction of the actual force */
1566 cprod(rotg->vec, ref_x, direction);
1567 unitv(direction,direction);
1568 u = iprod(direction, curr_x_rel);
1570 /*********************************/
1571 /* Add to the rotation potential */
1572 /*********************************/
1573 V += 0.5*rotg->k*gaussian*sqr(u);
1575 /*************************************************/
1576 /* sum_n1 is the main contribution to the force: */
1577 /*************************************************/
1578 /* gn*u*(vec_s - 0.5*u*beta/(sigma^2)*vec_a) */
1579 svmul(0.5*u*beta/sqr(sigma),rotg->vec,dummy1);
1580 /* Typically 'direction' will be the largest part */
1581 rvec_sub(direction,dummy1,dummy1);
1582 svmul(gaussian*u,dummy1,dummy1);
1583 /* Sum over n: */
1584 rvec_add(sum_n1,dummy1,sum_n1);
1586 /*************************************************************/
1587 /* For the term sum_n2 we need to loop over all atoms again: */
1588 /*************************************************************/
1589 clear_rvec(sum_i);
1590 for (i=0; i<rotg->nat; i++)
1592 /* Index of a rotation group atom */
1593 iii = rotg->ind[i];
1595 /* Coordinate xi of this atom */
1596 copy_rvec(rotg->xc[i],xi);
1598 gaussian_xi = gaussian_weight(xi,rotg,n);
1600 if (gaussian_xi > rotg->min_gaussian)
1602 /* Calculate r=Omega*(y_i-y_0) for atom i and slab n: */
1603 /* Unrotated reference coordinate y_i: */
1604 copy_rvec(rotg->xc_ref[i],yi);
1606 /* COG y0 for this slab: */
1607 /* The reference COG of this slab is still in ref_COG */
1608 rvec_sub(yi, ref_COG, tmp); /* tmp = y_i - y_0 */
1609 mvmul(rotmat, tmp, r); /* r = Omega*(y_i - y_0) */
1610 cprod(rotg->vec, r, tmp); /* tmp = v x Omega*(y_i - y_0) */
1611 unitv(tmp, s); /* s = v x Omega*(y_i - y_0) / |s x Omega*(y_i - y_0)| */
1612 /* Now that we have si, let's calculate the i-sum: */
1613 /* tmp = x_0 - x_l */
1614 rvec_sub(curr_COG, curr_x, tmp);
1615 /* tmp2 = beta/sigma^2 * (s*(x_0 - x_l)) * v */
1616 svmul(beta*iprod(tmp, s)/sqr(sigma), rotg->vec, tmp2);
1617 /* tmp = s + tmp2 */
1618 rvec_add(tmp2, s, tmp);
1619 /* tmp2 = xi - x0 */
1620 rvec_sub(xi, curr_COG, tmp2);
1621 /* fac = gn * s*(xi - x0 - ri) */
1622 fac = gaussian_xi*iprod(s, tmp2);
1623 /* tmp2 = gn * s*(xi - x0) * [beta/sigma^2 * (s*(x_0 - x_l)) * v] */
1624 svmul(fac, tmp, tmp2);
1625 rvec_add(sum_i, tmp2, sum_i);
1627 } /* now we have the i-sum for this atom in this slab */
1628 svmul(gaussian/rotg->slab_weights[islab], sum_i, sum_i);
1629 rvec_add(sum_n2, sum_i, sum_n2);
1631 /* Calculate the torque: */
1632 if (bCalcTorque)
1634 /* The force on atom ii from slab n only: */
1635 rvec_sub(sum_i, dummy1, force_n);
1636 svmul(rotg->k, force_n, force_n);
1637 rotg->slab_torque_v[islab] += torque(rotg->vec, force_n, curr_x, curr_COG);
1639 } /* END of "if gaussian > pg->min_gaussian" */
1640 } /* END of loop over slabs */
1642 /* Put both contributions together: */
1643 rvec_sub(sum_n2,sum_n1,tmp_f); /* F = -grad V */
1645 /* Store the additional force so that it can be added to the force
1646 * array after the normal forces have been evaluated */
1647 for(m=0; m<DIM; m++)
1648 rotg->f_rot_loc[l][m] = rotg->k*tmp_f[m];
1650 #ifdef INFOF
1651 static bool bFirst=1;
1652 char buf[255];
1653 static FILE *fp;
1654 if (bFirst)
1656 sprintf(buf, "forces%d.txt", cr->nodeid);
1657 fp = fopen(buf, "w");
1658 bFirst = 0;
1661 fprintf(fp," FORCE on ATOM %d/%d = (%15.8f %15.8f %15.8f) 1: %15.8f %15.8f %15.8f 2: %15.8f %15.8f %15.8f\n",
1662 l,ii,rotg->k*tmp_f[XX], rotg->k*tmp_f[YY], rotg->k*tmp_f[ZZ],
1663 -rotg->k*sum_n1[XX], -rotg->k*sum_n1[YY], -rotg->k*sum_n1[ZZ],
1664 rotg->k*sum_n2[XX], rotg->k*sum_n2[YY], rotg->k*sum_n2[ZZ]);
1665 #endif
1666 } /* END of loop over local atoms */
1668 return V;
1672 #ifdef PRINT_COORDS
1673 static void print_coordinates(t_commrec *cr, t_rotgrp *rotg, rvec x[], matrix box, int step)
1675 int i;
1676 static FILE *fp;
1677 static char buf[STRLEN];
1678 static bool bFirst=1;
1681 if (bFirst)
1683 sprintf(buf, "coords%d.txt", cr->nodeid);
1684 fp = fopen(buf, "w");
1685 bFirst = 0;
1688 fprintf(fp, "\nStep %d\n", step);
1689 fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
1690 box[XX][XX], box[XX][YY], box[XX][ZZ],
1691 box[YY][XX], box[YY][YY], box[YY][ZZ],
1692 box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
1693 for (i=0; i<rotg->nat; i++)
1695 fprintf(fp, "%4d %f %f %f\n", i,
1696 rotg->xc[i][XX], rotg->xc[i][YY], rotg->xc[i][ZZ]);
1698 fflush(fp);
1701 #endif
1704 /* Enforced rotation with a flexible axis */
1705 static void do_flexible(
1706 t_commrec *cr,
1707 t_rotgrp *rotg, /* The rotation group */
1708 int g, /* Group number */
1709 real degangle, /* Angle theta_ref [degrees] */
1710 matrix rotmat, /* Rotation matrix for this angle */
1711 rvec x[], /* The local coordinates */
1712 matrix box,
1713 double t, /* Time in picoseconds */
1714 bool bDynBox, /* Is the box dynamic? */
1715 int step, /* The time step */
1716 bool bOutstep,
1717 FILE *fp_slabs,
1718 FILE *fp_torque,
1719 FILE *fp_angles)
1721 int l;
1722 real sigma; /* The Gaussian width sigma */
1725 /* Define the sigma value */
1726 sigma = 0.7*rotg->slab_dist;
1728 /* Determine the gaussian-weighted center of coordinates for all slabs,
1729 * also reallocate memory if the number of slabs grows (i.e. box expands) */
1730 get_slab_centers(rotg,rotg->xc,box,cr,g,bDynBox,t,fp_slabs,bOutstep,FALSE);
1732 /* Clear the torque per slab from last time step: */
1733 for (l=0; l<2*rotg->slab_max_nr+1; l++)
1734 rotg->slab_torque_v[l] = 0.0;
1736 /* Call the rotational forces kernel */
1737 GMX_MPE_LOG(ev_flexll_start);
1738 if (rotg->eType == erotgFLEX1)
1739 rotg->V = do_flex_lowlevel(rotg, rotmat, sigma, x, bOutstep, box, cr);
1740 else if (rotg->eType == erotgFLEX2)
1741 rotg->V = do_flex2_lowlevel(rotg, rotmat, sigma, x, bOutstep, box, cr);
1742 else
1743 gmx_fatal(FARGS, "Unknown flexible rotation type");
1744 GMX_MPE_LOG(ev_flexll_finish);
1746 /* Determine actual angle of this slab by RMSD fit and output to file - Let's hope */
1747 /* this only happens once in a while, since this is not parallelized! */
1748 if (bOutstep && MASTER(cr))
1749 flex_fit_angle(g, rotg, t, degangle, fp_angles);
1753 /* Calculate the angle between reference and actual rotation group atom: */
1754 static void angle(t_rotgrp *rotg,
1755 rvec x_act,
1756 rvec x_ref,
1757 real *alpha,
1758 real *weight, /* atoms near the rotation axis should count less than atoms far away */
1759 rvec offset)
1761 rvec x , xr ; /* actual and reference coordinate in new coordinate system */
1762 rvec xp, xrp; /* dito, but projected on a plane perpendicular to pg->vec */
1763 real normxp; /* saved for efficiency reasons */
1764 rvec dum;
1765 real cosalpha; /* cos of angle between projected reference and actual coordinate */
1766 int sign;
1769 /* Move the center of coordinates to rot_offset: */
1770 rvec_sub(x_act, offset, x);
1771 rvec_sub(x_ref, offset, xr);
1773 /* Project xr and x into a plane through the origin perpendicular to rot_vec: */
1774 /* Project xr: xrp = xr - (vec * xr) * vec */
1775 svmul(iprod(rotg->vec, xr), rotg->vec, dum);
1776 rvec_sub(xr, dum, xrp);
1777 /* Project x: */
1778 svmul(iprod(rotg->vec, x), rotg->vec, dum);
1779 rvec_sub(x, dum, xp);
1781 /* Calculate the angle between the projected coordinates: */
1782 normxp = norm(xp); /* save for later use */
1783 cosalpha = iprod(xrp, xp) / (norm(xrp)*normxp);
1784 if (cosalpha < -1.0) cosalpha = -1.0;
1785 if (cosalpha > 1.0) cosalpha = 1.0;
1787 /* Retrieve some information about which vector precedes */
1788 cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
1790 if (iprod(rotg->vec, dum) >= 0)
1791 /* This will be the case when the reference group runs ahead. Thus the sign for the
1792 * angle of the actual group (which we are interested in) is negative = behind */
1793 sign = -1;
1794 else
1795 sign = 1;
1797 /* Return the angle in radians */
1798 *alpha = sign * acos(cosalpha);
1799 /* Also return the weight */
1800 *weight = normxp;
1804 /* Project first vector onto a plane perpendicular to the second vector
1805 * dr = dr - (dr.v)v
1807 static inline void project_onto_plane(rvec dr, const rvec v)
1809 rvec tmp;
1812 svmul(iprod(dr,v),v,tmp); /* tmp = (dr.v)v */
1813 rvec_dec(dr, tmp); /* dr = dr - (dr.v)v */
1817 /* Fixed rotation: The rotation reference group rotates around an axis */
1818 /* The atoms of the actual rotation group are attached with imaginary */
1819 /* springs to the reference atoms. */
1820 static void do_fixed(
1821 t_commrec *cr,
1822 t_rotgrp *rotg, /* The rotation group */
1823 matrix rotmat, /* rotary matrix */
1824 rvec x[], /* The coordinates (natoms) */
1825 t_pbc *pbc,
1826 double t, /* Time in picoseconds */
1827 int step, /* The time step */
1828 bool bTorque)
1830 int i,ii,m,iigrp;
1831 rvec dr;
1832 rvec curr_x; /* particle coordinate */
1833 rvec curr_x_pbc; /* particle coordinate with the right pbc representation
1834 * w.r.t. the reference coordinate xr */
1835 rvec tmp_f; /* Force */
1836 rvec xr, xrcpy; /* rotated (reference) particle coordinate */
1837 real alpha; /* a single angle between an actual and a reference coordinate */
1838 real weight; /* single weight for a single angle */
1841 /* Clear values from last time step */
1842 rotg->V = 0.0;
1843 rotg->fix_torque_v = 0.0;
1844 rotg->fix_angles_v = 0.0;
1845 rotg->fix_weight_v = 0.0;
1847 /* Loop over all local atoms of the rotation group */
1848 for (i=0; i<rotg->nat_loc; i++)
1850 /* Index of a rotation group atom */
1851 ii = rotg->ind_loc[i];
1852 /* Actual coordinate of this atom: x[ii][XX/YY/ZZ] */
1853 copy_rvec(x[ii], curr_x);
1855 /* Index of this rotation group atom with respect to the whole rotation group */
1856 iigrp = rotg->xc_ref_ind[i];
1858 /* Copy the (unrotated) reference coordinate of this atom: */
1859 copy_rvec(rotg->xc_ref[iigrp], xr);
1860 /* Rotate this atom around dislocated rotation axis: */
1861 /* Move rotation axis, so that it runs through the origin: */
1862 rvec_sub(xr, rotg->offset, xr);
1863 /* Rotate around the origin: */
1864 copy_rvec(xr, xrcpy);
1865 mvmul(rotmat, xrcpy, xr);
1866 /* And move back: */
1867 rvec_add(xr, rotg->offset, xr);
1868 /* Difference vector between reference and actual coordinate: */
1869 pbc_dx(pbc,xr,curr_x, dr);
1871 /* The reference coords are whole, therefore we can construct the
1872 * needed pbc image of curr_x from xr and dr: */
1873 rvec_sub(xr, dr, curr_x_pbc);
1875 if (rotg->eType==erotgFIXED_PLANE)
1876 project_onto_plane(dr, rotg->vec);
1878 /* Store the additional force so that it can be added to the force
1879 * array after the normal forces have been evaluated */
1880 for (m=0; m<DIM; m++)
1882 tmp_f[m] = rotg->k*dr[m];
1883 rotg->f_rot_loc[i][m] = tmp_f[m];
1884 rotg->V += 0.5*rotg->k*sqr(dr[m]);
1887 if (bTorque)
1889 /* Add to the torque of this rotation group */
1890 rotg->fix_torque_v += torque(rotg->vec, tmp_f, curr_x_pbc, rotg->offset);
1892 /* Calculate the angle between reference and actual rotation group atom: */
1893 angle(rotg, curr_x_pbc, xr, &alpha, &weight, rotg->offset); /* angle in rad, weighted */
1894 rotg->fix_angles_v += alpha * weight;
1895 rotg->fix_weight_v += weight;
1896 /* Use the next two lines instead if you don't want weighting: */
1898 angles_v[g] += alpha;
1899 weight_v[g] += 1;
1902 /* Probably one does not want enforced rotation to influence */
1903 /* the virial. But if so, activate the following lines */
1905 if (MASTER(cr))
1907 Add the rotation contribution to the virial
1908 for(j=0; j<DIM; j++)
1909 for(m=0;m<DIM;m++)
1910 vir[j][m] += 0.5*f[ii][j]*dr[m];
1913 #ifdef INFOF
1914 fprintf(stderr," FORCE on ATOM %d = (%15.8f %15.8f %15.8f) torque=%15.8f\n",
1915 ii,rotg->f_rot_loc[i][XX], rotg->f_rot_loc[i][YY], rotg->f_rot_loc[i][ZZ],rotg->fix_torque_v);
1916 #endif
1917 } /* end of loop over local rotation group atoms */
1921 /* Fixed rotation, subtype follow_plane: Similar to fixed_plane, however
1922 * the centers of mass of the reference and current group are subtracted
1923 * from reference and current coordinates, respectively. This way the rotation
1924 * group can move around in the box and does not stick to its reference location */
1925 static void do_follow_plane(
1926 t_commrec *cr,
1927 t_rotgrp *rotg, /* The rotation group */
1928 matrix rotmat, /* rotary matrix */
1929 rvec x[], /* The coordinates (natoms) */
1930 matrix box,
1931 double t, /* Time in picoseconds */
1932 int step, /* The time step */
1933 bool bTorque)
1935 int l,ii,m,iigrp;
1936 rvec dr;
1937 rvec curr_x; /* particle coordinate */
1938 rvec tmp_f; /* Force */
1939 rvec xr, xrcpy; /* rotated (reference) particle coordinate */
1940 real alpha; /* a single angle between an actual and a reference coordinate */
1941 real weight; /* single weight for a single angle */
1942 rvec zerovec;
1943 clear_rvec(zerovec);
1946 /* Clear values from last time step */
1947 rotg->V = 0.0;
1948 rotg->fix_torque_v = 0.0;
1949 rotg->fix_angles_v = 0.0;
1950 rotg->fix_weight_v = 0.0;
1952 /* Loop over all local atoms of the rotation group */
1953 for (l=0; l<rotg->nat_loc; l++)
1955 /* Index of a rotation group atom */
1956 ii = rotg->ind_loc[l];
1958 /* Index of this rotation group atom with respect to the whole rotation group */
1959 iigrp = rotg->xc_ref_ind[l];
1961 /* Actual coordinate of this atom: x[ii][XX/YY/ZZ] */
1962 copy_rvec(x[ii], curr_x);
1964 /* Shift this atom such that it is near its reference */
1965 shift_single_coord(box, curr_x, rotg->xc_shifts[iigrp]);
1967 /* Subtract center of mass */
1968 rvec_sub(curr_x, rotg->offset, curr_x);
1970 /* Copy the (unrotated) reference coordinate of this atom: */
1971 rvec_sub(rotg->xc_ref[iigrp], rotg->xc_ref_center, xr);
1973 /* Rotate this atom around COM: */
1974 copy_rvec(xr, xrcpy);
1975 mvmul(rotmat, xrcpy, xr);
1976 /* Difference vector between reference and actual coordinate: */
1977 rvec_sub(xr, curr_x, dr);
1979 project_onto_plane(dr, rotg->vec);
1981 /* Store the additional force so that it can be added to the force
1982 * array after the normal forces have been evaluated */
1983 for (m=0; m<DIM; m++)
1985 tmp_f[m] = rotg->k*dr[m];
1986 rotg->f_rot_loc[l][m] = tmp_f[m];
1987 rotg->V += 0.5*rotg->k*sqr(dr[m]);
1990 if (bTorque)
1992 /* Add to the torque of this rotation group */
1993 rotg->fix_torque_v += torque(rotg->vec, tmp_f, curr_x, zerovec);
1995 /* Calculate the angle between reference and actual rotation group atom: */
1996 angle(rotg, curr_x, xr, &alpha, &weight, zerovec); /* angle in rad, weighted */
1997 rotg->fix_angles_v += alpha * weight;
1998 rotg->fix_weight_v += weight;
1999 /* Use the next two lines instead if you don't want weighting: */
2001 angles_v[g] += alpha;
2002 weight_v[g] += 1;
2005 } /* end of loop over local rotation group atoms */
2009 extern void init_rot_group(
2010 FILE *fplog,t_commrec *cr,
2011 int g,t_rotgrp *rotg,
2012 rvec *x, /* the coordinates */
2013 gmx_mtop_t *mtop,
2014 int rot_type,
2015 FILE *out_slabs,
2016 matrix box)
2018 int i,ii;
2019 real box_d; /* The box diagonal (needed for maximum slab count) */
2020 char filename[255];/* File to save the reference coordinates in for enforced rotation */
2021 rvec f_box[3]; /* Box from reference file */
2022 rvec coord;
2023 t_trnheader header; /* Header information of reference file */
2024 bool bSame; /* Used for a check if box sizes agree */
2025 int nslabs; /* Maximum number of slabs that fit into simulation box */
2026 bool bFlex; /* Flexible rotation? */
2027 bool bColl; /* Use collective coordinates? */
2028 t_atom *atom;
2031 bFlex = (rot_type == erotgFLEX1 || rot_type == erotgFLEX2);
2032 bColl = (bFlex || (rot_type==erotgFOLLOW_PLANE));
2034 snew(rotg->xc_ref , rotg->nat);
2035 snew(rotg->xc_ref_ind , rotg->nat);
2036 snew(rotg->f_rot_loc , rotg->nat);
2037 if (bFlex && rotg->eFittype == erotgFitNORM)
2038 snew(rotg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
2039 if (rot_type == erotgFOLLOW_PLANE)
2040 snew(rotg->mc, rotg->nat);
2042 /* xc_ref_ind needs to be set to identity in the serial case */
2043 if (!PAR(cr))
2044 for (i=0; i<rotg->nat; i++)
2045 rotg->xc_ref_ind[i] = i;
2047 /* Allocate space for collective coordinates if used */
2048 if (bColl)
2050 snew(rotg->xc , rotg->nat);
2051 snew(rotg->xc_norm , rotg->nat);
2052 snew(rotg->xc_old , rotg->nat);
2053 snew(rotg->xc_shifts , rotg->nat);
2054 snew(rotg->xc_eshifts, rotg->nat);
2057 /* Enforced rotation with flexible axis */
2058 if (bFlex)
2060 /* A maximum of (box diagonal)/(slab distance) slabs are possible */
2061 box_d = diagonal_length(box);
2062 rotg->slab_max_nr = (int) ceil(box_d/rotg->slab_dist);
2063 nslabs = 2*rotg->slab_max_nr + 1;
2064 if (MASTER(cr))
2065 fprintf(stdout, "Enforced rotation: allocating memory to store data for %d slabs (rotation group %d).\n",nslabs,g);
2066 snew(rotg->slab_center , nslabs);
2067 snew(rotg->slab_center_ref, nslabs);
2068 snew(rotg->slab_weights , nslabs);
2069 snew(rotg->slab_torque_v , nslabs);
2070 snew(rotg->slab_data , nslabs);
2071 for (i=0; i<nslabs; i++)
2073 snew(rotg->slab_data[i].x , rotg->nat);
2074 snew(rotg->slab_data[i].ref , rotg->nat);
2075 snew(rotg->slab_data[i].weight, rotg->nat);
2079 /* Read in rotation reference coordinates from file, if it exists.
2080 * If not, write out the initial rotation group coordinates as reference coordinates */
2081 if (MASTER(cr))
2083 /* Save the reference coordinates to trr */
2084 /* Make a trr for each rotation group */
2085 sprintf(filename, "ref_%d_%s.trr", g, erotg_names[rotg->eType]);
2086 if (gmx_fexist(filename)) /* Read rotation reference coordinates from file */
2088 fprintf(fplog, "Enforced rotation: found reference coordinate file %s.\n", filename);
2089 read_trnheader(filename, &header);
2090 if (rotg->nat != header.natoms)
2091 gmx_fatal(FARGS,"Number of atoms in coordinate file %s (%d) does not match the number of atoms in rotation group (%d)!\n",
2092 filename, header.natoms, rotg->nat);
2093 read_trn(filename, &header.step, &header.t, &header.lambda, f_box, &header.natoms, rotg->xc_ref, NULL, NULL);
2094 fprintf(fplog , "Enforced rotation: read reference coordinates for group %d from %s.\n", g, filename);
2095 /* Check if the box is unchanged and output a warning if not: */
2096 bSame = TRUE;
2097 for (i=0; i<DIM; i++)
2098 for (ii=0; ii<DIM; ii++)
2099 if (f_box[i][ii] != box[i][ii]) bSame = FALSE;
2101 if (!bSame)
2103 sprintf(warn_buf, "Enforced rotation: Box size in reference file %s differs from actual box size!", filename);
2104 warning(NULL);
2105 pr_rvecs(stderr,0,"Your box is:",box ,3);
2106 pr_rvecs(fplog ,0,"Your box is:",box ,3);
2107 pr_rvecs(stderr,0,"Box in file:",f_box,3);
2108 pr_rvecs(fplog ,0,"Box in file:",f_box,3);
2111 if (g != header.step)
2112 { /* We use step to indicate the number of the rotation group here */
2113 sprintf(warn_buf,"Coordinates from %s will be used for rotation group %d", filename, g);
2114 warning(NULL);
2117 else /* Save the initial coordinates of the rotation group as reference */
2119 for(i=0; i<rotg->nat; i++)
2121 ii = rotg->ind[i];
2122 copy_rvec(x[ii], rotg->xc_ref[i]);
2124 write_trn(filename,g,0.0,0.0,box,rotg->nat,rotg->xc_ref,NULL,NULL);
2125 fprintf(fplog, "Enforced rotation: saved %d coordinates of group %d to %s.\n",
2126 rotg->nat, g, filename);
2128 if (bColl)
2130 /* Save the original (whole) coordinates such that later the
2131 * molecule can always be made whole again */
2132 for (i=0; i<rotg->nat; i++)
2134 ii = rotg->ind[i];
2135 copy_rvec(x[ii], rotg->xc_old[i]);
2139 #ifdef GMX_MPI
2140 /* Copy reference coordinates to all PP nodes */
2141 if (PAR(cr))
2143 gmx_bcast(rotg->nat*sizeof(rotg->xc_ref[0]), rotg->xc_ref, cr);
2144 if (bColl)
2145 gmx_bcast(rotg->nat*sizeof(rotg->xc_old[0]),rotg->xc_old, cr);
2147 #endif
2149 if (bFlex)
2151 /* Flexible rotation: determine the reference COGs for the rest of the simulation */
2152 get_slab_centers(rotg,rotg->xc_ref,box,cr,g,TRUE,-1,out_slabs,1,TRUE);
2154 /* Also save the center of geometry of the reference structure (needed for fitting): */
2155 get_center(rotg->xc_ref, NULL, rotg->nat, rotg->xc_ref_center);
2157 /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
2158 if (rotg->eFittype == erotgFitNORM)
2160 for (i=0; i<rotg->nat; i++)
2162 rvec_sub(rotg->xc_ref[i], rotg->xc_ref_center, coord);
2163 rotg->xc_ref_length[i] = norm(coord);
2168 if (rot_type == erotgFOLLOW_PLANE)
2170 /* We need to copy the masses for later usage */
2171 for (i=0; i<rotg->nat; i++)
2173 gmx_mtop_atomnr_to_atom(mtop,rotg->ind[i],&atom);
2174 rotg->mc[i] = atom->m;
2176 /* Save the center of mass of the reference structure: */
2177 get_center(rotg->xc_ref, rotg->mc, rotg->nat, rotg->xc_ref_center);
2183 static void make_local_rotation_group(gmx_ga2la_t ga2la,
2184 t_rotgrp *rotg,int start,int end)
2186 int i,ii;
2189 rotg->nat_loc = 0;
2190 for(i=0; i<rotg->nat; i++) {
2191 ii = rotg->ind[i];
2192 if (!ga2la_home(ga2la,ii,&ii)) {
2193 ii = -1;
2196 if (ii >= start && ii < end) {
2197 /* This is a home atom, add it to the local rotation group */
2198 if (rotg->nat_loc >= rotg->nalloc_loc) {
2199 rotg->nalloc_loc = over_alloc_dd(rotg->nat_loc+1);
2200 srenew(rotg->ind_loc,rotg->nalloc_loc);
2202 rotg->ind_loc[rotg->nat_loc] = ii;
2203 /* Copy the reference coordinates */
2204 if (rotg->xc_ref)
2206 /* Remember which of the x_rotref coordinates are local: */
2207 rotg->xc_ref_ind[rotg->nat_loc]=i; /* i is the number of the atom with respect to the whole rotation group */
2208 /* pg->ind[i] would be the number with respect to the whole system! */
2210 rotg->nat_loc++;
2215 void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot,t_mdatoms *md)
2217 gmx_ga2la_t ga2la;
2218 int g;
2221 ga2la = dd->ga2la;
2223 for(g=0; g<rot->ngrp; g++)
2224 make_local_rotation_group(ga2la,&rot->grp[g],md->start,md->start+md->homenr);
2228 void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
2229 t_commrec *cr, matrix box, rvec *x, gmx_mtop_t *mtop,
2230 const output_env_t oenv, unsigned long Flags)
2232 t_rot *rot;
2233 t_rotgrp *rotg;
2234 int g;
2235 bool bRerun;
2236 bool bFlex=FALSE;
2239 if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
2240 gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
2242 rot = ir->rot;
2244 /* Output every step for reruns */
2245 bRerun = Flags & MD_RERUN;
2246 if (bRerun)
2248 if (fplog)
2249 fprintf(fplog, "Enforced rotation: rerun - will write rotation output every available step.\n");
2250 rot->nstrout = 1;
2251 rot->nsttout = 1;
2254 rot->out_slabs = NULL;
2255 if (MASTER(cr))
2256 rot->out_slabs = open_slab_out(rot, opt2fn("-rs",nfile,fnm));
2258 for(g=0; g<rot->ngrp; g++)
2260 rotg = &rot->grp[g];
2262 if (fplog)
2263 fprintf(fplog,"Enforced rotation: group %d type '%s'\n", g, erotg_names[rotg->eType]);
2265 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
2266 bFlex = TRUE;
2268 if (rotg->nat > 0)
2270 if (PAR(cr))
2272 rotg->nat_loc = 0;
2273 rotg->nalloc_loc = 0;
2274 rotg->ind_loc = NULL;
2276 else
2278 rotg->nat_loc = rotg->nat;
2279 rotg->ind_loc = rotg->ind;
2281 init_rot_group(fplog,cr,g,rotg,x,mtop,rotg->eType,rot->out_slabs,box);
2285 /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
2286 rot->bufsize = 4*rot->ngrp; /* To start with */
2287 snew(rot->inbuf , rot->bufsize);
2288 snew(rot->outbuf, rot->bufsize);
2290 /* Only do I/O on the MASTER */
2291 rot->out_angles = NULL;
2292 rot->out_rot = NULL;
2293 rot->out_torque = NULL;
2294 if (MASTER(cr))
2296 rot->out_rot = open_rot_out(opt2fn("-r",nfile,fnm), rot, oenv, Flags);
2297 if (bFlex)
2299 if (rot->nstrout > 0)
2300 rot->out_angles = open_angles_out(rot, opt2fn("-ra",nfile,fnm));
2301 if (rot->nsttout > 0)
2302 rot->out_torque = open_torque_out(rot, opt2fn("-rt",nfile,fnm));
2308 extern void do_rotation(
2309 t_commrec *cr,
2310 t_inputrec *ir,
2311 matrix box,
2312 rvec x[],
2313 real t,
2314 int step,
2315 gmx_wallcycle_t wcycle,
2316 bool bNS)
2318 int g;
2319 t_pbc pbc;
2320 t_rot *rot;
2321 t_rotgrp *rotg;
2322 bool outstep_torque;
2323 float cycles_rot;
2324 real degangle;
2325 matrix rotmat;
2328 rot=ir->rot;
2330 /* At which time steps do we want to output the torque */
2331 outstep_torque = do_per_step(step, rot->nsttout);
2333 /* Output time into rotation output file */
2334 if (outstep_torque && MASTER(cr))
2335 fprintf(rot->out_rot, "%12.3e",t);
2337 /* First do ALL the communication! */
2338 for(g=0; g<rot->ngrp; g++)
2340 rotg = &rot->grp[g];
2341 /* Transfer the rotation group's coordinates such that every node has all of them.
2342 * Every node contributes its local coordinates x and stores it in
2343 * the collective pg->xc array. */
2344 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2 || rotg->eType == erotgFOLLOW_PLANE)
2345 get_coordinates(cr, rotg, x, bNS, box);
2346 if (rotg->eType == erotgFOLLOW_PLANE)
2348 /* Get the center of mass of the rotation group and store in rotg->offset */
2349 get_center(rotg->xc, rotg->mc, rotg->nat, rotg->offset);
2353 /* Done communicating, we can start to count cycles now ... */
2354 wallcycle_start(wcycle, ewcROT);
2355 GMX_MPE_LOG(ev_rotcycles_start);
2357 for(g=0; g<rot->ngrp; g++)
2359 rotg = &rot->grp[g];
2360 degangle = rotg->rate * t; /* angle of rotation for this group: */
2361 if (outstep_torque && MASTER(cr))
2362 fprintf(rot->out_rot, "%12.4f", degangle);
2363 /* Calculate the rotation matrix for this angle: */
2364 calc_rotmat(rotg->vec,degangle,rotmat);
2366 switch(rotg->eType)
2368 case erotgFIXED:
2369 case erotgFIXED_PLANE:
2370 set_pbc(&pbc,ir->ePBC,box);
2371 do_fixed(cr,rotg,rotmat,x,&pbc,t,step,outstep_torque);
2372 break;
2373 case erotgFOLLOW_PLANE:
2374 do_follow_plane(cr,rotg,rotmat,x,box,t,step,outstep_torque);
2375 break;
2376 case erotgFLEX1:
2377 case erotgFLEX2:
2378 do_flexible(cr,rotg,g,degangle,rotmat,x,box,t,DYNAMIC_BOX(*ir),step,outstep_torque,
2379 rot->out_slabs,rot->out_torque,rot->out_angles);
2380 break;
2381 default:
2382 break;
2386 /* Stop the cycle counter and add to the force cycles for load balancing */
2387 cycles_rot = wallcycle_stop(wcycle,ewcROT);
2388 if (DOMAINDECOMP(cr) && wcycle)
2389 dd_cycles_add(cr->dd,cycles_rot,ddCyclF);
2390 GMX_MPE_LOG(ev_rotcycles_finish);