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"
58 #include "mpelogging.h"
61 /* Helper structure for sorting coordinates along rotation vector */
63 real xcproj
; /* Projection of xc on the rotation vector */
64 int ind
; /* Index of xc */
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 */
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 */
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
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 */
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
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
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
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 */
156 static double** allocate_square_matrix(int dim
)
170 static void free_square_matrix(double** mat
, int dim
)
175 for (i
=0; i
<dim
; i
++)
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 */
187 gmx_enfrot_t er
; /* Pointer to the enforced rotation buffer variables */
188 gmx_enfrotgrp_t erg
; /* Pointer to enforced rotation group data */
193 /* Fill the MPI buffer with stuff to reduce: */
197 for (g
=0; g
< rot
->ngrp
; g
++)
201 nslabs
= 2*erg
->slab_max_nr
+1;
202 er
->mpi_inbuf
[count
++] = erg
->V
;
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
;
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
];
229 MPI_Reduce(er
->mpi_inbuf
, er
->mpi_outbuf
, count
, GMX_MPI_REAL
, MPI_SUM
, MASTERRANK(cr
), cr
->mpi_comm_mygroup
);
231 /* Copy back the reduced data from the buffer on the master */
235 for (g
=0; g
< rot
->ngrp
; g
++)
239 nslabs
= 2*erg
->slab_max_nr
+1;
240 erg
->V
= er
->mpi_outbuf
[count
++];
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
++];
252 for (i
=0; i
<nslabs
; i
++)
253 erg
->slab_torque_v
[i
] = er
->mpi_outbuf
[count
++];
265 /* Av. angle and total torque for each rotation group */
266 for (g
=0; g
< rot
->ngrp
; g
++)
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
,
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
)
306 gmx_enfrot_t er
; /* Pointer to the enforced rotation buffer variables */
307 gmx_enfrotgrp_t erg
; /* Pointer to enforced rotation group data */
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 */
322 /* Loop over enforced rotation groups (usually 1, though)
323 * Apply the forces from rotation potentials */
324 for (g
=0; g
<rot
->ngrp
; g
++)
329 for (l
=0; l
<erg
->nat_loc
; l
++)
331 /* Get the right index of the local force */
332 ii
= erg
->ind_loc
[l
];
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
)
350 copy_rvec(box
[XX
],diag
);
351 rvec_inc(diag
,box
[YY
]);
352 rvec_inc(diag
,box
[ZZ
]);
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 */
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
;
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 */
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 */
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 */
432 /* If the box grows during the simulation, we might need more memory */
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
)
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 */
470 k
= erg
->slab_max_nr
;
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! */
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 */
506 /* This get overwritten by the last slab that has enough weight: */
509 /* At first time step: save the COGs of the reference structure */
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
]);
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(
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
);
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: */
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
;
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
;
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
;
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");
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) */
592 /* Subtract offset */
593 rvec_sub(x
,offset
,vectmp
);
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
)
623 sprintf(sbuf
, "%s%d", str
, g
);
624 fprintf(fp
, "%12s", sbuf
);
628 static FILE *open_output_file(const char *fn
, int steps
)
633 fp
= ffopen(fn
, "w");
635 fprintf(fp
, "# Output is written every %d time steps.\n\n", steps
);
641 /* Open output file for slab COG data. Call on master only */
642 static FILE *open_slab_out(t_rot
*rot
, const char *fn
)
649 for (g
=0; g
<rot
->ngrp
; g
++)
652 if (rotg
->eType
== erotgFLEX1
|| rotg
->eType
== erotgFLEX2
)
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
);
662 fprintf(fp
, "# The following columns will have the syntax: (COG = center of geometry, gaussian weighted)\n");
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");
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
,
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");
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
++)
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
);
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
]);
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
]);
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
]);
737 fprintf(fp
, "#\n# Legend for the following data columns:\n");
739 print_aligned_short(fp
, "t");
741 snew(setname
, 4*rot
->ngrp
);
743 for (g
=0; g
<rot
->ngrp
; g
++)
746 sprintf(buf
, "theta_ref%d (degree)", g
);
747 print_aligned_group(fp
, "theta_ref", g
);
748 setname
[nsets
] = strdup(buf
);
751 for (g
=0; g
<rot
->ngrp
; 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
);
760 sprintf(buf
, "tau%d (kJ/mol)", g
);
761 print_aligned_group(fp
, "tau", g
);
762 setname
[nsets
] = strdup(buf
);
765 sprintf(buf
, "energy%d (kJ/mol)", g
);
766 print_aligned_group(fp
, "energy", g
);
767 setname
[nsets
] = strdup(buf
);
770 fprintf(fp
, "\n#\n");
773 xvgr_legend(fp
, nsets
, setname
, oenv
);
774 for(g
=0; g
<nsets
; g
++)
785 /* Call on master only */
786 static FILE *open_angles_out(t_rot
*rot
, const char *fn
)
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
++)
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");
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");
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
)
830 fp
= open_output_file(fn
, rot
->nsttout
);
832 for (g
=0; g
<rot
->ngrp
; 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
]);
844 fprintf(fp
, "# The following columns will have the syntax (tau=torque for that slab):\n");
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");
859 /* Determine center of structure with coordinates x */
860 static void get_center(rvec x
[], real weight
[], int nat
, rvec center
)
864 double weight_sum
= 0.0;
867 /* Zero out the center */
870 /* Loop over all atoms and add their weighted position vectors */
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
);
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
)
905 static void swap_col(double **mat
, int i
, int j
)
907 double tmp
[3] = {mat
[0][j
], mat
[1][j
], mat
[2][j
]};
920 /* Eigenvectors are stored in columns of eigen_vec */
921 static void diagonalize_symmetric(
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 */
956 rvec zet
= {0.0, 0.0, 1.0};
957 rvec rot_axis
={0.0, 0.0, 0.0};
958 rvec
*rotated_str
=NULL
;
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]);
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
++)
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
++)
996 s
[i
][j
]=rotated_str
[i
][j
];
1004 static void calc_correl_matrix(rvec
* Xstr
, rvec
* Ystr
, double** Rmat
, int natoms
)
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
)
1025 for(i
=0; i
<natoms
; i
++)
1028 str
[i
][j
] *= sqrt(weight
[i
]);
1033 static void trans(rvec
* x
, int natoms
, double* vec
)
1038 for(i
=0; i
<natoms
; i
++)
1047 static double opt_angle_analytic(
1060 double **Rmat
, **RtR
, **eigvec
;
1062 double V
[3][3], WS
[3][3];
1063 double rot_matrix
[3][3];
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 */
1078 shift
[i
] = (-1.0)*ref_com
[i
];
1079 trans(ref_s_1
, natoms
, shift
);
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
++)
1098 /* Weight coordinates with sqrt(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
);
1109 RtR
= allocate_square_matrix(3);
1116 RtR
[i
][j
] += Rmat
[k
][i
] * Rmat
[k
][j
];
1120 /* Diagonalize RtR */
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);
1143 WS
[i
][j
] = eigvec
[i
][j
] / sqrt(eigval
[j
]);
1151 V
[i
][j
] += Rmat
[i
][k
]*WS
[k
][j
];
1155 free_square_matrix(Rmat
, 3);
1157 /* Calculate optimal rotation matrix */
1160 rot_matrix
[i
][j
] = 0.0;
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);
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(
1199 int i
,l
,n
,islab
,ind
;
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 */
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;
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 */
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
;
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
);
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(
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
++)
1353 erg
->xc_eshifts
[i
][m
]++;
1355 while (dx
[m
] >= 0.5*box
[m
][m
])
1357 for(d
=0; d
<DIM
; d
++)
1359 erg
->xc_eshifts
[i
][m
]--;
1366 /* Assemble the coordinates such that every node has all of them */
1367 static void get_coordinates(
1371 bool bNeedShiftsUpdate
, /* NS step, the shifts have changed */
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 */
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
)
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
];
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(
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
;
1489 /* Determine the max slab */
1491 while (g
> rotg
->min_gaussian
)
1494 g
= gaussian_weight(curr_x
, rotg
, slab
);
1495 erg
->gn_slabind
[count
]=slab
;
1496 erg
->gn_atom
[count
]=g
;
1501 /* Determine the max slab */
1506 g
= gaussian_weight(curr_x
, rotg
, slab
);
1507 erg
->gn_atom
[count
]=g
;
1508 erg
->gn_slabind
[count
]=slab
;
1511 while (g
> rotg
->min_gaussian
);
1518 static real
do_flex2_lowlevel(
1521 real sigma
, /* The Gaussian width sigma */
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 */
1533 rvec curr_x
; /* particle coordinate */
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 */
1540 rvec force_n
; /* Single force from slab n on one atom */
1543 real sdotr_ii
; /* s_ii.r_ii */
1545 rvec tmp_v
, tmp_n1_v
, tmp_n2_v
, tmp_n3_v
, tmp_n4_v
;
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 /********************************************************/
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
);
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 */
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: */
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
];
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);
1693 } /* END of loop over local atoms */
1699 static real
do_flex_lowlevel(
1702 real sigma
, /* The Gaussian width sigma */
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 */
1718 rvec curr_x
; /* particle coordinate */
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 */
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 /********************************************************/
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
);
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
);
1801 rvec_add(sum_n1
,dummy1
,sum_n1
);
1803 /*************************************************************/
1804 /* For the term sum_n2 we need to loop over all atoms again: */
1805 /*************************************************************/
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 */
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: */
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
];
1870 static bool bFirst
=1;
1875 sprintf(buf
, "forces%d.txt", cr
->nodeid
);
1876 fp
= fopen(buf
, "w");
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
]);
1885 } /* END of loop over local atoms */
1892 static void print_coordinates(t_commrec
*cr
, t_rotgrp
*rotg
, rvec x
[], matrix box
, int step
)
1896 static char buf
[STRLEN
];
1897 static bool bFirst
=1;
1902 sprintf(buf
, "coords%d.txt", cr
->nodeid
);
1903 fp
= fopen(buf
, "w");
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
]);
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
)
1933 else if (xca
->xcproj
> xcb
->xcproj
)
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 */
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
);
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
1975 static void get_firstlast_atom_per_slab(t_rotgrp
*rotg
, t_commrec
*cr
)
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
);
1996 } while ((beta
< -erg
->max_beta
) && (i
< rotg
->nat
));
1998 erg
->firstatom
[n
] = i
;
1999 /* Proceed to the next slab */
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
);
2012 } while ((beta
> erg
->max_beta
) && (i
> -1));
2014 erg
->lastatom
[n
] = i
;
2015 /* Proceed to the next slab */
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(
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 */
2033 double t
, /* Time in picoseconds */
2034 bool bDynBox
, /* Is the box dynamic? */
2035 int step
, /* The time step */
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
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
);
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
,
2093 real
*weight
, /* atoms near the rotation axis should count less than atoms far away */
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 */
2100 real cosalpha
; /* cos of angle between projected reference and actual coordinate */
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
);
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 */
2132 /* Return the angle in radians */
2133 *alpha
= sign
* acos(cosalpha
);
2134 /* Also return the weight */
2139 /* Project first vector onto a plane perpendicular to the second vector
2142 static inline void project_onto_plane(rvec dr
, const rvec v
)
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(
2157 t_rotgrp
*rotg
, /* The rotation group */
2158 matrix rotmat
, /* rotary matrix */
2159 rvec x
[], /* The coordinates (natoms) */
2161 double t
, /* Time in picoseconds */
2162 int step
, /* The time step */
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 */
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
]);
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;
2240 /* Probably one does not want enforced rotation to influence */
2241 /* the virial. But if so, activate the following lines */
2245 Add the rotation contribution to the virial
2246 for(j=0; j<DIM; j++)
2248 vir[j][m] += 0.5*f[ii][j]*dr[m];
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
);
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(
2265 t_rotgrp
*rotg
, /* The rotation group */
2266 matrix rotmat
, /* rotary matrix */
2267 rvec x
[], /* The coordinates (natoms) */
2269 double t
, /* Time in picoseconds */
2270 int step
, /* The time step */
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 */
2282 clear_rvec(zerovec
);
2285 erg
=rotg
->enfrotgrp
;
2287 /* Clear values from last time step */
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
]);
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;
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 */
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 */
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? */
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 */
2388 for (i
=0; i
<rotg
->nat
; i
++)
2389 erg
->xc_ref_ind
[i
] = i
;
2391 /* Allocate space for collective coordinates if used */
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 */
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;
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 */
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: */
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
;
2457 sprintf(warn_buf
, "Enforced rotation: Box size in reference file %s differs from actual box size!", filename
);
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
);
2471 else /* Save the initial coordinates of the rotation group as reference */
2473 for(i
=0; i
<rotg
->nat
; 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
);
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
++)
2489 copy_rvec(x
[ii
], erg
->xc_old
[i
]);
2494 /* Copy reference coordinates to all PP nodes */
2497 gmx_bcast(rotg
->nat
*sizeof(erg
->xc_ref
[0]), erg
->xc_ref
, cr
);
2499 gmx_bcast(rotg
->nat
*sizeof(erg
->xc_old
[0]),erg
->xc_old
, cr
);
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
)
2541 gmx_enfrotgrp_t erg
; /* Pointer to enforced rotation group data */
2544 erg
=rotg
->enfrotgrp
;
2547 for(i
=0; i
<rotg
->nat
; i
++) {
2549 if (!ga2la_home(ga2la
,ii
,&ii
)) {
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 */
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! */
2572 void dd_make_local_rotation_groups(gmx_domdec_t
*dd
,t_rot
*rot
,t_mdatoms
*md
)
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
)
2592 int nat_max
=0; /* Size of biggest rotation group */
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!");
2603 snew(rot
->enfrot
, 1);
2606 /* Output every step for reruns */
2607 bRerun
= Flags
& MD_RERUN
;
2611 fprintf(fplog
, "Enforced rotation: rerun - will write rotation output every available step.\n");
2616 er
->out_slabs
= NULL
;
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
];
2625 fprintf(fplog
,"Enforced rotation: group %d type '%s'\n", g
, erotg_names
[rotg
->eType
]);
2627 if (rotg
->eType
== erotgFLEX1
|| rotg
->eType
== erotgFLEX2
)
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
);
2641 erg
->nalloc_loc
= 0;
2642 erg
->ind_loc
= NULL
;
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
;
2666 er
->out_torque
= NULL
;
2669 er
->out_rot
= open_rot_out(opt2fn("-ro",nfile
,fnm
), rot
, oenv
, Flags
);
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(
2688 gmx_wallcycle_t wcycle
,
2695 bool outstep_torque
;
2699 gmx_enfrot_t er
; /* Pointer to the enforced rotation buffer variables */
2700 gmx_enfrotgrp_t erg
; /* Pointer to enforced rotation group data */
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
);
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
);
2756 case erotgFIXED_PLANE
:
2757 set_pbc(&pbc
,ir
->ePBC
,box
);
2758 do_fixed(cr
,rotg
,rotmat
,x
,&pbc
,t
,step
,outstep_torque
);
2760 case erotgFOLLOW_PLANE
:
2761 do_follow_plane(cr
,rotg
,rotmat
,x
,box
,t
,step
,outstep_torque
);
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
);
2775 fprintf(stderr
, "Enforced rotation calculation (step %d) took %g seconds.\n", step
, MPI_Wtime()-t0
);
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
);