Enforced rotation: changed mdrun switch -r to -ro to prevent confusing it with -r...
[gromacs/adressmacs.git] / src / mdlib / pull_rotation.c
blob43eaecf216c735ba4d9cfe1f3de7f456098b0f8a
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"
58 #include "mpelogging.h"
61 /* Helper structure for sorting coordinates along rotation vector */
62 typedef struct {
63 real xcproj; /* Projection of xc on the rotation vector */
64 int ind; /* Index of xc */
65 } sort_along_vec_t;
68 /* Enforced rotation / flexible: determine the angle of each slab */
69 typedef struct gmx_slabdata
71 int nat; /* Number of coordinates belonging to this slab */
72 rvec *x; /* The coordinates belonging to this slab. In
73 general, this should be all rotation group
74 coordinates, but we leave those away that have
75 a small enough weight */
76 rvec *ref; /* Same for reference */
77 real *weight; /* The weight for each atom */
78 } t_gmx_slabdata;
81 /* Enforced rotation data for all groups */
82 typedef struct gmx_enfrot
84 FILE *out_rot; /* Output file for rotation data */
85 FILE *out_torque; /* Output file for torque data */
86 FILE *out_angles; /* Output file for slab angles for flexible type */
87 FILE *out_slabs; /* Output file for slab centers */
88 int bufsize; /* Allocation size of buf */
89 rvec *buf; /* Coordinate buffer variable */
90 sort_along_vec_t *data; /* Buffer variable needed for coordinate sorting */
91 real *mpi_inbuf; /* MPI buffer */
92 real *mpi_outbuf; /* MPI buffer */
93 int mpi_bufsize; /* Allocation size of in & outbuf */
94 real Vrot; /* (Local) part of the enf. rotation potential */
95 } t_gmx_enfrot;
98 /* Global enforced rotation data for a single rotation group */
99 typedef struct gmx_enfrotgrp
101 atom_id *ind_loc; /* Local rotation indices */
102 int nat_loc; /* Number of local group atoms */
103 int nalloc_loc; /* Allocation size for ind_loc and weight_loc */
105 /* Collective coordinates for the whole rotation group */
106 rvec *xc_ref; /* Reference (unrotated) coordinates */
107 real *xc_ref_length; /* Length of each x_rotref vector after x_rotref
108 has been put into origin */
109 int *xc_ref_ind; /* Local indices to the reference coordinates */
110 rvec xc_ref_center; /* Center of the reference coordinates. May be
111 mass-weighted */
112 rvec *xc; /* Current (collective) coordinates */
113 ivec *xc_shifts; /* Current (collective) shifts */
114 ivec *xc_eshifts; /* Extra shifts since last DD step */
115 rvec *xc_old; /* Old (collective) coordinates */
116 rvec *xc_norm; /* Normalized form of the current coordinates */
117 rvec *xc_ref_sorted; /* Reference coordinates (sorted in the same order
118 as like xc when sorted) */
119 int *xc_sortind; /* Indices of sorted coordinates */
120 real *mc; /* Collective masses */
121 /* Fixed rotation only */
122 real fix_torque_v; /* Torque in the direction of rotation vector */
123 real fix_angles_v;
124 real fix_weight_v;
125 /* Flexible rotation only */
126 int slab_max_nr; /* The maximum number of slabs in the box */
127 int slab_first; /* Lowermost slab for that the calculation needs
128 to be performed */
129 int slab_last; /* Uppermost slab ... */
130 int *firstatom; /* First relevant atom for a slab */
131 int *lastatom; /* Last relevant atom for a slab */
132 rvec *slab_center; /* Gaussian-weighted slab center (COG) */
133 rvec *slab_center_ref; /* Gaussian-weighted slab COG for the
134 reference coordinates */
135 real *slab_weights; /* Sum of gaussian weights in a slab */
136 real *slab_torque_v; /* Torque T = r x f for each slab. */
137 /* torque_v = m.v = angular momentum in the
138 direction of v */
139 real max_beta; /* min_gaussian from inputrec->rotgrp is the
140 minimum value the gaussian must have so that
141 the force is actually evaluated max_beta is
142 just another way to put it */
143 real *gn_atom; /* Precalculated gaussians for a single atom */
144 int *gn_slabind; /* Tells to which slab each precalculated gaussian
145 belongs */
146 int gn_alloc; /* Allocation size of gn_atom and gn_slabind */
148 real V; /* Rotation potential for this rotation group */
149 rvec *f_rot_loc; /* Array to store the forces on the local atoms
150 resulting from enforced rotation potential */
151 t_gmx_slabdata *slab_data; /* Holds atom coordinates and gaussian weights
152 of atoms belonging to a slab */
153 } t_gmx_enfrotgrp;
156 static double** allocate_square_matrix(int dim)
158 int i;
159 double** mat = NULL;
162 snew(mat, dim);
163 for(i=0; i<dim; i++)
164 snew(mat[i], dim);
166 return mat;
170 static void free_square_matrix(double** mat, int dim)
172 int i;
175 for (i=0; i<dim; i++)
176 sfree(mat[i]);
177 sfree(mat);
181 /* Output rotation energy and torque for each rotation group */
182 static void reduce_output(t_commrec *cr, t_rot *rot, real t)
184 int g,i,islab,k,nslabs=0;
185 int count; /* MPI element counter */
186 t_rotgrp *rotg;
187 gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
188 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
191 er=rot->enfrot;
193 /* Fill the MPI buffer with stuff to reduce: */
194 if (PAR(cr))
196 count=0;
197 for (g=0; g < rot->ngrp; g++)
199 rotg = &rot->grp[g];
200 erg=rotg->enfrotgrp;
201 nslabs = 2*erg->slab_max_nr+1;
202 er->mpi_inbuf[count++] = erg->V;
203 switch (rotg->eType)
205 case erotgFIXED:
206 case erotgFIXED_PLANE:
207 case erotgFOLLOW_PLANE:
208 er->mpi_inbuf[count++] = erg->fix_torque_v;
209 er->mpi_inbuf[count++] = erg->fix_angles_v;
210 er->mpi_inbuf[count++] = erg->fix_weight_v;
211 break;
212 case erotgFLEX1:
213 case erotgFLEX2:
214 /* (Re-)allocate memory for MPI buffer: */
215 if (er->mpi_bufsize < count+nslabs)
217 er->mpi_bufsize = count+nslabs;
218 srenew(er->mpi_inbuf , er->mpi_bufsize);
219 srenew(er->mpi_outbuf, er->mpi_bufsize);
221 for (i=0; i<nslabs; i++)
222 er->mpi_inbuf[count++] = erg->slab_torque_v[i];
223 break;
224 default:
225 break;
228 #ifdef GMX_MPI
229 MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
230 #endif
231 /* Copy back the reduced data from the buffer on the master */
232 if (MASTER(cr))
234 count=0;
235 for (g=0; g < rot->ngrp; g++)
237 rotg = &rot->grp[g];
238 erg=rotg->enfrotgrp;
239 nslabs = 2*erg->slab_max_nr+1;
240 erg->V = er->mpi_outbuf[count++];
241 switch (rotg->eType)
243 case erotgFIXED:
244 case erotgFIXED_PLANE:
245 case erotgFOLLOW_PLANE:
246 erg->fix_torque_v = er->mpi_outbuf[count++];
247 erg->fix_angles_v = er->mpi_outbuf[count++];
248 erg->fix_weight_v = er->mpi_outbuf[count++];
249 break;
250 case erotgFLEX1:
251 case erotgFLEX2:
252 for (i=0; i<nslabs; i++)
253 erg->slab_torque_v[i] = er->mpi_outbuf[count++];
254 break;
255 default:
256 break;
262 /* Output */
263 if (MASTER(cr))
265 /* Av. angle and total torque for each rotation group */
266 for (g=0; g < rot->ngrp; g++)
268 rotg=&rot->grp[g];
269 erg=rotg->enfrotgrp;
271 /* Output to main rotation log file: */
272 if (rotg->eType == erotgFIXED || rotg->eType == erotgFIXED_PLANE || rotg->eType == erotgFOLLOW_PLANE)
274 fprintf(er->out_rot, "%12.4f%12.3e",
275 (erg->fix_angles_v/erg->fix_weight_v)*180.0*M_1_PI,
276 erg->fix_torque_v);
278 fprintf(er->out_rot, "%12.3e", erg->V);
280 /* Output to torque log file: */
281 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
283 fprintf(er->out_torque, "%12.3e%6d", t, g);
284 k = erg->slab_max_nr;
285 for (i=-k; i <= k; i++)
287 islab = i + erg->slab_max_nr; /* slab index */
288 /* Only output if enough weight is in slab */
289 if (erg->slab_weights[islab] > rotg->min_gaussian)
290 fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
292 fprintf(er->out_torque , "\n");
295 fprintf(er->out_rot, "\n");
300 /* Add the forces from enforced rotation potential to the local forces.
301 * Should be called after the SR forces have been evaluated */
302 extern real add_rot_forces(t_rot *rot, rvec f[], t_commrec *cr, int step, real t)
304 int g,l,ii;
305 t_rotgrp *rotg;
306 gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
307 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
310 er=rot->enfrot;
312 GMX_MPE_LOG(ev_add_rot_forces_start);
314 /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
315 * on the master and output these values to file. */
316 if (do_per_step(step, rot->nsttout))
317 reduce_output(cr, rot, t);
319 /* Total rotation potential is the sum over all rotation groups */
320 er->Vrot = 0.0;
322 /* Loop over enforced rotation groups (usually 1, though)
323 * Apply the forces from rotation potentials */
324 for (g=0; g<rot->ngrp; g++)
326 rotg = &rot->grp[g];
327 erg=rotg->enfrotgrp;
328 er->Vrot += erg->V;
329 for (l=0; l<erg->nat_loc; l++)
331 /* Get the right index of the local force */
332 ii = erg->ind_loc[l];
333 /* Add */
334 rvec_inc(f[ii],erg->f_rot_loc[l]);
338 GMX_MPE_LOG(ev_add_rot_forces_finish);
340 return (MASTER(cr)? er->Vrot : 0.0);
344 /* Calculate the box diagonal length */
345 static real diagonal_length(matrix box)
347 rvec diag;
350 copy_rvec(box[XX],diag);
351 rvec_inc(diag,box[YY]);
352 rvec_inc(diag,box[ZZ]);
354 return norm(diag);
358 /* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
359 * also does some checks
361 static double calc_beta_max(real min_gaussian, real slab_dist)
363 const double norm = 0.5698457353514458216; /* = 1/1.7548609 */
364 double sigma;
365 double arg;
368 if (slab_dist <= 0)
369 gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
371 /* Define the sigma value */
372 sigma = 0.7*slab_dist;
374 /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
375 arg = min_gaussian/norm;
376 if (arg > 1.0)
377 gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", norm);
379 return sqrt(-2.0*sigma*sigma*log(min_gaussian/norm));
383 static inline real calc_beta(rvec curr_x, t_rotgrp *rotg, int n)
385 return iprod(curr_x, rotg->vec) - rotg->slab_dist * n;
389 static inline real gaussian_weight(rvec curr_x, t_rotgrp *rotg, int n)
391 /* norm is chosen such that the sum of the gaussians
392 * over the slabs is approximately 1.0 everywhere */
393 const real norm = 0.5698457353514458216; /* = 1/1.7548609 */
394 real sigma;
397 /* Define the sigma value */
398 sigma = 0.7*rotg->slab_dist;
399 /* Calculate the Gaussian value of slab n for coordinate curr_x */
400 return norm * exp( -0.5 * sqr( calc_beta(curr_x, rotg, n)/sigma ) );
404 static void get_slab_centers(
405 t_rotgrp *rotg, /* The rotation group information */
406 rvec *xc, /* The rotation group coordinates; will
407 typically be enfrotgrp->xc, but at first call
408 it is enfrotgrp->xc_ref */
409 matrix box, /* The box coordinates */
410 t_commrec *cr, /* Communication record */
411 int g, /* The number of the rotation group */
412 bool bDynBox, /* Is the box dynamic? */
413 real time, /* Used for output only */
414 FILE *out_slabs, /* For outputting COG per slab information */
415 bool bOutStep, /* Is this an output step? */
416 bool bReference) /* If this routine is called from
417 init_rot_group we need to store
418 the reference slab COGs */
420 rvec curr_x; /* The coordinate of an atom */
421 rvec curr_x_weighted; /* The gaussian-weighted coordinate */
422 real gaussian; /* The gaussian */
423 int i,j,k,islab,nslabs;
424 real box_d; /* The box diagonal */
425 bool bFirstSet;
426 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
427 bool slab_has_weight; /* Remember whether relevant coordinates
428 * are found in this slab */
430 erg=rotg->enfrotgrp;
432 /* If the box grows during the simulation, we might need more memory */
433 if (bDynBox)
435 box_d = diagonal_length(box);
437 /* The slab indices run from [-pgrp->slab_max_nr, -1, 0, +1, ..., +pgrp->slab_max_nr] */
438 nslabs = 2*erg->slab_max_nr + 1;
440 /* The box diagonal divided by the slab distance gives the maximum number of slabs in positive direction: */
441 if ( (int)ceil(box_d/rotg->slab_dist) > erg->slab_max_nr )
443 while ( (int)ceil(box_d/rotg->slab_dist) > erg->slab_max_nr )
444 erg->slab_max_nr++;
445 /* TODO: it could still be that the rotation group diffuses out of the
446 * box. Then we would have to allocate more slabs than fit in a box!
449 nslabs = 2*erg->slab_max_nr + 1;
450 fprintf(stdout, "Node %d reallocates memory to hold data for %d slabs (rotation group %d).\n", cr->nodeid,nslabs,g);
451 srenew(erg->slab_center , nslabs);
452 srenew(erg->slab_weights , nslabs);
453 srenew(erg->slab_torque_v, nslabs);
454 srenew(erg->slab_data, nslabs);
455 erg->gn_alloc = nslabs;
456 srenew(erg->gn_atom , nslabs);
457 srenew(erg->gn_slabind , nslabs);
459 for (i=0; i<nslabs; i++)
461 srenew(erg->slab_data[i].x , rotg->nat);
462 srenew(erg->slab_data[i].ref , rotg->nat);
463 srenew(erg->slab_data[i].weight, rotg->nat);
468 /* Loop over slabs */
469 bFirstSet = FALSE;
470 k = erg->slab_max_nr;
471 erg->slab_first = 1;
472 erg->slab_last = 0;
473 for (j = -k; j <= k; j++)
475 /* Remember whether at least one of the coordinates has a weight
476 * larger than the required minimum: */
477 slab_has_weight = FALSE;
479 islab = j+erg->slab_max_nr; /* slab index */
480 /* Initialize data for this slab: */
481 clear_rvec(erg->slab_center[islab]);
482 erg->slab_weights[islab] = 0.0;
484 /* loop over all atoms in the rotation group */
485 for(i=0; i<rotg->nat;i++)
487 copy_rvec(xc[i], curr_x);
488 gaussian = gaussian_weight(curr_x, rotg, j);
489 if (gaussian >= rotg->min_gaussian)
490 slab_has_weight = TRUE;
491 svmul(gaussian, curr_x, curr_x_weighted);
492 rvec_add(erg->slab_center[islab], curr_x_weighted, erg->slab_center[islab]);
493 erg->slab_weights[islab] += gaussian;
494 } /* END of loop over rotation group atoms */
496 /* Do the calculations ONLY if there is enough weight in the slab! */
497 if (slab_has_weight)
499 svmul(1.0/erg->slab_weights[islab], erg->slab_center[islab], erg->slab_center[islab]);
500 /* Remember which slabs to calculate for the low-level routines */
501 if (!bFirstSet)
503 erg->slab_first = j;
504 bFirstSet = TRUE;
506 /* This get overwritten by the last slab that has enough weight: */
507 erg->slab_last = j;
509 /* At first time step: save the COGs of the reference structure */
510 if(bReference)
511 copy_rvec(erg->slab_center[islab], erg->slab_center_ref[islab]);
512 } /* END of loop over slabs */
514 /* Output on the master */
515 if (MASTER(cr) && bOutStep)
517 fprintf(out_slabs, "%12.3e%6d", time, g);
518 for (j = erg->slab_first; j <= erg->slab_last; j++)
520 islab = j+erg->slab_max_nr; /* slab index */
521 fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
522 j,erg->slab_center[islab][XX],erg->slab_center[islab][YY],erg->slab_center[islab][ZZ]);
524 if (!bFirstSet)
525 fprintf(out_slabs, "WARNING: no weight in any of the slabs - nothing to calculate!");
526 fprintf(out_slabs, "\n");
531 static void calc_rotmat(
532 rvec vec,
533 real degangle, /* Angle alpha of rotation at time t in degrees */
534 matrix rotmat) /* Rotation matrix */
536 real radangle; /* Rotation angle in radians */
537 real cosa; /* cosine alpha */
538 real sina; /* sine alpha */
539 real OMcosa; /* 1 - cos(alpha) */
540 real dumxy, dumxz, dumyz; /* save computations */
541 rvec rot_vec; /* Rotate around rot_vec ... */
544 radangle = degangle * M_PI/180.0;
545 copy_rvec(vec , rot_vec );
547 /* Precompute some variables: */
548 cosa = cos(radangle);
549 sina = sin(radangle);
550 OMcosa = 1.0 - cosa;
551 dumxy = rot_vec[XX]*rot_vec[YY]*OMcosa;
552 dumxz = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
553 dumyz = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
555 /* Construct the rotation matrix for this rotation group: */
556 /* 1st column: */
557 rotmat[XX][XX] = cosa + rot_vec[XX]*rot_vec[XX]*OMcosa;
558 rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
559 rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
560 /* 2nd column: */
561 rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
562 rotmat[YY][YY] = cosa + rot_vec[YY]*rot_vec[YY]*OMcosa;
563 rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
564 /* 3rd column: */
565 rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
566 rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
567 rotmat[ZZ][ZZ] = cosa + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
569 #ifdef PRINTMATRIX
570 int iii,jjj;
572 for (iii=0; iii<3; iii++) {
573 for (jjj=0; jjj<3; jjj++)
574 fprintf(stderr, " %10.8f ", rotmat[iii][jjj]);
575 fprintf(stderr, "\n");
577 #endif
581 /* Calculates torque on the rotation axis tau = coord x force */
582 static inline real torque(
583 rvec rotvec, /* rotation vector; MUST be normalized! */
584 rvec force, /* force */
585 rvec x, /* coordinate of atom on which the force acts */
586 rvec offset) /* piercing point of rotation axis
587 (or center of the slab for the flexible types) */
589 rvec vectmp, tau;
592 /* Subtract offset */
593 rvec_sub(x,offset,vectmp);
595 /* coord x force */
596 cprod(vectmp, force, tau);
598 /* Return the part of the torque which is parallel to the rotation vector */
599 return iprod(tau, rotvec);
603 /* Right-aligned output of value with standard width */
604 static void print_aligned(FILE *fp, char *str)
606 fprintf(fp, "%12s", str);
610 /* Right-aligned output of value with standard short width */
611 static void print_aligned_short(FILE *fp, char *str)
613 fprintf(fp, "%6s", str);
617 /* Right-aligned output of value with standard width */
618 static void print_aligned_group(FILE *fp, char *str, int g)
620 char sbuf[STRLEN];
623 sprintf(sbuf, "%s%d", str, g);
624 fprintf(fp, "%12s", sbuf);
628 static FILE *open_output_file(const char *fn, int steps)
630 FILE *fp;
633 fp = ffopen(fn, "w");
635 fprintf(fp, "# Output is written every %d time steps.\n\n", steps);
637 return fp;
641 /* Open output file for slab COG data. Call on master only */
642 static FILE *open_slab_out(t_rot *rot, const char *fn)
644 FILE *fp=NULL;
645 int g;
646 t_rotgrp *rotg;
649 for (g=0; g<rot->ngrp; g++)
651 rotg = &rot->grp[g];
652 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
654 if (NULL == fp)
655 fp = open_output_file(fn, rot->nsttout);
656 fprintf(fp, "# Rotation group %d (%s), slab distance %f nm\n", g, erotg_names[rotg->eType], rotg->slab_dist);
660 if (fp != NULL)
662 fprintf(fp, "# The following columns will have the syntax: (COG = center of geometry, gaussian weighted)\n");
663 fprintf(fp, "# ");
664 print_aligned_short(fp, "t");
665 print_aligned_short(fp, "grp");
666 print_aligned_short(fp, "slab");
667 print_aligned(fp, "COG-X");
668 print_aligned(fp, "COG-Y");
669 print_aligned(fp, "COG-Z");
670 print_aligned_short(fp, "slab");
671 print_aligned(fp, "COG-X");
672 print_aligned(fp, "COG-Y");
673 print_aligned(fp, "COG-Z");
674 print_aligned_short(fp, "slab");
675 fprintf(fp, " ...\n");
676 fflush(fp);
679 return fp;
683 /* Open output file and print some general information about the rotation groups.
684 * Call on master only */
685 static FILE *open_rot_out(const char *fn, t_rot *rot, const output_env_t oenv,
686 unsigned long Flags)
688 FILE *fp;
689 int g,nsets;
690 t_rotgrp *rotg;
691 char **setname,buf[50];
692 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
695 if (Flags & MD_APPENDFILES)
697 fp = gmx_fio_fopen(fn,"a");
699 else
701 fp = xvgropen(fn, "Rotation angles and energy", "Time (ps)", "angles and energies", oenv);
702 fprintf(fp, "# The scalar tau is the torque in the direction of the rotation vector v.\n");
703 fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot_vec.\n");
704 fprintf(fp, "# Torques are given in [kJ/mol], anlges in degrees, time in ps.\n");
706 for (g=0; g<rot->ngrp; g++)
708 rotg = &rot->grp[g];
709 erg=rotg->enfrotgrp;
711 fprintf(fp, "# Rotation group %d (%s):\n", g, erotg_names[rotg->eType]);
712 fprintf(fp, "# rot_vec%d %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
713 fprintf(fp, "# rot_rate%d %10.3e degree/ps\n", g, rotg->rate);
714 fprintf(fp, "# rot_k%d %10.3e kJ/(mol*nm^2)\n", g, rotg->k);
716 switch (rotg->eType)
718 case erotgFIXED:
719 case erotgFIXED_PLANE:
720 fprintf(fp, "# rot_offset%d %10.3e %10.3e %10.3e\n", g, rotg->offset[XX], rotg->offset[YY], rotg->offset[ZZ]);
721 break;
722 case erotgFOLLOW_PLANE:
723 fprintf(fp, "# COM of ref. grp. %d %10.3e %10.3e %10.3e\n", g, erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
724 break;
725 case erotgFLEX1:
726 case erotgFLEX2:
727 fprintf(fp, "# rot_slab_distance%d %f nm\n", g, rotg->slab_dist);
728 fprintf(fp, "# rot_min_gaussian%d %10.3e\n", g, rotg->min_gaussian);
729 fprintf(fp, "# COG of ref. grp. %d %10.3e %10.3e %10.3e (only needed for fit)\n",
730 g, erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
731 break;
732 default:
733 break;
737 fprintf(fp, "#\n# Legend for the following data columns:\n");
738 fprintf(fp, "# ");
739 print_aligned_short(fp, "t");
740 nsets = 0;
741 snew(setname, 4*rot->ngrp);
743 for (g=0; g<rot->ngrp; g++)
745 rotg = &rot->grp[g];
746 sprintf(buf, "theta_ref%d (degree)", g);
747 print_aligned_group(fp, "theta_ref", g);
748 setname[nsets] = strdup(buf);
749 nsets++;
751 for (g=0; g<rot->ngrp; g++)
753 rotg = &rot->grp[g];
754 if (rotg->eType==erotgFIXED || rotg->eType==erotgFIXED_PLANE || rotg->eType==erotgFOLLOW_PLANE)
756 sprintf(buf, "theta-av%d (degree)", g);
757 print_aligned_group(fp, "theta_av", g);
758 setname[nsets] = strdup(buf);
759 nsets++;
760 sprintf(buf, "tau%d (kJ/mol)", g);
761 print_aligned_group(fp, "tau", g);
762 setname[nsets] = strdup(buf);
763 nsets++;
765 sprintf(buf, "energy%d (kJ/mol)", g);
766 print_aligned_group(fp, "energy", g);
767 setname[nsets] = strdup(buf);
768 nsets++;
770 fprintf(fp, "\n#\n");
772 if (nsets > 1)
773 xvgr_legend(fp, nsets, setname, oenv);
774 for(g=0; g<nsets; g++)
775 sfree(setname[g]);
776 sfree(setname);
778 fflush(fp);
781 return fp;
785 /* Call on master only */
786 static FILE *open_angles_out(t_rot *rot, const char *fn)
788 int g;
789 FILE *fp=NULL;
790 t_rotgrp *rotg;
793 /* Open output file and write some information about it's structure: */
794 fp = open_output_file(fn, rot->nstrout);
795 fprintf(fp, "# All angles given in degrees, time in ps\n");
796 for (g=0; g<rot->ngrp; g++)
798 rotg = &rot->grp[g];
799 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
800 fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, fit type %s\n",
801 g, erotg_names[rotg->eType], rotg->slab_dist, erotg_fitnames[rotg->eFittype]);
803 fprintf(fp, "# The following columns will have the syntax:\n");
804 fprintf(fp, "# ");
805 print_aligned_short(fp, "t");
806 print_aligned_short(fp, "grp");
807 print_aligned(fp, "theta_ref");
808 print_aligned(fp, "theta_fit");
809 print_aligned_short(fp, "slab");
810 print_aligned_short(fp, "atoms");
811 print_aligned(fp, "theta_fit");
812 print_aligned_short(fp, "slab");
813 print_aligned_short(fp, "atoms");
814 print_aligned(fp, "theta_fit");
815 fprintf(fp, " ...\n");
816 fflush(fp);
817 return fp;
821 /* Open torque output file and write some information about it's structure.
822 * Call on master only */
823 static FILE *open_torque_out(t_rot *rot, const char *fn)
825 FILE *fp;
826 int g;
827 t_rotgrp *rotg;
830 fp = open_output_file(fn, rot->nsttout);
832 for (g=0; g<rot->ngrp; g++)
834 rotg = &rot->grp[g];
835 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
837 fprintf(fp, "# Rotation group %d (%s), slab distance %f nm\n", g, erotg_names[rotg->eType], rotg->slab_dist);
838 fprintf(fp, "# The scalar tau is the torque [kJ/mol] in the direction of the rotation vector.\n");
839 fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
840 fprintf(fp, "# rot_vec%d %10.3e %10.3e %10.3e\n", g, rotg->vec[XX], rotg->vec[YY], rotg->vec[ZZ]);
841 fprintf(fp, "#\n");
844 fprintf(fp, "# The following columns will have the syntax (tau=torque for that slab):\n");
845 fprintf(fp, "# ");
846 print_aligned_short(fp, "t");
847 print_aligned_short(fp, "grp");
848 print_aligned_short(fp, "slab");
849 print_aligned(fp, "tau");
850 print_aligned_short(fp, "slab");
851 print_aligned(fp, "tau");
852 fprintf(fp, " ...\n");
853 fflush(fp);
855 return fp;
859 /* Determine center of structure with coordinates x */
860 static void get_center(rvec x[], real weight[], int nat, rvec center)
862 int i;
863 rvec coord;
864 double weight_sum = 0.0;
867 /* Zero out the center */
868 clear_rvec(center);
870 /* Loop over all atoms and add their weighted position vectors */
871 if (weight)
873 for (i=0; i<nat; i++)
875 weight_sum += weight[i];
876 svmul(weight[i], x[i], coord);
877 rvec_inc(center, coord);
880 /* Divide by the sum of weight */
881 svmul(1.0/weight_sum, center, center);
883 else
885 for (i=0; i<nat; i++)
886 rvec_inc(center, x[i]);
888 /* Divide by the number of atoms */
889 svmul(1.0/nat, center, center);
895 static void swap_val(double* vec, int i, int j)
897 double tmp = vec[j];
900 vec[j]=vec[i];
901 vec[i]=tmp;
905 static void swap_col(double **mat, int i, int j)
907 double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
910 mat[0][j]=mat[0][i];
911 mat[1][j]=mat[1][i];
912 mat[2][j]=mat[2][i];
914 mat[0][i]=tmp[0];
915 mat[1][i]=tmp[1];
916 mat[2][i]=tmp[2];
920 /* Eigenvectors are stored in columns of eigen_vec */
921 static void diagonalize_symmetric(
922 double **matrix,
923 double **eigen_vec,
924 double eigenval[3])
926 int n_rot;
929 jacobi(matrix,3,eigenval,eigen_vec,&n_rot);
931 /* sort in ascending order */
932 if (eigenval[0] > eigenval[1])
934 swap_val(eigenval, 0, 1);
935 swap_col(eigen_vec, 0, 1);
937 if (eigenval[1] > eigenval[2])
939 swap_val(eigenval, 1, 2);
940 swap_col(eigen_vec, 1, 2);
942 if (eigenval[0] > eigenval[1])
944 swap_val(eigenval, 0, 1);
945 swap_col(eigen_vec, 0, 1);
950 static void align_with_z(
951 rvec* s, /* Structure to align */
952 int natoms,
953 rvec axis)
955 int i, j, k;
956 rvec zet = {0.0, 0.0, 1.0};
957 rvec rot_axis={0.0, 0.0, 0.0};
958 rvec *rotated_str=NULL;
959 real ooanorm;
960 real angle;
961 matrix rotmat;
964 snew(rotated_str, natoms);
966 /* Normalize the axis */
967 ooanorm = 1.0/norm(axis);
968 svmul(ooanorm, axis, axis);
970 /* Calculate the angle for the fitting procedure */
971 cprod(axis, zet, rot_axis);
972 angle = acos(axis[2]);
973 if (angle < 0.0)
974 angle += M_PI;
976 /* Calculate the rotation matrix */
977 calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
979 /* Apply the rotation matrix to s */
980 for (i=0; i<natoms; i++)
982 for(j=0; j<3; j++)
984 for(k=0; k<3; k++)
986 rotated_str[i][j] += rotmat[j][k]*s[i][k];
991 /* Rewrite the rotated structure to s */
992 for(i=0; i<natoms; i++)
994 for(j=0; j<3; j++)
996 s[i][j]=rotated_str[i][j];
1000 sfree(rotated_str);
1004 static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
1006 int i, j, k;
1009 for (i=0; i<3; i++)
1010 for (j=0; j<3; j++)
1011 Rmat[i][j] = 0.0;
1013 for (i=0; i<3; i++)
1014 for (j=0; j<3; j++)
1015 for (k=0; k<natoms; k++)
1016 Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
1020 static void weight_coords(rvec* str, real* weight, int natoms)
1022 int i, j;
1025 for(i=0; i<natoms; i++)
1027 for(j=0; j<3; j++)
1028 str[i][j] *= sqrt(weight[i]);
1033 static void trans(rvec* x, int natoms, double* vec)
1035 int i;
1038 for(i=0; i<natoms; i++)
1040 x[i][0] += vec[0];
1041 x[i][1] += vec[1];
1042 x[i][2] += vec[2];
1047 static double opt_angle_analytic(
1048 rvec* ref_s,
1049 rvec* act_s,
1050 real* weight,
1051 int natoms,
1052 rvec ref_com,
1053 rvec act_com,
1054 rvec axis)
1056 int i, j, k;
1057 rvec *ref_s_1=NULL;
1058 rvec *act_s_1=NULL;
1059 double shift[3];
1060 double **Rmat, **RtR, **eigvec;
1061 double eigval[3];
1062 double V[3][3], WS[3][3];
1063 double rot_matrix[3][3];
1064 double opt_angle;
1067 /* Do not change the original coordinates */
1068 snew(ref_s_1, natoms);
1069 snew(act_s_1, natoms);
1070 for(i=0; i<natoms; i++)
1072 copy_rvec(ref_s[i], ref_s_1[i]);
1073 copy_rvec(act_s[i], act_s_1[i]);
1076 /* Translate the structures to the origin */
1077 for (i=0; i<3; i++)
1078 shift[i] = (-1.0)*ref_com[i];
1079 trans(ref_s_1, natoms, shift);
1081 for (i=0; i<3; i++)
1082 shift[i] = (-1.0)*act_com[i];
1083 trans(act_s_1, natoms, shift);
1085 /* Align rotation axis with z */
1086 align_with_z(ref_s_1, natoms, axis);
1087 align_with_z(act_s_1, natoms, axis);
1089 /* Correlation matrix */
1090 Rmat = allocate_square_matrix(3);
1092 for (i=0; i<natoms; i++)
1094 ref_s_1[i][2]=0.0;
1095 act_s_1[i][2]=0.0;
1098 /* Weight coordinates with sqrt(weight) */
1099 if (weight)
1101 weight_coords(ref_s_1, weight, natoms);
1102 weight_coords(act_s_1, weight, natoms);
1105 /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
1106 calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
1108 /* Calculate RtR */
1109 RtR = allocate_square_matrix(3);
1110 for (i=0; i<3; i++)
1112 for (j=0; j<3; j++)
1114 for (k=0; k<3; k++)
1116 RtR[i][j] += Rmat[k][i] * Rmat[k][j];
1120 /* Diagonalize RtR */
1121 snew(eigvec,3);
1122 for (i=0; i<3; i++)
1123 snew(eigvec[i],3);
1125 diagonalize_symmetric(RtR, eigvec, eigval);
1126 swap_col(eigvec,0,1);
1127 swap_col(eigvec,1,2);
1128 swap_val(eigval,0,1);
1129 swap_val(eigval,1,2);
1131 /* Calculate V */
1132 for(i=0; i<3; i++)
1134 for(j=0; j<3; j++)
1136 V[i][j] = 0.0;
1137 WS[i][j] = 0.0;
1141 for (i=0; i<2; i++)
1142 for (j=0; j<2; j++)
1143 WS[i][j] = eigvec[i][j] / sqrt(eigval[j]);
1145 for (i=0; i<3; i++)
1147 for (j=0; j<3; j++)
1149 for (k=0; k<3; k++)
1151 V[i][j] += Rmat[i][k]*WS[k][j];
1155 free_square_matrix(Rmat, 3);
1157 /* Calculate optimal rotation matrix */
1158 for (i=0; i<3; i++)
1159 for (j=0; j<3; j++)
1160 rot_matrix[i][j] = 0.0;
1162 for (i=0; i<3; i++)
1164 for(j=0; j<3; j++)
1166 for(k=0; k<3; k++){
1167 rot_matrix[i][j] += eigvec[i][k]*V[j][k];
1171 rot_matrix[2][2] = 1.0;
1173 /* Determine the optimal rotation angle: */
1174 opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
1175 if (rot_matrix[0][1] < 0.0)
1176 opt_angle = (-1.0)*opt_angle;
1178 /* Give back some memory */
1179 free_square_matrix(RtR, 3);
1180 sfree(ref_s_1);
1181 sfree(act_s_1);
1182 for (i=0; i<3; i++)
1183 sfree(eigvec[i]);
1184 sfree(eigvec);
1186 return opt_angle;
1190 /* Determine actual angle of this slab by RMSD fit */
1191 /* Not parallelized, call this routine only on the master */
1192 static void flex_fit_angle(
1193 int g,
1194 t_rotgrp *rotg,
1195 double t,
1196 real degangle,
1197 FILE *fp)
1199 int i,l,n,islab,ind;
1200 rvec curr_x, ref_x;
1201 rvec *fitcoords=NULL;
1202 rvec act_center; /* Center of actual coordinates that are passed to the fit routine */
1203 rvec ref_center; /* Same for the reference coordinates */
1204 double fitangle; /* This will be the actual angle of the rotation group derived
1205 * from an RMSD fit to the reference structure at t=0 */
1206 t_gmx_slabdata *sd;
1207 rvec coord;
1208 real scal;
1209 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
1212 erg=rotg->enfrotgrp;
1214 /**********************************/
1215 /* First collect the data we need */
1216 /**********************************/
1218 /* Loop over slabs */
1219 for (n = erg->slab_first; n <= erg->slab_last; n++)
1221 islab = n+erg->slab_max_nr; /* slab index */
1222 sd = &(rotg->enfrotgrp->slab_data[islab]);
1223 sd->nat = erg->lastatom[n]-erg->firstatom[n]+1;
1224 ind = 0;
1226 /* Loop over the relevant atoms in the slab */
1227 for (l=erg->firstatom[n]; l<=erg->lastatom[n]; l++)
1229 /* Current coordinate of this atom: x[ii][XX/YY/ZZ] */
1230 copy_rvec(erg->xc[l], curr_x);
1232 /* The (unrotated) reference coordinate of this atom is copied to ref_x.
1233 * Beware, the xc coords have been sorted in do_flex! */
1234 copy_rvec(erg->xc_ref_sorted[l], ref_x);
1236 /* Save data for doing angular RMSD fit later */
1237 /* Save the current atom coordinate */
1238 copy_rvec(curr_x, sd->x[ind]);
1239 /* Save the corresponding reference coordinate */
1240 copy_rvec(ref_x , sd->ref[ind]);
1241 /* Save the weight for this atom in this slab */
1242 sd->weight[ind] = gaussian_weight(curr_x, rotg, n);
1244 /* Next atom in this slab */
1245 ind++;
1249 /* Get the center of all rotation group coordinates: */
1250 get_center(erg->xc, NULL, rotg->nat, act_center);
1253 /******************************/
1254 /* Now do the fit calculation */
1255 /******************************/
1257 /* === Determine the optimal fit angle for the whole rotation group === */
1258 if (rotg->eFittype == erotgFitNORM)
1260 /* Normalize every coordinate to it's reference coordinate length
1261 * prior to performing the fit */
1262 for (i=0; i<rotg->nat; i++)
1264 /* First put center of coordinates into origin */
1265 rvec_sub(erg->xc[i], act_center, coord);
1266 /* Determine the scaling factor for the coordinate: */
1267 scal = erg->xc_ref_length[erg->xc_sortind[i]] / norm(coord);
1268 /* Get coordinate, multiply with the scaling factor and save in buf[i] */
1269 svmul(scal, coord, erg->xc_norm[i]);
1271 fitcoords = erg->xc_norm;
1273 else
1275 fitcoords = erg->xc;
1277 /* Note that from the point of view of the current coordinates, the reference has rotated backwards,
1278 * but we want to output the angle relative to the fixed reference, therefore the minus sign. */
1279 fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, NULL, rotg->nat, erg->xc_ref_center, act_center, rotg->vec);
1280 fprintf(fp, "%12.3e%6d%12.3f%12.3lf", t, g, degangle, fitangle);
1283 /* === Now do RMSD fitting for each slab === */
1284 /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
1285 #define SLAB_MIN_ATOMS 9
1287 for (n = erg->slab_first; n <= erg->slab_last; n++)
1289 islab = n+erg->slab_max_nr; /* slab index */
1290 sd = &(rotg->enfrotgrp->slab_data[islab]);
1291 if (sd->nat >= SLAB_MIN_ATOMS)
1293 /* Get the center of the slabs reference and current coordinates */
1294 get_center(sd->ref, sd->weight, sd->nat, ref_center);
1295 get_center(sd->x , sd->weight, sd->nat, act_center);
1296 if (rotg->eFittype == erotgFitNORM)
1298 /* Normalize every coordinate to it's reference coordinate length
1299 * prior to performing the fit */
1300 for (i=0; i<sd->nat;i++) /* Center */
1302 rvec_dec(sd->ref[i], ref_center);
1303 rvec_dec(sd->x[i] , act_center);
1304 /* Normalize x_i such that it gets the same length as ref_i */
1305 svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
1307 /* We already subtracted the centers */
1308 clear_rvec(ref_center);
1309 clear_rvec(act_center);
1311 fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat, ref_center, act_center, rotg->vec);
1312 fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
1315 fprintf(fp , "\n");
1317 #undef SLAB_MIN_ATOMS
1321 /* Get the shifts such that each atom is within closest
1322 * distance to its position at the last NS time step after shifting.
1323 * If we start with a whole structure, and always keep track of
1324 * shift changes, the structure will stay whole this way */
1325 static void get_extra_shifts(
1326 t_rotgrp *rotg,
1327 int npbcdim,
1328 matrix box,
1329 rvec *xc)
1331 int i,m,d;
1332 rvec dx;
1333 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
1336 erg=rotg->enfrotgrp;
1338 for (i=0; i < rotg->nat; i++)
1339 clear_ivec(erg->xc_eshifts[i]);
1341 for (i=0; i<rotg->nat; i++)
1343 /* The distance this atom moved since the last time step */
1344 /* If this is more than just a bit, it has changed its home pbc box */
1345 rvec_sub(xc[i],erg->xc_old[i],dx);
1347 for(m=npbcdim-1; m>=0; m--)
1349 while (dx[m] < -0.5*box[m][m])
1351 for(d=0; d<DIM; d++)
1352 dx[d] += box[m][d];
1353 erg->xc_eshifts[i][m]++;
1355 while (dx[m] >= 0.5*box[m][m])
1357 for(d=0; d<DIM; d++)
1358 dx[d] -= box[m][d];
1359 erg->xc_eshifts[i][m]--;
1366 /* Assemble the coordinates such that every node has all of them */
1367 static void get_coordinates(
1368 t_commrec *cr,
1369 t_rotgrp *rotg,
1370 rvec x[],
1371 bool bNeedShiftsUpdate, /* NS step, the shifts have changed */
1372 matrix box)
1374 int i, ii, j, l;
1375 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
1378 erg=rotg->enfrotgrp;
1380 GMX_MPE_LOG(ev_get_coords_start);
1382 /* Zero out the collective coordinate array */
1383 clear_rvecs(rotg->nat, erg->xc);
1385 /* Put the local coordinates that this node has into the right place of
1386 * the collective array. */
1387 for (l=0; l<erg->nat_loc; l++)
1389 /* Local index of a rotation group atom */
1390 ii = erg->ind_loc[l];
1391 /* Index of this rotation group atom w.r.t. the whole rotation group */
1392 j = erg->xc_ref_ind[l];
1393 /* Sort the current x-coordinates into the right place of the array: */
1394 copy_rvec(x[ii], erg->xc[j]);
1396 /* Add the arrays from all nodes together */
1397 if (PAR(cr))
1398 gmx_sum(DIM*rotg->nat, erg->xc[0], cr);
1400 /* To make the molecule whole, start with a whole structure and each
1401 * step move the assembled coordinates at closest distance to the positions
1402 * from the last step. First shift the coordinates with the saved shift
1403 * vectors (these are 0 when this routine is called for the first time!) */
1404 ed_shift_coords(box, erg->xc, erg->xc_shifts, rotg->nat);
1406 /* Now check if some shifts changed since the last step.
1407 * This only needs to be done when the shifts are expected to have changed,
1408 * i.e. after neighboursearching */
1409 if (bNeedShiftsUpdate)
1411 get_extra_shifts(rotg, 3, box, erg->xc);
1413 /* Shift with the additional shifts such that we get a whole molecule now */
1414 ed_shift_coords(box, erg->xc, erg->xc_eshifts, rotg->nat);
1416 /* Add the shift vectors together for the next time step */
1417 for (i=0; i<rotg->nat; i++)
1419 erg->xc_shifts[i][XX] += erg->xc_eshifts[i][XX];
1420 erg->xc_shifts[i][YY] += erg->xc_eshifts[i][YY];
1421 erg->xc_shifts[i][ZZ] += erg->xc_eshifts[i][ZZ];
1424 /* Store current correctly-shifted coordinates for comparison in the next NS time step */
1425 for (i=0; i<rotg->nat; i++)
1426 copy_rvec(erg->xc[i],erg->xc_old[i]);
1429 GMX_MPE_LOG(ev_get_coords_finish);
1433 static inline void shift_single_coord(matrix box, rvec x, const ivec is)
1435 int tx,ty,tz;
1438 tx=is[XX];
1439 ty=is[YY];
1440 tz=is[ZZ];
1442 if(TRICLINIC(box))
1444 x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
1445 x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
1446 x[ZZ] += tz*box[ZZ][ZZ];
1447 } else
1449 x[XX] += tx*box[XX][XX];
1450 x[YY] += ty*box[YY][YY];
1451 x[ZZ] += tz*box[ZZ][ZZ];
1456 #define round(a) (int)(a+0.5)
1457 /* For a local atom determine the relevant slabs, i.e. slabs in
1458 * which the gaussian is larger than min_gaussian
1460 static int get_single_atom_gaussians(
1461 rvec curr_x,
1462 t_commrec *cr,
1463 t_rotgrp *rotg)
1465 int slab, homeslab;
1466 real dist;
1467 real g;
1468 int count = 0;
1469 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
1472 erg=rotg->enfrotgrp;
1474 /* The distance of the atom to the coordinate center (where the
1475 * slab with index 0) is */
1476 dist = iprod(rotg->vec, curr_x);
1478 /* Determine the 'home' slab of this atom: */
1479 homeslab = round(dist / rotg->slab_dist);
1481 /* First determine the weight in the atoms home slab: */
1482 g = gaussian_weight(curr_x, rotg, homeslab);
1484 erg->gn_atom[count] = g;
1485 erg->gn_slabind[count] = homeslab;
1486 count++;
1489 /* Determine the max slab */
1490 slab = homeslab;
1491 while (g > rotg->min_gaussian)
1493 slab++;
1494 g = gaussian_weight(curr_x, rotg, slab);
1495 erg->gn_slabind[count]=slab;
1496 erg->gn_atom[count]=g;
1497 count++;
1499 count--;
1501 /* Determine the max slab */
1502 slab = homeslab;
1505 slab--;
1506 g = gaussian_weight(curr_x, rotg, slab);
1507 erg->gn_atom[count]=g;
1508 erg->gn_slabind[count]=slab;
1509 count++;
1511 while (g > rotg->min_gaussian);
1512 count--;
1514 return count;
1518 static real do_flex2_lowlevel(
1519 t_rotgrp *rotg,
1520 matrix rotmat,
1521 real sigma, /* The Gaussian width sigma */
1522 rvec x[],
1523 bool bCalcTorque,
1524 matrix box,
1525 t_commrec *cr)
1527 int count,i,ic,ii,l,m,n,islab,ipgrp;
1528 rvec dr; /* difference vector between actual and reference coordinate */
1529 real V; /* The rotation potential energy */
1530 real gaussian; /* Gaussian weight */
1531 real gaussian_xi;
1532 real beta;
1533 rvec curr_x; /* particle coordinate */
1534 rvec xi;
1535 rvec curr_x_rel; /* particle coordinate relative to COG */
1536 rvec curr_COG; /* the current slab's center of geometry (COG) */
1537 rvec ref_x, ref_x_cpy; /* the reference particle coordinate */
1538 rvec ref_COG; /* the reference slab's COG */
1539 rvec r, yi, tmp;
1540 rvec force_n; /* Single force from slab n on one atom */
1541 rvec s_ii;
1542 real inv_norm_ii;
1543 real sdotr_ii; /* s_ii.r_ii */
1544 real tmp_s;
1545 rvec tmp_v, tmp_n1_v, tmp_n2_v, tmp_n3_v, tmp_n4_v;
1546 rvec sum_i1;
1547 real sum_i2;
1548 rvec s_i;
1549 real inv_norm_i;
1550 real sdotr_i; /* s_i.r_i */
1551 real gauss_ratio; /* g_n(x_ii)/Sum_i(g_n(x_i)*/
1552 real beta_sigma; /* beta/sigma^2 */
1553 rvec sum_f_ii; /* force on one atom summed over all slabs */
1554 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
1557 erg=rotg->enfrotgrp;
1559 /********************************************************/
1560 /* Main loop over all local atoms of the rotation group */
1561 /********************************************************/
1562 V = 0.0;
1563 for (l=0; l<erg->nat_loc; l++)
1565 /* Local index of a rotation group atom */
1566 ii = erg->ind_loc[l];
1567 /* Index of this rotation group atom with respect to the whole rotation group */
1568 ipgrp = erg->xc_ref_ind[l];
1570 /* Current coordinate of this atom: x[ii][XX/YY/ZZ] */
1571 copy_rvec(x[ii], curr_x);
1572 /* Shift this atom such that it is near its reference */
1573 shift_single_coord(box, curr_x, erg->xc_shifts[ipgrp]);
1575 /* Determine the slabs to loop over, i.e. the ones with contributions
1576 * larger than min_gaussian */
1577 count = get_single_atom_gaussians(curr_x, cr, rotg);
1579 clear_rvec(sum_f_ii);
1581 /* Loop over the relevant slabs for this atom */
1582 for (ic=0; ic < count; ic++)
1584 n = erg->gn_slabind[ic];
1586 /* Get the precomputed Gaussian value of curr_slab for curr_x */
1587 gaussian = erg->gn_atom[ic];
1589 islab = n+erg->slab_max_nr; /* slab index */
1591 /* The (unrotated) reference coordinate of this atom is copied to ref_x: */
1592 copy_rvec(erg->xc_ref[ipgrp], ref_x);
1593 beta = calc_beta(curr_x, rotg,n);
1594 /* The center of geometry (COG) of this slab is copied to curr_COG: */
1595 copy_rvec(erg->slab_center[islab], curr_COG);
1596 /* The reference COG of this slab is copied to ref_COG: */
1597 copy_rvec(erg->slab_center_ref[islab], ref_COG);
1599 /* Rotate the reference coordinate around the rotation axis through this slab's reference COG */
1600 /* 1. Subtract the reference slab COG from the reference coordinate i */
1601 rvec_sub(ref_x, ref_COG, ref_x); /* ref_x =y_ii-y_0 */
1602 /* 2. Rotate reference coordinate around origin: */
1603 copy_rvec(ref_x, ref_x_cpy);
1604 mvmul(rotmat, ref_x_cpy, ref_x); /* ref_x = r_ii = Omega.(y_ii-y_0) */
1606 /* Now subtract the slab COG from current coordinate i */
1607 rvec_sub(curr_x, curr_COG, curr_x_rel); /* curr_x_rel = x_ii-x_0 */
1609 /* Force on atom i is based on difference vector between current coordinate and rotated reference coordinate */
1610 /* Difference vector between current and reference coordinate: */
1611 rvec_sub(curr_x_rel, ref_x, dr); /* dr=(x_ii-x_0)-Omega.(y_ii-y_0) */
1613 cprod(rotg->vec, curr_x_rel, s_ii); /* s_ii = v x (x_ii-x_0) */
1614 inv_norm_ii=1.0/norm(s_ii); /* inv_norm_ii = 1/|v x (x_ii-x_0)| */
1615 unitv(s_ii, s_ii); /* s_ii = v x (x_ii-x_0)/|v x (x_ii-x_0)| */
1616 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) */
1618 /*********************************/
1619 /* Add to the rotation potential */
1620 /*********************************/
1621 V += 0.5*rotg->k*gaussian*sqr(sdotr_ii);
1623 tmp_s=gaussian*sdotr_ii*inv_norm_ii;
1624 svmul(sdotr_ii, s_ii, tmp_n1_v);
1625 rvec_sub(ref_x, tmp_n1_v, tmp_n1_v);
1626 svmul(tmp_s, tmp_n1_v, tmp_n1_v);
1628 clear_rvec(sum_i1);
1629 sum_i2=0.0;
1631 for (i=erg->firstatom[n]; i<=erg->lastatom[n]; i++)
1633 /* Coordinate xi of this atom */
1634 copy_rvec(erg->xc[i],xi);
1636 gaussian_xi = gaussian_weight(xi,rotg,n);
1638 /* Calculate r_i for atom i and slab n: */
1639 /* Unrotated reference coordinate y_i: */
1640 copy_rvec(erg->xc_ref_sorted[i],yi);
1642 /* COG y0 for this slab: */
1643 /* The reference COG of this slab is still in ref_COG */
1644 rvec_sub(yi, ref_COG, tmp); /* tmp = y_i - y_0 */
1645 mvmul(rotmat, tmp, r); /* r = Omega*(y_i - y_0) */
1646 rvec_sub(xi, curr_COG, tmp); /* tmp = (x_i - x_0) */
1647 cprod(rotg->vec, tmp, s_i); /* s_i = v x (x_i - x_0) */
1648 inv_norm_i=1.0/norm(s_i); /* 1/|v x (x_i - x_0)| */
1649 unitv(s_i, s_i); /* s_i = (v x (x_i-x_0))/|v x (x_i-x_0)| */
1650 sdotr_i=iprod(s_i,r); /* sdotr_i = (s_i.r_i) */
1652 tmp_s=gaussian_xi*sdotr_i*inv_norm_i; /* tmp_s = g_n(xi)*(s_i.r_i)*1/norm */
1653 /* sum_11 */
1654 svmul(sdotr_i, s_i, tmp_v);
1655 rvec_sub(tmp_v, r, tmp_v);
1656 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) */
1657 rvec_add(sum_i1, tmp_v, sum_i1); /* n2 */
1658 sum_i2 += tmp_s*(iprod(r,s_ii)-(iprod(s_i,s_ii)*sdotr_i)); /* n3 */
1659 } /* now we have the sum-over-atoms (over i) for the ii-th atom in the n-th slab */
1661 gauss_ratio=gaussian/erg->slab_weights[islab];
1662 beta_sigma=beta/sqr(sigma);
1663 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)) */
1665 rvec_add(tmp_n1_v, tmp_n2_v, force_n); /* adding up the terms perpendicular to v and to x_ii-x0 */
1666 cprod(force_n, rotg->vec, tmp_v); /* now these are indeed perpendicular */
1667 svmul((-1.0*rotg->k), tmp_v, force_n); /* multiplying by -k* we've got the final tangent contribution */
1668 /* calculating the parallel contribution */
1669 svmul((-1.0)*rotg->k*gauss_ratio*beta_sigma*sum_i2,rotg->vec,tmp_n3_v);
1670 svmul(0.5*rotg->k*beta_sigma*gaussian*sqr(sdotr_ii),rotg->vec,tmp_n4_v);
1671 rvec_add(tmp_n3_v, tmp_n4_v, tmp_n3_v); /* tmp_n3_v contains the final parallel contribution */
1672 rvec_add(tmp_n3_v, force_n, force_n); /* force_n is the total force from slab n */
1673 /* sum the forces over slabs */
1674 rvec_add(sum_f_ii,force_n,sum_f_ii);
1676 /* Calculate the torque: */
1677 if (bCalcTorque)
1679 /* The force on atom ii from slab n only: */
1680 erg->slab_torque_v[islab] += torque(rotg->vec, force_n, curr_x, curr_COG);
1682 } /* END of loop over slabs */
1684 /* Store the additional force so that it can be added to the force
1685 * array after the normal forces have been evaluated */
1686 for (m=0; m<DIM; m++)
1687 erg->f_rot_loc[l][m] = sum_f_ii[m];
1689 #ifdef INFOF
1690 fprintf(stderr," FORCE on ATOM %d/%d = (%15.8f %15.8f %15.8f) \n",
1691 l,ii,rotg->sum_f_ii[XX], rotg->sum_f_ii[YY], rotg->sum_f_ii[ZZ]5);
1692 #endif
1693 } /* END of loop over local atoms */
1695 return V;
1699 static real do_flex_lowlevel(
1700 t_rotgrp *rotg,
1701 matrix rotmat,
1702 real sigma, /* The Gaussian width sigma */
1703 rvec x[],
1704 bool bCalcTorque,
1705 matrix box,
1706 t_commrec *cr)
1708 int count,i,ic,ii,iii,l,m,n,islab,ipgrp;
1709 rvec direction; /* the direction for the force on atom i */
1710 rvec sum_n1,sum_n2; /* Two contributions to the rotation force */
1711 rvec sum_i; /* Inner part of sum_n2 */
1712 rvec dummy1,tmp_f,s,tmp2;
1713 real u; /* direction*dr */
1714 real V; /* The rotation potential energy */
1715 real gaussian; /* Gaussian weight */
1716 real gaussian_xi;
1717 real fac,beta;
1718 rvec curr_x; /* particle coordinate */
1719 rvec xi;
1720 rvec curr_x_rel; /* particle coordinate relative to COG */
1721 rvec curr_COG; /* the current slab's center of geometry (COG) */
1722 rvec ref_x, ref_x_cpy; /* the reference particle coordinate */
1723 rvec ref_COG; /* the reference slab's COG */
1724 rvec r, yi, tmp;
1725 rvec force_n; /* Single force from slab n on one atom */
1726 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
1729 erg=rotg->enfrotgrp;
1731 /********************************************************/
1732 /* Main loop over all local atoms of the rotation group */
1733 /********************************************************/
1734 V = 0.0;
1735 for (l=0; l<erg->nat_loc; l++)
1737 /* Local index of a rotation group atom */
1738 ii = erg->ind_loc[l];
1739 /* Index of this rotation group atom with respect to the whole rotation group */
1740 ipgrp = erg->xc_ref_ind[l];
1742 /* Current coordinate of this atom: x[ii][XX/YY/ZZ] */
1743 copy_rvec(x[ii], curr_x);
1744 /* Shift this atom such that it is near its reference */
1745 shift_single_coord(box, curr_x, erg->xc_shifts[ipgrp]);
1747 /* Determine the slabs to loop over, i.e. the ones with contributions
1748 * larger than min_gaussian */
1749 count = get_single_atom_gaussians(curr_x, cr, rotg);
1751 clear_rvec(sum_n1);
1752 clear_rvec(sum_n2);
1754 /* Loop over the relevant slabs for this atom */
1755 for (ic=0; ic < count; ic++)
1757 n = erg->gn_slabind[ic];
1759 /* Get the precomputed Gaussian value of curr_slab for curr_x */
1760 gaussian = erg->gn_atom[ic];
1762 islab = n+erg->slab_max_nr; /* slab index */
1764 /* The (unrotated) reference coordinate of this atom is copied to ref_x: */
1765 copy_rvec(erg->xc_ref[ipgrp], ref_x);
1766 beta = calc_beta(curr_x, rotg,n);
1767 /* The center of geometry (COG) of this slab is copied to curr_COG: */
1768 copy_rvec(erg->slab_center[islab], curr_COG);
1769 /* The reference COG of this slab is copied to ref_COG: */
1770 copy_rvec(erg->slab_center_ref[islab], ref_COG);
1772 /* Rotate the reference coordinate around the rotation axis through this slab's reference COG */
1773 /* 1. Subtract the reference slab COG from the reference coordinate i */
1774 rvec_sub(ref_x, ref_COG, ref_x); /* ref_x =y_ii-y_0 */
1775 /* 2. Rotate reference coordinate around origin: */
1776 copy_rvec(ref_x, ref_x_cpy);
1777 mvmul(rotmat, ref_x_cpy, ref_x); /* ref_x = r_ii = Omega.(y_ii-y_0) */
1779 /* Now subtract the slab COG from current coordinate i */
1780 rvec_sub(curr_x, curr_COG, curr_x_rel); /* curr_x_rel = x_ii-x_0 */
1782 /* Calculate the direction of the actual force */
1783 cprod(rotg->vec, ref_x, direction);
1784 unitv(direction,direction);
1785 u = iprod(direction, curr_x_rel);
1787 /*********************************/
1788 /* Add to the rotation potential */
1789 /*********************************/
1790 V += 0.5*rotg->k*gaussian*sqr(u);
1792 /*************************************************/
1793 /* sum_n1 is the main contribution to the force: */
1794 /*************************************************/
1795 /* gn*u*(vec_s - 0.5*u*beta/(sigma^2)*vec_a) */
1796 svmul(0.5*u*beta/sqr(sigma),rotg->vec,dummy1);
1797 /* Typically 'direction' will be the largest part */
1798 rvec_sub(direction,dummy1,dummy1);
1799 svmul(gaussian*u,dummy1,dummy1);
1800 /* Sum over n: */
1801 rvec_add(sum_n1,dummy1,sum_n1);
1803 /*************************************************************/
1804 /* For the term sum_n2 we need to loop over all atoms again: */
1805 /*************************************************************/
1806 clear_rvec(sum_i);
1808 GMX_MPE_LOG(ev_inner_loop_start);
1810 for (i=erg->firstatom[n]; i<=erg->lastatom[n]; i++)
1812 /* Index of a rotation group atom */
1813 iii = rotg->ind[i];
1815 /* Coordinate xi of this atom */
1816 copy_rvec(erg->xc[i],xi);
1818 gaussian_xi = gaussian_weight(xi,rotg,n);
1820 /* Calculate r=Omega*(y_i-y_0) for atom i and slab n: */
1821 /* Unrotated reference coordinate y_i: */
1822 copy_rvec(erg->xc_ref[i],yi);
1824 /* COG y0 for this slab: */
1825 /* The reference COG of this slab is still in ref_COG */
1826 rvec_sub(yi, ref_COG, tmp); /* tmp = y_i - y_0 */
1827 mvmul(rotmat, tmp, r); /* r = Omega*(y_i - y_0) */
1828 cprod(rotg->vec, r, tmp); /* tmp = v x Omega*(y_i - y_0) */
1829 unitv(tmp, s); /* s = v x Omega*(y_i - y_0) / |s x Omega*(y_i - y_0)| */
1830 /* Now that we have si, let's calculate the i-sum: */
1831 /* tmp = x_0 - x_l */
1832 rvec_sub(curr_COG, curr_x, tmp);
1833 /* tmp2 = beta/sigma^2 * (s*(x_0 - x_l)) * v */
1834 svmul(beta*iprod(tmp, s)/sqr(sigma), rotg->vec, tmp2);
1835 /* tmp = s + tmp2 */
1836 rvec_add(tmp2, s, tmp);
1837 /* tmp2 = xi - x0 */
1838 rvec_sub(xi, curr_COG, tmp2);
1839 /* fac = gn * s*(xi - x0 - ri) */
1840 fac = gaussian_xi*iprod(s, tmp2);
1841 /* tmp2 = gn * s*(xi - x0) * [beta/sigma^2 * (s*(x_0 - x_l)) * v] */
1842 svmul(fac, tmp, tmp2);
1843 rvec_add(sum_i, tmp2, sum_i);
1845 } /* now we have the i-sum for this atom in this slab */
1846 svmul(gaussian/erg->slab_weights[islab], sum_i, sum_i);
1847 rvec_add(sum_n2, sum_i, sum_n2);
1849 GMX_MPE_LOG(ev_inner_loop_finish);
1851 /* Calculate the torque: */
1852 if (bCalcTorque)
1854 /* The force on atom ii from slab n only: */
1855 rvec_sub(sum_i, dummy1, force_n);
1856 svmul(rotg->k, force_n, force_n);
1857 erg->slab_torque_v[islab] += torque(rotg->vec, force_n, curr_x, curr_COG);
1859 } /* END of loop over slabs */
1861 /* Put both contributions together: */
1862 rvec_sub(sum_n2,sum_n1,tmp_f); /* F = -grad V */
1864 /* Store the additional force so that it can be added to the force
1865 * array after the normal forces have been evaluated */
1866 for(m=0; m<DIM; m++)
1867 erg->f_rot_loc[l][m] = rotg->k*tmp_f[m];
1869 #ifdef INFOF
1870 static bool bFirst=1;
1871 char buf[255];
1872 static FILE *fp;
1873 if (bFirst)
1875 sprintf(buf, "forces%d.txt", cr->nodeid);
1876 fp = fopen(buf, "w");
1877 bFirst = 0;
1880 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",
1881 l,ii,rotg->k*tmp_f[XX], rotg->k*tmp_f[YY], rotg->k*tmp_f[ZZ],
1882 -rotg->k*sum_n1[XX], -rotg->k*sum_n1[YY], -rotg->k*sum_n1[ZZ],
1883 rotg->k*sum_n2[XX], rotg->k*sum_n2[YY], rotg->k*sum_n2[ZZ]);
1884 #endif
1885 } /* END of loop over local atoms */
1887 return V;
1891 #ifdef PRINT_COORDS
1892 static void print_coordinates(t_commrec *cr, t_rotgrp *rotg, rvec x[], matrix box, int step)
1894 int i;
1895 static FILE *fp;
1896 static char buf[STRLEN];
1897 static bool bFirst=1;
1900 if (bFirst)
1902 sprintf(buf, "coords%d.txt", cr->nodeid);
1903 fp = fopen(buf, "w");
1904 bFirst = 0;
1907 fprintf(fp, "\nStep %d\n", step);
1908 fprintf(fp, "box: %f %f %f %f %f %f %f %f %f\n",
1909 box[XX][XX], box[XX][YY], box[XX][ZZ],
1910 box[YY][XX], box[YY][YY], box[YY][ZZ],
1911 box[ZZ][XX], box[ZZ][ZZ], box[ZZ][ZZ]);
1912 for (i=0; i<rotg->nat; i++)
1914 fprintf(fp, "%4d %f %f %f\n", i,
1915 erg->xc[i][XX], erg->xc[i][YY], erg->xc[i][ZZ]);
1917 fflush(fp);
1920 #endif
1923 static int projection_compare(const void *a, const void *b)
1925 sort_along_vec_t *xca, *xcb;
1928 xca = (sort_along_vec_t *)a;
1929 xcb = (sort_along_vec_t *)b;
1931 if (xca->xcproj < xcb->xcproj)
1932 return -1;
1933 else if (xca->xcproj > xcb->xcproj)
1934 return 1;
1935 else
1936 return 0;
1940 /* Sort the collective coordinates along the rotation vector */
1941 static void sort_collective_coordinates(
1942 t_rotgrp *rotg, /* Rotation group */
1943 sort_along_vec_t *data, /* Buffer for sorting the coordinates */
1944 rvec *buf) /* Buffer for sorting the coordinates */
1946 int i;
1947 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
1950 erg=rotg->enfrotgrp;
1952 for (i=0; i<rotg->nat; i++)
1954 data[i].xcproj = iprod(erg->xc[i], rotg->vec);
1955 data[i].ind = i;
1957 qsort(data, rotg->nat, sizeof(sort_along_vec_t), projection_compare);
1959 for (i=0; i<rotg->nat; i++)
1961 copy_rvec(erg->xc[data[i].ind], buf[i]);
1962 copy_rvec(erg->xc_ref[data[i].ind], erg->xc_ref_sorted[i]);
1963 erg->xc_sortind[i] = data[i].ind;
1966 for (i=0; i<rotg->nat; i++)
1968 copy_rvec(buf[i], erg->xc[i]);
1973 /* For each slab, get the first and the last index of the sorted atom
1974 * indices */
1975 static void get_firstlast_atom_per_slab(t_rotgrp *rotg, t_commrec *cr)
1977 int i,n;
1978 real beta;
1979 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
1982 erg=rotg->enfrotgrp;
1984 GMX_MPE_LOG(ev_get_firstlast_start);
1986 /* Find the first atom that needs to enter the calculation for each slab */
1987 n = erg->slab_first;
1988 i = 0; /* start with the first atom */
1991 /* Find the first coordinate that significantly contributes to this slab */
1992 do /* move forward in coordinate until a large enough beta is found */
1994 beta = calc_beta(erg->xc[i], rotg, n);
1995 i++;
1996 } while ((beta < -erg->max_beta) && (i < rotg->nat));
1997 i--;
1998 erg->firstatom[n] = i;
1999 /* Proceed to the next slab */
2000 n++;
2001 } while (n <= erg->slab_last);
2003 /* Find the last atom for each slab */
2004 n = erg->slab_last; /* start with last slab */
2005 i = rotg->nat-1; /* start with the last atom */
2008 do /* move backward in coordinate until a large enough beta is found */
2010 beta = calc_beta(erg->xc[i], rotg, n);
2011 i--;
2012 } while ((beta > erg->max_beta) && (i > -1));
2013 i++;
2014 erg->lastatom[n] = i;
2015 /* Proceed to the next slab */
2016 n--;
2017 } while (n >= erg->slab_first);
2019 GMX_MPE_LOG(ev_get_firstlast_finish);
2023 /* Enforced rotation with a flexible axis */
2024 static void do_flexible(
2025 t_commrec *cr,
2026 gmx_enfrot_t enfrot, /* Other rotation data */
2027 t_rotgrp *rotg, /* The rotation group */
2028 int g, /* Group number */
2029 real degangle, /* Angle theta_ref [degrees] */
2030 matrix rotmat, /* Rotation matrix for this angle */
2031 rvec x[], /* The local coordinates */
2032 matrix box,
2033 double t, /* Time in picoseconds */
2034 bool bDynBox, /* Is the box dynamic? */
2035 int step, /* The time step */
2036 bool bOutstep,
2037 FILE *fp_slabs,
2038 FILE *fp_torque,
2039 FILE *fp_angles)
2041 int l;
2042 real sigma; /* The Gaussian width sigma */
2043 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
2046 erg=rotg->enfrotgrp;
2048 /* Define the sigma value */
2049 sigma = 0.7*rotg->slab_dist;
2051 /* TODO: sort first and then determine the slab COMs just for the relevant atoms
2052 * in each slab */
2054 /* Determine the gaussian-weighted center of coordinates for all slabs,
2055 * also reallocate memory if the number of slabs grows (i.e. box expands) */
2056 get_slab_centers(rotg,erg->xc,box,cr,g,bDynBox,t,fp_slabs,bOutstep,FALSE);
2058 /* Clear the torque per slab from last time step: */
2059 for (l=0; l<2*erg->slab_max_nr+1; l++)
2060 erg->slab_torque_v[l] = 0.0;
2062 /* Sort the collective coordinates erg->xc along the rotation vector. This is
2063 * an optimization for the inner loop.
2065 sort_collective_coordinates(rotg, enfrot->data, enfrot->buf);
2067 /* Determine for each slab depending on the min_gaussian cutoff criterium,
2068 * a first and a last atom index inbetween stuff needs to be calculated */
2069 get_firstlast_atom_per_slab(rotg, cr);
2071 /* Call the rotational forces kernel */
2072 GMX_MPE_LOG(ev_flexll_start);
2073 if (rotg->eType == erotgFLEX1)
2074 erg->V = do_flex_lowlevel(rotg, rotmat, sigma, x, bOutstep, box, cr);
2075 else if (rotg->eType == erotgFLEX2)
2076 erg->V = do_flex2_lowlevel(rotg, rotmat, sigma, x, bOutstep, box, cr);
2077 else
2078 gmx_fatal(FARGS, "Unknown flexible rotation type");
2079 GMX_MPE_LOG(ev_flexll_finish);
2081 /* Determine actual angle of this slab by RMSD fit and output to file - Let's hope */
2082 /* this only happens once in a while, since this is not parallelized! */
2083 if (bOutstep && MASTER(cr))
2084 flex_fit_angle(g, rotg, t, degangle, fp_angles);
2088 /* Calculate the angle between reference and actual rotation group atom: */
2089 static void angle(t_rotgrp *rotg,
2090 rvec x_act,
2091 rvec x_ref,
2092 real *alpha,
2093 real *weight, /* atoms near the rotation axis should count less than atoms far away */
2094 rvec offset)
2096 rvec x , xr ; /* actual and reference coordinate in new coordinate system */
2097 rvec xp, xrp; /* dito, but projected on a plane perpendicular to pg->vec */
2098 real normxp; /* saved for efficiency reasons */
2099 rvec dum;
2100 real cosalpha; /* cos of angle between projected reference and actual coordinate */
2101 int sign;
2104 /* Move the center of coordinates to rot_offset: */
2105 rvec_sub(x_act, offset, x);
2106 rvec_sub(x_ref, offset, xr);
2108 /* Project xr and x into a plane through the origin perpendicular to rot_vec: */
2109 /* Project xr: xrp = xr - (vec * xr) * vec */
2110 svmul(iprod(rotg->vec, xr), rotg->vec, dum);
2111 rvec_sub(xr, dum, xrp);
2112 /* Project x: */
2113 svmul(iprod(rotg->vec, x), rotg->vec, dum);
2114 rvec_sub(x, dum, xp);
2116 /* Calculate the angle between the projected coordinates: */
2117 normxp = norm(xp); /* save for later use */
2118 cosalpha = iprod(xrp, xp) / (norm(xrp)*normxp);
2119 if (cosalpha < -1.0) cosalpha = -1.0;
2120 if (cosalpha > 1.0) cosalpha = 1.0;
2122 /* Retrieve some information about which vector precedes */
2123 cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
2125 if (iprod(rotg->vec, dum) >= 0)
2126 /* This will be the case when the reference group runs ahead. Thus the sign for the
2127 * angle of the actual group (which we are interested in) is negative = behind */
2128 sign = -1;
2129 else
2130 sign = 1;
2132 /* Return the angle in radians */
2133 *alpha = sign * acos(cosalpha);
2134 /* Also return the weight */
2135 *weight = normxp;
2139 /* Project first vector onto a plane perpendicular to the second vector
2140 * dr = dr - (dr.v)v
2142 static inline void project_onto_plane(rvec dr, const rvec v)
2144 rvec tmp;
2147 svmul(iprod(dr,v),v,tmp); /* tmp = (dr.v)v */
2148 rvec_dec(dr, tmp); /* dr = dr - (dr.v)v */
2152 /* Fixed rotation: The rotation reference group rotates around an axis */
2153 /* The atoms of the actual rotation group are attached with imaginary */
2154 /* springs to the reference atoms. */
2155 static void do_fixed(
2156 t_commrec *cr,
2157 t_rotgrp *rotg, /* The rotation group */
2158 matrix rotmat, /* rotary matrix */
2159 rvec x[], /* The coordinates (natoms) */
2160 t_pbc *pbc,
2161 double t, /* Time in picoseconds */
2162 int step, /* The time step */
2163 bool bTorque)
2165 int i,ii,m,iigrp;
2166 rvec dr;
2167 rvec curr_x; /* particle coordinate */
2168 rvec curr_x_pbc; /* particle coordinate with the right pbc representation
2169 * w.r.t. the reference coordinate xr */
2170 rvec tmp_f; /* Force */
2171 rvec xr, xrcpy; /* rotated (reference) particle coordinate */
2172 real alpha; /* a single angle between an actual and a reference coordinate */
2173 real weight; /* single weight for a single angle */
2174 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
2177 erg=rotg->enfrotgrp;
2179 /* Clear values from last time step */
2180 erg->V = 0.0;
2181 erg->fix_torque_v = 0.0;
2182 erg->fix_angles_v = 0.0;
2183 erg->fix_weight_v = 0.0;
2185 /* Loop over all local atoms of the rotation group */
2186 for (i=0; i<erg->nat_loc; i++)
2188 /* Index of a rotation group atom */
2189 ii = erg->ind_loc[i];
2190 /* Actual coordinate of this atom: x[ii][XX/YY/ZZ] */
2191 copy_rvec(x[ii], curr_x);
2193 /* Index of this rotation group atom with respect to the whole rotation group */
2194 iigrp = erg->xc_ref_ind[i];
2196 /* Copy the (unrotated) reference coordinate of this atom: */
2197 copy_rvec(erg->xc_ref[iigrp], xr);
2198 /* Rotate this atom around dislocated rotation axis: */
2199 /* Move rotation axis, so that it runs through the origin: */
2200 rvec_sub(xr, rotg->offset, xr);
2201 /* Rotate around the origin: */
2202 copy_rvec(xr, xrcpy);
2203 mvmul(rotmat, xrcpy, xr);
2204 /* And move back: */
2205 rvec_add(xr, rotg->offset, xr);
2206 /* Difference vector between reference and actual coordinate: */
2207 pbc_dx(pbc,xr,curr_x, dr);
2209 /* The reference coords are whole, therefore we can construct the
2210 * needed pbc image of curr_x from xr and dr: */
2211 rvec_sub(xr, dr, curr_x_pbc);
2213 if (rotg->eType==erotgFIXED_PLANE)
2214 project_onto_plane(dr, rotg->vec);
2216 /* Store the additional force so that it can be added to the force
2217 * array after the normal forces have been evaluated */
2218 for (m=0; m<DIM; m++)
2220 tmp_f[m] = rotg->k*dr[m];
2221 erg->f_rot_loc[i][m] = tmp_f[m];
2222 erg->V += 0.5*rotg->k*sqr(dr[m]);
2225 if (bTorque)
2227 /* Add to the torque of this rotation group */
2228 erg->fix_torque_v += torque(rotg->vec, tmp_f, curr_x_pbc, rotg->offset);
2230 /* Calculate the angle between reference and actual rotation group atom: */
2231 angle(rotg, curr_x_pbc, xr, &alpha, &weight, rotg->offset); /* angle in rad, weighted */
2232 erg->fix_angles_v += alpha * weight;
2233 erg->fix_weight_v += weight;
2234 /* Use the next two lines instead if you don't want weighting: */
2236 angles_v[g] += alpha;
2237 weight_v[g] += 1;
2240 /* Probably one does not want enforced rotation to influence */
2241 /* the virial. But if so, activate the following lines */
2243 if (MASTER(cr))
2245 Add the rotation contribution to the virial
2246 for(j=0; j<DIM; j++)
2247 for(m=0;m<DIM;m++)
2248 vir[j][m] += 0.5*f[ii][j]*dr[m];
2251 #ifdef INFOF
2252 fprintf(stderr," FORCE on ATOM %d = (%15.8f %15.8f %15.8f) torque=%15.8f\n",
2253 ii,erg->f_rot_loc[i][XX], erg->f_rot_loc[i][YY], erg->f_rot_loc[i][ZZ],erg->fix_torque_v);
2254 #endif
2255 } /* end of loop over local rotation group atoms */
2259 /* Fixed rotation, subtype follow_plane: Similar to fixed_plane, however
2260 * the centers of mass of the reference and current group are subtracted
2261 * from reference and current coordinates, respectively. This way the rotation
2262 * group can move around in the box and does not stick to its reference location */
2263 static void do_follow_plane(
2264 t_commrec *cr,
2265 t_rotgrp *rotg, /* The rotation group */
2266 matrix rotmat, /* rotary matrix */
2267 rvec x[], /* The coordinates (natoms) */
2268 matrix box,
2269 double t, /* Time in picoseconds */
2270 int step, /* The time step */
2271 bool bTorque)
2273 int l,ii,m,iigrp;
2274 rvec dr;
2275 rvec curr_x; /* particle coordinate */
2276 rvec tmp_f; /* Force */
2277 rvec xr, xrcpy; /* rotated (reference) particle coordinate */
2278 real alpha; /* a single angle between an actual and a reference coordinate */
2279 real weight; /* single weight for a single angle */
2280 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
2281 rvec zerovec;
2282 clear_rvec(zerovec);
2285 erg=rotg->enfrotgrp;
2287 /* Clear values from last time step */
2288 erg->V = 0.0;
2289 erg->fix_torque_v = 0.0;
2290 erg->fix_angles_v = 0.0;
2291 erg->fix_weight_v = 0.0;
2293 /* Loop over all local atoms of the rotation group */
2294 for (l=0; l<erg->nat_loc; l++)
2296 /* Index of a rotation group atom */
2297 ii = erg->ind_loc[l];
2299 /* Index of this rotation group atom with respect to the whole rotation group */
2300 iigrp = erg->xc_ref_ind[l];
2302 /* Actual coordinate of this atom: x[ii][XX/YY/ZZ] */
2303 copy_rvec(x[ii], curr_x);
2305 /* Shift this atom such that it is near its reference */
2306 shift_single_coord(box, curr_x, erg->xc_shifts[iigrp]);
2308 /* Subtract center of mass */
2309 rvec_sub(curr_x, rotg->offset, curr_x);
2311 /* Copy the (unrotated) reference coordinate of this atom: */
2312 rvec_sub(erg->xc_ref[iigrp], erg->xc_ref_center, xr);
2314 /* Rotate this atom around COM: */
2315 copy_rvec(xr, xrcpy);
2316 mvmul(rotmat, xrcpy, xr);
2317 /* Difference vector between reference and actual coordinate: */
2318 rvec_sub(xr, curr_x, dr);
2320 project_onto_plane(dr, rotg->vec);
2322 /* Store the additional force so that it can be added to the force
2323 * array after the normal forces have been evaluated */
2324 for (m=0; m<DIM; m++)
2326 tmp_f[m] = rotg->k*dr[m];
2327 erg->f_rot_loc[l][m] = tmp_f[m];
2328 erg->V += 0.5*rotg->k*sqr(dr[m]);
2331 if (bTorque)
2333 /* Add to the torque of this rotation group */
2334 erg->fix_torque_v += torque(rotg->vec, tmp_f, curr_x, zerovec);
2336 /* Calculate the angle between reference and actual rotation group atom: */
2337 angle(rotg, curr_x, xr, &alpha, &weight, zerovec); /* angle in rad, weighted */
2338 erg->fix_angles_v += alpha * weight;
2339 erg->fix_weight_v += weight;
2340 /* Use the next two lines instead if you don't want weighting: */
2342 angles_v[g] += alpha;
2343 weight_v[g] += 1;
2346 } /* end of loop over local rotation group atoms */
2350 extern void init_rot_group(
2351 FILE *fplog,t_commrec *cr,
2352 int g,t_rotgrp *rotg,
2353 rvec *x, /* the coordinates */
2354 gmx_mtop_t *mtop,
2355 int rot_type,
2356 FILE *out_slabs,
2357 matrix box)
2359 int i,ii;
2360 real box_d; /* The box diagonal (needed for maximum slab count) */
2361 char filename[255];/* File to save the reference coordinates in for enforced rotation */
2362 rvec f_box[3]; /* Box from reference file */
2363 rvec coord;
2364 t_trnheader header; /* Header information of reference file */
2365 bool bSame; /* Used for a check if box sizes agree */
2366 int nslabs; /* Maximum number of slabs that fit into simulation box */
2367 bool bFlex; /* Flexible rotation? */
2368 bool bColl; /* Use collective coordinates? */
2369 t_atom *atom;
2370 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
2373 bFlex = (rot_type == erotgFLEX1 || rot_type == erotgFLEX2);
2374 bColl = (bFlex || (rot_type==erotgFOLLOW_PLANE));
2376 erg=rotg->enfrotgrp;
2378 snew(erg->xc_ref , rotg->nat);
2379 snew(erg->xc_ref_ind, rotg->nat);
2380 snew(erg->f_rot_loc , rotg->nat);
2381 if (bFlex && rotg->eFittype == erotgFitNORM)
2382 snew(erg->xc_ref_length, rotg->nat); /* in case fit type NORM is chosen */
2383 if (rot_type == erotgFOLLOW_PLANE)
2384 snew(erg->mc, rotg->nat);
2386 /* xc_ref_ind needs to be set to identity in the serial case */
2387 if (!PAR(cr))
2388 for (i=0; i<rotg->nat; i++)
2389 erg->xc_ref_ind[i] = i;
2391 /* Allocate space for collective coordinates if used */
2392 if (bColl)
2394 snew(erg->xc , rotg->nat);
2395 snew(erg->xc_norm , rotg->nat);
2396 snew(erg->xc_old , rotg->nat);
2397 snew(erg->xc_shifts , rotg->nat);
2398 snew(erg->xc_eshifts, rotg->nat);
2401 /* Enforced rotation with flexible axis */
2402 if (bFlex)
2404 /* Calculate maximum beta value from minimum gaussian (performance opt.) */
2405 erg->max_beta = calc_beta_max(rotg->min_gaussian, rotg->slab_dist);
2407 /* A maximum of (box diagonal)/(slab distance) slabs are possible */
2408 box_d = diagonal_length(box);
2409 erg->slab_max_nr = (int) ceil(box_d/rotg->slab_dist);
2410 nslabs = 2*erg->slab_max_nr + 1;
2411 if (MASTER(cr))
2412 fprintf(fplog, "Enforced rotation: allocating memory to store data for %d slabs (rotation group %d).\n",nslabs,g);
2413 snew(erg->slab_center , nslabs);
2414 snew(erg->slab_center_ref, nslabs);
2415 snew(erg->slab_weights , nslabs);
2416 snew(erg->slab_torque_v , nslabs);
2417 snew(erg->slab_data , nslabs);
2418 erg->gn_alloc = nslabs;
2419 snew(erg->gn_atom , nslabs);
2420 snew(erg->gn_slabind , nslabs);
2421 for (i=0; i<nslabs; i++)
2423 snew(erg->slab_data[i].x , rotg->nat);
2424 snew(erg->slab_data[i].ref , rotg->nat);
2425 snew(erg->slab_data[i].weight, rotg->nat);
2427 snew(erg->xc_ref_sorted, rotg->nat);
2428 snew(erg->xc_sortind , rotg->nat);
2429 snew(erg->firstatom , nslabs);
2430 snew(erg->lastatom , nslabs);
2433 /* Read in rotation reference coordinates from file, if it exists.
2434 * If not, write out the initial rotation group coordinates as reference coordinates */
2435 if (MASTER(cr))
2437 /* Save the reference coordinates to trr */
2438 /* Make a trr for each rotation group */
2439 sprintf(filename, "ref_%d_%s.trr", g, erotg_names[rotg->eType]);
2440 if (gmx_fexist(filename)) /* Read rotation reference coordinates from file */
2442 fprintf(fplog, "Enforced rotation: found reference coordinate file %s.\n", filename);
2443 read_trnheader(filename, &header);
2444 if (rotg->nat != header.natoms)
2445 gmx_fatal(FARGS,"Number of atoms in coordinate file %s (%d) does not match the number of atoms in rotation group (%d)!\n",
2446 filename, header.natoms, rotg->nat);
2447 read_trn(filename, &header.step, &header.t, &header.lambda, f_box, &header.natoms, erg->xc_ref, NULL, NULL);
2448 fprintf(fplog , "Enforced rotation: read reference coordinates for group %d from %s.\n", g, filename);
2449 /* Check if the box is unchanged and output a warning if not: */
2450 bSame = TRUE;
2451 for (i=0; i<DIM; i++)
2452 for (ii=0; ii<DIM; ii++)
2453 if (f_box[i][ii] != box[i][ii]) bSame = FALSE;
2455 if (!bSame)
2457 sprintf(warn_buf, "Enforced rotation: Box size in reference file %s differs from actual box size!", filename);
2458 warning(NULL);
2459 pr_rvecs(stderr,0,"Your box is:",box ,3);
2460 pr_rvecs(fplog ,0,"Your box is:",box ,3);
2461 pr_rvecs(stderr,0,"Box in file:",f_box,3);
2462 pr_rvecs(fplog ,0,"Box in file:",f_box,3);
2465 if (g != header.step)
2466 { /* We use step to indicate the number of the rotation group here */
2467 sprintf(warn_buf,"Coordinates from %s will be used for rotation group %d", filename, g);
2468 warning(NULL);
2471 else /* Save the initial coordinates of the rotation group as reference */
2473 for(i=0; i<rotg->nat; i++)
2475 ii = rotg->ind[i];
2476 copy_rvec(x[ii], erg->xc_ref[i]);
2478 write_trn(filename,g,0.0,0.0,box,rotg->nat,erg->xc_ref,NULL,NULL);
2479 fprintf(fplog, "Enforced rotation: saved %d coordinates of group %d to %s.\n",
2480 rotg->nat, g, filename);
2482 if (bColl)
2484 /* Save the original (whole) coordinates such that later the
2485 * molecule can always be made whole again */
2486 for (i=0; i<rotg->nat; i++)
2488 ii = rotg->ind[i];
2489 copy_rvec(x[ii], erg->xc_old[i]);
2493 #ifdef GMX_MPI
2494 /* Copy reference coordinates to all PP nodes */
2495 if (PAR(cr))
2497 gmx_bcast(rotg->nat*sizeof(erg->xc_ref[0]), erg->xc_ref, cr);
2498 if (bColl)
2499 gmx_bcast(rotg->nat*sizeof(erg->xc_old[0]),erg->xc_old, cr);
2501 #endif
2503 if (bFlex)
2505 /* Flexible rotation: determine the reference COGs for the rest of the simulation */
2506 get_slab_centers(rotg,erg->xc_ref,box,cr,g,TRUE,-1,out_slabs,1,TRUE);
2508 /* Also save the center of geometry of the reference structure (needed for fitting): */
2509 get_center(erg->xc_ref, NULL, rotg->nat, erg->xc_ref_center);
2511 /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
2512 if (rotg->eFittype == erotgFitNORM)
2514 for (i=0; i<rotg->nat; i++)
2516 rvec_sub(erg->xc_ref[i], erg->xc_ref_center, coord);
2517 erg->xc_ref_length[i] = norm(coord);
2522 if (rot_type == erotgFOLLOW_PLANE)
2524 /* We need to copy the masses for later usage */
2525 for (i=0; i<rotg->nat; i++)
2527 gmx_mtop_atomnr_to_atom(mtop,rotg->ind[i],&atom);
2528 erg->mc[i] = atom->m;
2530 /* Save the center of mass of the reference structure: */
2531 get_center(erg->xc_ref, erg->mc, rotg->nat, erg->xc_ref_center);
2537 static void make_local_rotation_group(gmx_ga2la_t ga2la,
2538 t_rotgrp *rotg,int start,int end)
2540 int i,ii;
2541 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
2544 erg=rotg->enfrotgrp;
2546 erg->nat_loc = 0;
2547 for(i=0; i<rotg->nat; i++) {
2548 ii = rotg->ind[i];
2549 if (!ga2la_home(ga2la,ii,&ii)) {
2550 ii = -1;
2553 if (ii >= start && ii < end) {
2554 /* This is a home atom, add it to the local rotation group */
2555 if (erg->nat_loc >= erg->nalloc_loc) {
2556 erg->nalloc_loc = over_alloc_dd(erg->nat_loc+1);
2557 srenew(erg->ind_loc,erg->nalloc_loc);
2559 erg->ind_loc[erg->nat_loc] = ii;
2560 /* Copy the reference coordinates */
2561 if (erg->xc_ref)
2563 /* Remember which of the x_rotref coordinates are local: */
2564 erg->xc_ref_ind[erg->nat_loc]=i; /* i is the number of the atom with respect to the whole rotation group */
2565 /* pg->ind[i] would be the number with respect to the whole system! */
2567 erg->nat_loc++;
2572 void dd_make_local_rotation_groups(gmx_domdec_t *dd,t_rot *rot,t_mdatoms *md)
2574 gmx_ga2la_t ga2la;
2575 int g;
2578 ga2la = dd->ga2la;
2580 for(g=0; g<rot->ngrp; g++)
2581 make_local_rotation_group(ga2la,&rot->grp[g],md->start,md->start+md->homenr);
2585 void init_rot(FILE *fplog,t_inputrec *ir,int nfile,const t_filenm fnm[],
2586 t_commrec *cr, matrix box, rvec *x, gmx_mtop_t *mtop,
2587 const output_env_t oenv, unsigned long Flags)
2589 t_rot *rot;
2590 t_rotgrp *rotg;
2591 int g;
2592 int nat_max=0; /* Size of biggest rotation group */
2593 bool bRerun;
2594 bool bFlex=FALSE;
2595 gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
2596 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
2599 if ( (PAR(cr)) && !DOMAINDECOMP(cr) )
2600 gmx_fatal(FARGS, "Enforced rotation is only implemented for domain decomposition!");
2602 rot = ir->rot;
2603 snew(rot->enfrot, 1);
2604 er=rot->enfrot;
2606 /* Output every step for reruns */
2607 bRerun = Flags & MD_RERUN;
2608 if (bRerun)
2610 if (fplog)
2611 fprintf(fplog, "Enforced rotation: rerun - will write rotation output every available step.\n");
2612 rot->nstrout = 1;
2613 rot->nsttout = 1;
2616 er->out_slabs = NULL;
2617 if (MASTER(cr))
2618 er->out_slabs = open_slab_out(rot, opt2fn("-rs",nfile,fnm));
2620 for(g=0; g<rot->ngrp; g++)
2622 rotg = &rot->grp[g];
2624 if (fplog)
2625 fprintf(fplog,"Enforced rotation: group %d type '%s'\n", g, erotg_names[rotg->eType]);
2627 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2)
2628 bFlex = TRUE;
2630 if (rotg->nat > 0)
2632 /* Allocate space for the rotation group's data: */
2633 snew(rotg->enfrotgrp, 1);
2634 erg = rotg->enfrotgrp;
2636 nat_max=max(nat_max, rotg->nat);
2638 if (PAR(cr))
2640 erg->nat_loc = 0;
2641 erg->nalloc_loc = 0;
2642 erg->ind_loc = NULL;
2644 else
2646 erg->nat_loc = rotg->nat;
2647 erg->ind_loc = rotg->ind;
2649 init_rot_group(fplog,cr,g,rotg,x,mtop,rotg->eType,er->out_slabs,box);
2653 /* Allocate space for enforced rotation buffer variables */
2654 er->bufsize = nat_max;
2655 snew(er->data, nat_max);
2656 snew(er->buf , nat_max);
2658 /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
2659 er->mpi_bufsize = 4*rot->ngrp; /* To start with */
2660 snew(er->mpi_inbuf , er->mpi_bufsize);
2661 snew(er->mpi_outbuf, er->mpi_bufsize);
2663 /* Only do I/O on the MASTER */
2664 er->out_angles = NULL;
2665 er->out_rot = NULL;
2666 er->out_torque = NULL;
2667 if (MASTER(cr))
2669 er->out_rot = open_rot_out(opt2fn("-ro",nfile,fnm), rot, oenv, Flags);
2670 if (bFlex)
2672 if (rot->nstrout > 0)
2673 er->out_angles = open_angles_out(rot, opt2fn("-ra",nfile,fnm));
2674 if (rot->nsttout > 0)
2675 er->out_torque = open_torque_out(rot, opt2fn("-rt",nfile,fnm));
2681 extern void do_rotation(
2682 t_commrec *cr,
2683 t_inputrec *ir,
2684 matrix box,
2685 rvec x[],
2686 real t,
2687 int step,
2688 gmx_wallcycle_t wcycle,
2689 bool bNS)
2691 int g;
2692 t_pbc pbc;
2693 t_rot *rot;
2694 t_rotgrp *rotg;
2695 bool outstep_torque;
2696 float cycles_rot;
2697 real degangle;
2698 matrix rotmat;
2699 gmx_enfrot_t er; /* Pointer to the enforced rotation buffer variables */
2700 gmx_enfrotgrp_t erg; /* Pointer to enforced rotation group data */
2701 #ifdef TAKETIME
2702 double t0;
2703 #endif
2706 rot=ir->rot;
2707 er=rot->enfrot;
2709 /* At which time steps do we want to output the torque */
2710 outstep_torque = do_per_step(step, rot->nsttout);
2712 /* Output time into rotation output file */
2713 if (outstep_torque && MASTER(cr))
2714 fprintf(er->out_rot, "%12.3e",t);
2716 /* First do ALL the communication! */
2717 for(g=0; g<rot->ngrp; g++)
2719 rotg = &rot->grp[g];
2720 erg=rotg->enfrotgrp;
2722 /* Transfer the rotation group's coordinates such that every node has all of them.
2723 * Every node contributes its local coordinates x and stores it in
2724 * the collective erg->xc array. */
2725 if (rotg->eType == erotgFLEX1 || rotg->eType == erotgFLEX2 || rotg->eType == erotgFOLLOW_PLANE)
2726 get_coordinates(cr, rotg, x, bNS, box);
2727 if (rotg->eType == erotgFOLLOW_PLANE)
2729 /* Get the center of mass of the rotation group and store in rotg->offset */
2730 get_center(erg->xc, erg->mc, rotg->nat, rotg->offset);
2734 /* Done communicating, we can start to count cycles now ... */
2735 wallcycle_start(wcycle, ewcROT);
2736 GMX_MPE_LOG(ev_rotcycles_start);
2738 #ifdef TAKETIME
2739 t0 = MPI_Wtime();
2740 #endif
2742 for(g=0; g<rot->ngrp; g++)
2744 rotg = &rot->grp[g];
2745 erg=rotg->enfrotgrp;
2747 degangle = rotg->rate * t; /* angle of rotation for this group: */
2748 if (outstep_torque && MASTER(cr))
2749 fprintf(er->out_rot, "%12.4f", degangle);
2750 /* Calculate the rotation matrix for this angle: */
2751 calc_rotmat(rotg->vec,degangle,rotmat);
2753 switch(rotg->eType)
2755 case erotgFIXED:
2756 case erotgFIXED_PLANE:
2757 set_pbc(&pbc,ir->ePBC,box);
2758 do_fixed(cr,rotg,rotmat,x,&pbc,t,step,outstep_torque);
2759 break;
2760 case erotgFOLLOW_PLANE:
2761 do_follow_plane(cr,rotg,rotmat,x,box,t,step,outstep_torque);
2762 break;
2763 case erotgFLEX1:
2764 case erotgFLEX2:
2765 do_flexible(cr,er,rotg,g,degangle,rotmat,x,box,t,DYNAMIC_BOX(*ir),step,outstep_torque,
2766 er->out_slabs,er->out_torque,er->out_angles);
2767 break;
2768 default:
2769 break;
2773 #ifdef TAKETIME
2774 if (MASTER(cr))
2775 fprintf(stderr, "Enforced rotation calculation (step %d) took %g seconds.\n", step, MPI_Wtime()-t0);
2776 #endif
2778 /* Stop the cycle counter and add to the force cycles for load balancing */
2779 cycles_rot = wallcycle_stop(wcycle,ewcROT);
2780 if (DOMAINDECOMP(cr) && wcycle)
2781 dd_cycles_add(cr->dd,cycles_rot,ddCyclF);
2782 GMX_MPE_LOG(ev_rotcycles_finish);