3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
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
32 * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
41 #include "gmx_wallcycle.h"
50 #include "mtop_util.h"
54 #include "gmx_ga2la.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 */
72 static double** allocate_square_matrix(int dim
)
86 static void free_square_matrix(double** mat
, int dim
)
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 */
105 /* Fill the MPI buffer with stuff to reduce: */
109 for (g
=0; g
< rot
->ngrp
; g
++)
112 nslabs
= 2*rotg
->slab_max_nr
+1;
113 rot
->inbuf
[count
++] = rotg
->V
;
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
;
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
];
140 MPI_Reduce(rot
->inbuf
, rot
->outbuf
, count
, GMX_MPI_REAL
, MPI_SUM
, MASTERRANK(cr
), cr
->mpi_comm_mygroup
);
142 /* Copy back the reduced data from the buffer on the master */
146 for (g
=0; g
< rot
->ngrp
; g
++)
149 nslabs
= 2*rotg
->slab_max_nr
+1;
150 rotg
->V
= rot
->outbuf
[count
++];
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
++];
162 for (i
=0; i
<nslabs
; i
++)
163 rotg
->slab_torque_v
[i
] = rot
->outbuf
[count
++];
175 /* Av. angle and total torque for each rotation group */
176 for (g
=0; g
< rot
->ngrp
; 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
,
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
)
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 */
227 /* Loop over enforced rotation groups (usually 1, though)
228 * Apply the forces from rotation potentials */
229 for (g
=0; g
<rot
->ngrp
; 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
];
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
)
254 copy_rvec(box
[XX
],diag
);
255 rvec_inc(diag
,box
[YY
]);
256 rvec_inc(diag
,box
[ZZ
]);
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 */
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 */
306 /* If the box grows during the simulation, we might need more memory */
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
)
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 */
340 k
= rotg
->slab_max_nr
;
341 rotg
->slab_first
= 1;
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 */
367 rotg
->slab_first
= j
;
372 /* At first time step: save the COGs of the reference structure */
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
]);
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(
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
);
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: */
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
;
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
;
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
;
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");
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) */
456 /* Subtract offset */
457 rvec_sub(x
,offset
,vectmp
);
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
)
487 sprintf(sbuf
, "%s%d", str
, g
);
488 fprintf(fp
, "%12s", sbuf
);
492 static FILE *open_output_file(const char *fn
, int steps
)
497 fp
= ffopen(fn
, "w");
499 fprintf(fp
, "# Output is written every %d time steps.\n\n", steps
);
505 /* Open output file for slab COG data. Call on master only */
506 static FILE *open_slab_out(t_rot
*rot
, const char *fn
)
513 for (g
=0; g
<rot
->ngrp
; g
++)
516 if (rotg
->eType
== erotgFLEX1
|| rotg
->eType
== erotgFLEX2
)
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
);
526 fprintf(fp
, "# The following columns will have the syntax: (COG = center of geometry, gaussian weighted)\n");
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");
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
,
555 char **setname
,buf
[50];
558 if (Flags
& MD_APPENDFILES
)
560 fp
= gmx_fio_fopen(fn
,"a");
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
++)
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
);
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
]);
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
]);
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
]);
598 fprintf(fp
, "#\n# Legend for the following data columns:\n");
600 print_aligned_short(fp
, "t");
602 snew(setname
, 4*rot
->ngrp
);
604 for (g
=0; g
<rot
->ngrp
; g
++)
607 sprintf(buf
, "theta_ref%d (degree)", g
);
608 print_aligned_group(fp
, "theta_ref", g
);
609 setname
[nsets
] = strdup(buf
);
612 for (g
=0; g
<rot
->ngrp
; 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
);
621 sprintf(buf
, "tau%d (kJ/mol)", g
);
622 print_aligned_group(fp
, "tau", g
);
623 setname
[nsets
] = strdup(buf
);
626 sprintf(buf
, "energy%d (kJ/mol)", g
);
627 print_aligned_group(fp
, "energy", g
);
628 setname
[nsets
] = strdup(buf
);
631 fprintf(fp
, "\n#\n");
634 xvgr_legend(fp
, nsets
, setname
, oenv
);
635 for(g
=0; g
<nsets
; g
++)
646 /* Call on master only */
647 static FILE *open_angles_out(t_rot
*rot
, const char *fn
)
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
++)
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");
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");
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
)
691 fp
= open_output_file(fn
, rot
->nsttout
);
693 for (g
=0; g
<rot
->ngrp
; 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
]);
705 fprintf(fp
, "# The following columns will have the syntax (tau=torque for that slab):\n");
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");
720 /* Determine center of structure with coordinates x */
721 static void get_center(rvec x
[], real weight
[], int nat
, rvec center
)
725 double weight_sum
= 0.0;
728 /* Zero out the center of mass */
731 /* Loop over all atoms and add their weighted position vectors */
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
);
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
)
766 static void swap_col(double **mat
, int i
, int j
)
768 double tmp
[3] = {mat
[0][j
], mat
[1][j
], mat
[2][j
]};
781 /* Eigenvectors are stored in columns of eigen_vec */
782 static void diagonalize_symmetric(
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 */
817 rvec zet
= {0.0, 0.0, 1.0};
818 rvec rot_axis
={0.0, 0.0, 0.0};
819 rvec
*rotated_str
=NULL
;
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]);
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
++)
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
++)
857 s
[i
][j
]=rotated_str
[i
][j
];
865 static void calc_correl_matrix(rvec
* Xstr
, rvec
* Ystr
, double** Rmat
, int natoms
)
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
)
886 for(i
=0; i
<natoms
; i
++)
889 str
[i
][j
] *= sqrt(weight
[i
]);
894 static void trans(rvec
* x
, int natoms
, double* vec
)
899 for(i
=0; i
<natoms
; i
++)
908 static double opt_angle_analytic(
921 double **Rmat
, **RtR
, **eigvec
;
923 double V
[3][3], WS
[3][3];
924 double rot_matrix
[3][3];
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 */
939 shift
[i
] = (-1.0)*ref_com
[i
];
940 trans(ref_s_1
, natoms
, shift
);
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
++)
959 /* Weight coordinates with sqrt(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
);
970 RtR
= allocate_square_matrix(3);
977 RtR
[i
][j
] += Rmat
[k
][i
] * Rmat
[k
][j
];
981 /* Diagonalize RtR */
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);
1004 WS
[i
][j
] = eigvec
[i
][j
] / sqrt(eigval
[j
]);
1012 V
[i
][j
] += Rmat
[i
][k
]*WS
[k
][j
];
1016 free_square_matrix(Rmat
, 3);
1018 /* Calculate optimal rotation matrix */
1021 rot_matrix
[i
][j
] = 0.0;
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);
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(
1060 int i
,l
,n
,islab
,ind
;
1062 rvec
*fitcoords
=NULL
;
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 */
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
]);
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 */
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
;
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
);
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(
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
++)
1212 rotg
->xc_eshifts
[i
][m
]++;
1214 while (dx
[m
] >= 0.5*box
[m
][m
])
1216 for(d
=0; d
<DIM
; d
++)
1218 rotg
->xc_eshifts
[i
][m
]--;
1225 /* Assemble the coordinates such that every node has all of them */
1226 static void get_coordinates(
1230 bool bNeedShiftsUpdate
, /* NS step, the shifts have changed */
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 */
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
)
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
];
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(
1315 real sigma
, /* The Gaussian width sigma */
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 */
1327 rvec curr_x
; /* particle coordinate */
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 */
1334 rvec force_n
; /* Single force from slab n on one atom */
1337 real sdotr_ii
; /* s_ii.r_ii */
1339 rvec tmp_v
, tmp_n1_v
, tmp_n2_v
, tmp_n3_v
, tmp_n4_v
;
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 /********************************************************/
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
);
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 */
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: */
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
];
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);
1483 } /* END of loop over local atoms */
1489 static real
do_flex_lowlevel(
1492 real sigma
, /* The Gaussian width sigma */
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 */
1508 rvec curr_x
; /* particle coordinate */
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 */
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 /********************************************************/
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 */
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
);
1584 rvec_add(sum_n1
,dummy1
,sum_n1
);
1586 /*************************************************************/
1587 /* For the term sum_n2 we need to loop over all atoms again: */
1588 /*************************************************************/
1590 for (i
=0; i
<rotg
->nat
; i
++)
1592 /* Index of a rotation group atom */
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: */
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
];
1651 static bool bFirst
=1;
1656 sprintf(buf
, "forces%d.txt", cr
->nodeid
);
1657 fp
= fopen(buf
, "w");
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
]);
1666 } /* END of loop over local atoms */
1673 static void print_coordinates(t_commrec
*cr
, t_rotgrp
*rotg
, rvec x
[], matrix box
, int step
)
1677 static char buf
[STRLEN
];
1678 static bool bFirst
=1;
1683 sprintf(buf
, "coords%d.txt", cr
->nodeid
);
1684 fp
= fopen(buf
, "w");
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
]);
1704 /* Enforced rotation with a flexible axis */
1705 static void do_flexible(
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 */
1713 double t
, /* Time in picoseconds */
1714 bool bDynBox
, /* Is the box dynamic? */
1715 int step
, /* The time step */
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
);
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
,
1758 real
*weight
, /* atoms near the rotation axis should count less than atoms far away */
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 */
1765 real cosalpha
; /* cos of angle between projected reference and actual coordinate */
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
);
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 */
1797 /* Return the angle in radians */
1798 *alpha
= sign
* acos(cosalpha
);
1799 /* Also return the weight */
1804 /* Project first vector onto a plane perpendicular to the second vector
1807 static inline void project_onto_plane(rvec dr
, const rvec v
)
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(
1822 t_rotgrp
*rotg
, /* The rotation group */
1823 matrix rotmat
, /* rotary matrix */
1824 rvec x
[], /* The coordinates (natoms) */
1826 double t
, /* Time in picoseconds */
1827 int step
, /* The time step */
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 */
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
]);
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;
1902 /* Probably one does not want enforced rotation to influence */
1903 /* the virial. But if so, activate the following lines */
1907 Add the rotation contribution to the virial
1908 for(j=0; j<DIM; j++)
1910 vir[j][m] += 0.5*f[ii][j]*dr[m];
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
);
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(
1927 t_rotgrp
*rotg
, /* The rotation group */
1928 matrix rotmat
, /* rotary matrix */
1929 rvec x
[], /* The coordinates (natoms) */
1931 double t
, /* Time in picoseconds */
1932 int step
, /* The time step */
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 */
1943 clear_rvec(zerovec
);
1946 /* Clear values from last time step */
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
]);
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;
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 */
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 */
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? */
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 */
2044 for (i
=0; i
<rotg
->nat
; i
++)
2045 rotg
->xc_ref_ind
[i
] = i
;
2047 /* Allocate space for collective coordinates if used */
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 */
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;
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 */
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: */
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
;
2103 sprintf(warn_buf
, "Enforced rotation: Box size in reference file %s differs from actual box size!", filename
);
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
);
2117 else /* Save the initial coordinates of the rotation group as reference */
2119 for(i
=0; i
<rotg
->nat
; 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
);
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
++)
2135 copy_rvec(x
[ii
], rotg
->xc_old
[i
]);
2140 /* Copy reference coordinates to all PP nodes */
2143 gmx_bcast(rotg
->nat
*sizeof(rotg
->xc_ref
[0]), rotg
->xc_ref
, cr
);
2145 gmx_bcast(rotg
->nat
*sizeof(rotg
->xc_old
[0]),rotg
->xc_old
, cr
);
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
)
2190 for(i
=0; i
<rotg
->nat
; i
++) {
2192 if (!ga2la_home(ga2la
,ii
,&ii
)) {
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 */
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! */
2215 void dd_make_local_rotation_groups(gmx_domdec_t
*dd
,t_rot
*rot
,t_mdatoms
*md
)
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
)
2239 if ( (PAR(cr
)) && !DOMAINDECOMP(cr
) )
2240 gmx_fatal(FARGS
, "Enforced rotation is only implemented for domain decomposition!");
2244 /* Output every step for reruns */
2245 bRerun
= Flags
& MD_RERUN
;
2249 fprintf(fplog
, "Enforced rotation: rerun - will write rotation output every available step.\n");
2254 rot
->out_slabs
= NULL
;
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
];
2263 fprintf(fplog
,"Enforced rotation: group %d type '%s'\n", g
, erotg_names
[rotg
->eType
]);
2265 if (rotg
->eType
== erotgFLEX1
|| rotg
->eType
== erotgFLEX2
)
2273 rotg
->nalloc_loc
= 0;
2274 rotg
->ind_loc
= NULL
;
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
;
2296 rot
->out_rot
= open_rot_out(opt2fn("-r",nfile
,fnm
), rot
, oenv
, Flags
);
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(
2315 gmx_wallcycle_t wcycle
,
2322 bool outstep_torque
;
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
);
2369 case erotgFIXED_PLANE
:
2370 set_pbc(&pbc
,ir
->ePBC
,box
);
2371 do_fixed(cr
,rotg
,rotmat
,x
,&pbc
,t
,step
,outstep_torque
);
2373 case erotgFOLLOW_PLANE
:
2374 do_follow_plane(cr
,rotg
,rotmat
,x
,box
,t
,step
,outstep_torque
);
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
);
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
);