74fb143114d3b2a1ef1ef5e608fe37f27ca7a2b9
[alexxy/gromacs.git] / src / gromacs / pulling / pull_rotation.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2008, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "gmxpre.h"
38
39 #include "pull_rotation.h"
40
41 #include "config.h"
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include <algorithm>
48
49 #include "gromacs/commandline/filenm.h"
50 #include "gromacs/compat/make_unique.h"
51 #include "gromacs/domdec/dlbtiming.h"
52 #include "gromacs/domdec/domdec_struct.h"
53 #include "gromacs/domdec/ga2la.h"
54 #include "gromacs/domdec/localatomset.h"
55 #include "gromacs/domdec/localatomsetmanager.h"
56 #include "gromacs/fileio/gmxfio.h"
57 #include "gromacs/fileio/xvgr.h"
58 #include "gromacs/gmxlib/network.h"
59 #include "gromacs/linearalgebra/nrjac.h"
60 #include "gromacs/math/functions.h"
61 #include "gromacs/math/utilities.h"
62 #include "gromacs/math/vec.h"
63 #include "gromacs/mdlib/groupcoord.h"
64 #include "gromacs/mdlib/mdrun.h"
65 #include "gromacs/mdlib/sim_util.h"
66 #include "gromacs/mdtypes/commrec.h"
67 #include "gromacs/mdtypes/inputrec.h"
68 #include "gromacs/mdtypes/md_enums.h"
69 #include "gromacs/mdtypes/state.h"
70 #include "gromacs/pbcutil/pbc.h"
71 #include "gromacs/timing/cyclecounter.h"
72 #include "gromacs/timing/wallcycle.h"
73 #include "gromacs/topology/mtop_lookup.h"
74 #include "gromacs/topology/mtop_util.h"
75 #include "gromacs/utility/basedefinitions.h"
76 #include "gromacs/utility/fatalerror.h"
77 #include "gromacs/utility/pleasecite.h"
78 #include "gromacs/utility/smalloc.h"
79
80 static char const *RotStr = {"Enforced rotation:"};
81
82 /* Set the minimum weight for the determination of the slab centers */
83 #define WEIGHT_MIN (10*GMX_FLOAT_MIN)
84
85 //! Helper structure for sorting positions along rotation vector
86 struct sort_along_vec_t
87 {
88     //! Projection of xc on the rotation vector
89     real xcproj;
90     //! Index of xc
91     int  ind;
92     //! Mass
93     real m;
94     //! Position
95     rvec x;
96     //! Reference position
97     rvec x_ref;
98 };
99
100
101 //! Enforced rotation / flexible: determine the angle of each slab
102 struct gmx_slabdata
103 {
104     //! Number of atoms belonging to this slab
105     int   nat;
106     /*! \brief The positions belonging to this slab.
107      *
108      * In general, this should be all positions of the whole
109      * rotation group, but we leave those away that have a small
110      * enough weight. */
111     rvec *x;
112     //! Same for reference
113     rvec *ref;
114     //! The weight for each atom
115     real *weight;
116 };
117
118
119 //! Helper structure for potential fitting
120 struct gmx_potfit
121 {
122     /*! \brief Set of angles for which the potential is calculated.
123      *
124      * The optimum fit is determined as the angle for with the
125      * potential is minimal. */
126     real   *degangle;
127     //! Potential for the different angles
128     real   *V;
129     //! Rotation matrix corresponding to the angles
130     matrix *rotmat;
131 };
132
133
134 //! Enforced rotation data for a single rotation group
135 struct gmx_enfrotgrp
136 {
137     //! Input parameters for this group
138     const t_rotgrp                    *rotg = nullptr;
139     //! Index of this group within the set of groups
140     int                                groupIndex;
141     //! Rotation angle in degrees
142     real                               degangle;
143     //! Rotation matrix
144     matrix                             rotmat;
145     //! The atoms subject to enforced rotation
146     std::unique_ptr<gmx::LocalAtomSet> atomSet;
147
148     //! The normalized rotation vector
149     rvec  vec;
150     //! Rotation potential for this rotation group
151     real  V;
152     //! Array to store the forces on the local atoms resulting from enforced rotation potential
153     rvec *f_rot_loc;
154
155     /* Collective coordinates for the whole rotation group */
156     //! Length of each x_rotref vector after x_rotref has been put into origin
157     real  *xc_ref_length;
158     //! Center of the rotation group positions, may be mass weighted
159     rvec   xc_center;
160     //! Center of the rotation group reference positions
161     rvec   xc_ref_center;
162     //! Current (collective) positions
163     rvec  *xc;
164     //! Current (collective) shifts
165     ivec  *xc_shifts;
166     //! Extra shifts since last DD step
167     ivec  *xc_eshifts;
168     //! Old (collective) positions
169     rvec  *xc_old;
170     //! Normalized form of the current positions
171     rvec  *xc_norm;
172     //! Reference positions (sorted in the same order as xc when sorted)
173     rvec  *xc_ref_sorted;
174     //! Where is a position found after sorting?
175     int   *xc_sortind;
176     //! Collective masses
177     real  *mc;
178     //! Collective masses sorted
179     real  *mc_sorted;
180     //! one over the total mass of the rotation group
181     real   invmass;
182
183     //! Torque in the direction of rotation vector
184     real torque_v;
185     //! Actual angle of the whole rotation group
186     real angle_v;
187     /* Fixed rotation only */
188     //! Weights for angle determination
189     real  weight_v;
190     //! Local reference coords, correctly rotated
191     rvec *xr_loc;
192     //! Local current coords, correct PBC image
193     rvec *x_loc_pbc;
194     //! Masses of the current local atoms
195     real *m_loc;
196
197     /* Flexible rotation only */
198     //! For this many slabs memory is allocated
199     int             nslabs_alloc;
200     //! Lowermost slab for that the calculation needs to be performed at a given time step
201     int             slab_first;
202     //! Uppermost slab ...
203     int             slab_last;
204     //! First slab for which ref. center is stored
205     int             slab_first_ref;
206     //! Last ...
207     int             slab_last_ref;
208     //! Slab buffer region around reference slabs
209     int             slab_buffer;
210     //! First relevant atom for a slab
211     int            *firstatom;
212     //! Last relevant atom for a slab
213     int            *lastatom;
214     //! Gaussian-weighted slab center
215     rvec           *slab_center;
216     //! Gaussian-weighted slab center for the reference positions
217     rvec           *slab_center_ref;
218     //! Sum of gaussian weights in a slab
219     real           *slab_weights;
220     //! Torque T = r x f for each slab. torque_v = m.v = angular momentum in the direction of v
221     real           *slab_torque_v;
222     //! min_gaussian from t_rotgrp is the minimum value the gaussian must have so that the force is actually evaluated. max_beta is just another way to put it
223     real            max_beta;
224     //! Precalculated gaussians for a single atom
225     real           *gn_atom;
226     //! Tells to which slab each precalculated gaussian belongs
227     int            *gn_slabind;
228     //! Inner sum of the flexible2 potential per slab; this is precalculated for optimization reasons
229     rvec           *slab_innersumvec;
230     //! Holds atom positions and gaussian weights of atoms belonging to a slab
231     gmx_slabdata   *slab_data;
232
233     /* For potential fits with varying angle: */
234     //! Used for fit type 'potential'
235     gmx_potfit *PotAngleFit;
236 };
237
238
239 //! Enforced rotation data for all groups
240 struct gmx_enfrot
241 {
242     //! Input parameters.
243     const t_rot               *rot = nullptr;
244     //! Output period for main rotation outfile
245     int                        nstrout;
246     //! Output period for per-slab data
247     int                        nstsout;
248     //! Output file for rotation data
249     FILE                      *out_rot = nullptr;
250     //! Output file for torque data
251     FILE                      *out_torque = nullptr;
252     //! Output file for slab angles for flexible type
253     FILE                      *out_angles = nullptr;
254     //! Output file for slab centers
255     FILE                      *out_slabs = nullptr;
256     //! Allocation size of buf
257     int                        bufsize = 0;
258     //! Coordinate buffer variable for sorting
259     rvec                      *xbuf = nullptr;
260     //! Masses buffer variable for sorting
261     real                      *mbuf = nullptr;
262     //! Buffer variable needed for position sorting
263     sort_along_vec_t          *data = nullptr;
264     //! MPI buffer
265     real                      *mpi_inbuf = nullptr;
266     //! MPI buffer
267     real                      *mpi_outbuf = nullptr;
268     //! Allocation size of in & outbuf
269     int                        mpi_bufsize = 0;
270     //! If true, append output files
271     gmx_bool                   appendFiles = false;
272     //! Used to skip first output when appending to avoid duplicate entries in rotation outfiles
273     gmx_bool                   bOut = false;
274     //! Stores working data per group
275     std::vector<gmx_enfrotgrp> enfrotgrp;
276     ~gmx_enfrot();
277 };
278
279 gmx_enfrot::~gmx_enfrot()
280 {
281     if (out_rot)
282     {
283         gmx_fio_fclose(out_rot);
284     }
285     if (out_slabs)
286     {
287         gmx_fio_fclose(out_slabs);
288     }
289     if (out_angles)
290     {
291         gmx_fio_fclose(out_angles);
292     }
293     if (out_torque)
294     {
295         gmx_fio_fclose(out_torque);
296     }
297 }
298
299 namespace gmx
300 {
301
302 class EnforcedRotation::Impl
303 {
304     public:
305         gmx_enfrot enforcedRotation_;
306 };
307
308 EnforcedRotation::EnforcedRotation() : impl_(new Impl)
309 {
310 }
311
312 EnforcedRotation::~EnforcedRotation() = default;
313
314 gmx_enfrot *EnforcedRotation::getLegacyEnfrot()
315 {
316     return &impl_->enforcedRotation_;
317 }
318
319 }  // namespace gmx
320
321 /* Activate output of forces for correctness checks */
322 /* #define PRINT_FORCES */
323 #ifdef PRINT_FORCES
324 #define PRINT_FORCE_J  fprintf(stderr, "f%d = %15.8f %15.8f %15.8f\n", erg->xc_ref_ind[j], erg->f_rot_loc[j][XX], erg->f_rot_loc[j][YY], erg->f_rot_loc[j][ZZ]);
325 #define PRINT_POT_TAU  if (MASTER(cr)) { \
326         fprintf(stderr, "potential = %15.8f\n" "torque    = %15.8f\n", erg->V, erg->torque_v); \
327 }
328 #else
329 #define PRINT_FORCE_J
330 #define PRINT_POT_TAU
331 #endif
332
333 /* Shortcuts for often used queries */
334 #define ISFLEX(rg) ( ((rg)->eType == erotgFLEX) || ((rg)->eType == erotgFLEXT) || ((rg)->eType == erotgFLEX2) || ((rg)->eType == erotgFLEX2T) )
335 #define ISCOLL(rg) ( ((rg)->eType == erotgFLEX) || ((rg)->eType == erotgFLEXT) || ((rg)->eType == erotgFLEX2) || ((rg)->eType == erotgFLEX2T) || ((rg)->eType == erotgRMPF) || ((rg)->eType == erotgRM2PF) )
336
337
338 /* Does any of the rotation groups use slab decomposition? */
339 static gmx_bool HaveFlexibleGroups(const t_rot *rot)
340 {
341     for (int g = 0; g < rot->ngrp; g++)
342     {
343         if (ISFLEX(&rot->grp[g]))
344         {
345             return TRUE;
346         }
347     }
348
349     return FALSE;
350 }
351
352
353 /* Is for any group the fit angle determined by finding the minimum of the
354  * rotation potential? */
355 static gmx_bool HavePotFitGroups(const t_rot *rot)
356 {
357     for (int g = 0; g < rot->ngrp; g++)
358     {
359         if (erotgFitPOT == rot->grp[g].eFittype)
360         {
361             return TRUE;
362         }
363     }
364
365     return FALSE;
366 }
367
368
369 static double** allocate_square_matrix(int dim)
370 {
371     int      i;
372     double** mat = nullptr;
373
374
375     snew(mat, dim);
376     for (i = 0; i < dim; i++)
377     {
378         snew(mat[i], dim);
379     }
380
381     return mat;
382 }
383
384
385 static void free_square_matrix(double** mat, int dim)
386 {
387     int i;
388
389
390     for (i = 0; i < dim; i++)
391     {
392         sfree(mat[i]);
393     }
394     sfree(mat);
395 }
396
397
398 /* Return the angle for which the potential is minimal */
399 static real get_fitangle(const gmx_enfrotgrp *erg)
400 {
401     int           i;
402     real          fitangle = -999.9;
403     real          pot_min  = GMX_FLOAT_MAX;
404     gmx_potfit   *fit;
405
406
407     fit = erg->PotAngleFit;
408
409     for (i = 0; i < erg->rotg->PotAngle_nstep; i++)
410     {
411         if (fit->V[i] < pot_min)
412         {
413             pot_min  = fit->V[i];
414             fitangle = fit->degangle[i];
415         }
416     }
417
418     return fitangle;
419 }
420
421
422 /* Reduce potential angle fit data for this group at this time step? */
423 static inline gmx_bool bPotAngle(const gmx_enfrot *er, const t_rotgrp *rotg, int64_t step)
424 {
425     return ( (erotgFitPOT == rotg->eFittype) && (do_per_step(step, er->nstsout) || do_per_step(step, er->nstrout)) );
426 }
427
428 /* Reduce slab torqe data for this group at this time step? */
429 static inline gmx_bool bSlabTau(const gmx_enfrot *er, const t_rotgrp *rotg, int64_t step)
430 {
431     return ( (ISFLEX(rotg)) && do_per_step(step, er->nstsout) );
432 }
433
434 /* Output rotation energy, torques, etc. for each rotation group */
435 static void reduce_output(const t_commrec *cr,
436                           gmx_enfrot *er, real t, int64_t step)
437 {
438     int             i, islab, nslabs = 0;
439     int             count; /* MPI element counter                               */
440     real            fitangle;
441     gmx_bool        bFlex;
442
443     /* Fill the MPI buffer with stuff to reduce. If items are added for reduction
444      * here, the MPI buffer size has to be enlarged also in calc_mpi_bufsize() */
445     if (PAR(cr))
446     {
447         count = 0;
448         for (auto &ergRef : er->enfrotgrp)
449         {
450             gmx_enfrotgrp  *erg  = &ergRef;
451             const t_rotgrp *rotg = erg->rotg;
452             nslabs                 = erg->slab_last - erg->slab_first + 1;
453             er->mpi_inbuf[count++] = erg->V;
454             er->mpi_inbuf[count++] = erg->torque_v;
455             er->mpi_inbuf[count++] = erg->angle_v;
456             er->mpi_inbuf[count++] = erg->weight_v; /* weights are not needed for flex types, but this is just a single value */
457
458             if (bPotAngle(er, rotg, step))
459             {
460                 for (i = 0; i < rotg->PotAngle_nstep; i++)
461                 {
462                     er->mpi_inbuf[count++] = erg->PotAngleFit->V[i];
463                 }
464             }
465             if (bSlabTau(er, rotg, step))
466             {
467                 for (i = 0; i < nslabs; i++)
468                 {
469                     er->mpi_inbuf[count++] = erg->slab_torque_v[i];
470                 }
471             }
472         }
473         if (count > er->mpi_bufsize)
474         {
475             gmx_fatal(FARGS, "%s MPI buffer overflow, please report this error.", RotStr);
476         }
477
478 #if GMX_MPI
479         MPI_Reduce(er->mpi_inbuf, er->mpi_outbuf, count, GMX_MPI_REAL, MPI_SUM, MASTERRANK(cr), cr->mpi_comm_mygroup);
480 #endif
481
482         /* Copy back the reduced data from the buffer on the master */
483         if (MASTER(cr))
484         {
485             count = 0;
486             for (auto &ergRef : er->enfrotgrp)
487             {
488                 gmx_enfrotgrp  *erg  = &ergRef;
489                 const t_rotgrp *rotg = erg->rotg;
490                 nslabs        = erg->slab_last - erg->slab_first + 1;
491                 erg->V        = er->mpi_outbuf[count++];
492                 erg->torque_v = er->mpi_outbuf[count++];
493                 erg->angle_v  = er->mpi_outbuf[count++];
494                 erg->weight_v = er->mpi_outbuf[count++];
495
496                 if (bPotAngle(er, rotg, step))
497                 {
498                     for (int i = 0; i < rotg->PotAngle_nstep; i++)
499                     {
500                         erg->PotAngleFit->V[i] = er->mpi_outbuf[count++];
501                     }
502                 }
503                 if (bSlabTau(er, rotg, step))
504                 {
505                     for (int i = 0; i < nslabs; i++)
506                     {
507                         erg->slab_torque_v[i] = er->mpi_outbuf[count++];
508                     }
509                 }
510             }
511         }
512     }
513
514     /* Output */
515     if (MASTER(cr))
516     {
517         /* Angle and torque for each rotation group */
518         for (auto &ergRef : er->enfrotgrp)
519         {
520             gmx_enfrotgrp  *erg  = &ergRef;
521             const t_rotgrp *rotg = erg->rotg;
522             bFlex = ISFLEX(rotg);
523
524             /* Output to main rotation output file: */
525             if (do_per_step(step, er->nstrout) )
526             {
527                 if (erotgFitPOT == rotg->eFittype)
528                 {
529                     fitangle = get_fitangle(erg);
530                 }
531                 else
532                 {
533                     if (bFlex)
534                     {
535                         fitangle = erg->angle_v; /* RMSD fit angle */
536                     }
537                     else
538                     {
539                         fitangle = (erg->angle_v/erg->weight_v)*180.0*M_1_PI;
540                     }
541                 }
542                 fprintf(er->out_rot, "%12.4f", fitangle);
543                 fprintf(er->out_rot, "%12.3e", erg->torque_v);
544                 fprintf(er->out_rot, "%12.3e", erg->V);
545             }
546
547             if (do_per_step(step, er->nstsout) )
548             {
549                 /* Output to torque log file: */
550                 if (bFlex)
551                 {
552                     fprintf(er->out_torque, "%12.3e%6d", t, erg->groupIndex);
553                     for (int i = erg->slab_first; i <= erg->slab_last; i++)
554                     {
555                         islab = i - erg->slab_first;  /* slab index */
556                         /* Only output if enough weight is in slab */
557                         if (erg->slab_weights[islab] > rotg->min_gaussian)
558                         {
559                             fprintf(er->out_torque, "%6d%12.3e", i, erg->slab_torque_v[islab]);
560                         }
561                     }
562                     fprintf(er->out_torque, "\n");
563                 }
564
565                 /* Output to angles log file: */
566                 if (erotgFitPOT == rotg->eFittype)
567                 {
568                     fprintf(er->out_angles, "%12.3e%6d%12.4f", t, erg->groupIndex, erg->degangle);
569                     /* Output energies at a set of angles around the reference angle */
570                     for (int i = 0; i < rotg->PotAngle_nstep; i++)
571                     {
572                         fprintf(er->out_angles, "%12.3e", erg->PotAngleFit->V[i]);
573                     }
574                     fprintf(er->out_angles, "\n");
575                 }
576             }
577         }
578         if (do_per_step(step, er->nstrout) )
579         {
580             fprintf(er->out_rot, "\n");
581         }
582     }
583 }
584
585
586 /* Add the forces from enforced rotation potential to the local forces.
587  * Should be called after the SR forces have been evaluated */
588 real add_rot_forces(gmx_enfrot *er,
589                     rvec f[], const t_commrec *cr, int64_t step, real t)
590 {
591     real            Vrot = 0.0; /* If more than one rotation group is present, Vrot
592                                    assembles the local parts from all groups         */
593
594     /* Loop over enforced rotation groups (usually 1, though)
595      * Apply the forces from rotation potentials */
596     for (auto &ergRef : er->enfrotgrp)
597     {
598         gmx_enfrotgrp *erg = &ergRef;
599         Vrot += erg->V;  /* add the local parts from the nodes */
600         const auto    &localRotationGroupIndex = erg->atomSet->localIndex();
601         for (gmx::index l = 0; l < localRotationGroupIndex.size(); l++)
602         {
603             /* Get the right index of the local force */
604             int ii = localRotationGroupIndex[l];
605             /* Add */
606             rvec_inc(f[ii], erg->f_rot_loc[l]);
607         }
608     }
609
610     /* Reduce energy,torque, angles etc. to get the sum values (per rotation group)
611      * on the master and output these values to file. */
612     if ( (do_per_step(step, er->nstrout) || do_per_step(step, er->nstsout)) && er->bOut)
613     {
614         reduce_output(cr, er, t, step);
615     }
616
617     /* When appending, er->bOut is FALSE the first time to avoid duplicate entries */
618     er->bOut = TRUE;
619
620     PRINT_POT_TAU
621
622     return Vrot;
623 }
624
625
626 /* The Gaussian norm is chosen such that the sum of the gaussian functions
627  * over the slabs is approximately 1.0 everywhere */
628 #define GAUSS_NORM   0.569917543430618
629
630
631 /* Calculate the maximum beta that leads to a gaussian larger min_gaussian,
632  * also does some checks
633  */
634 static double calc_beta_max(real min_gaussian, real slab_dist)
635 {
636     double sigma;
637     double arg;
638
639
640     /* Actually the next two checks are already made in grompp */
641     if (slab_dist <= 0)
642     {
643         gmx_fatal(FARGS, "Slab distance of flexible rotation groups must be >=0 !");
644     }
645     if (min_gaussian <= 0)
646     {
647         gmx_fatal(FARGS, "Cutoff value for Gaussian must be > 0. (You requested %f)");
648     }
649
650     /* Define the sigma value */
651     sigma = 0.7*slab_dist;
652
653     /* Calculate the argument for the logarithm and check that the log() result is negative or 0 */
654     arg = min_gaussian/GAUSS_NORM;
655     if (arg > 1.0)
656     {
657         gmx_fatal(FARGS, "min_gaussian of flexible rotation groups must be <%g", GAUSS_NORM);
658     }
659
660     return std::sqrt(-2.0*sigma*sigma*log(min_gaussian/GAUSS_NORM));
661 }
662
663
664 static inline real calc_beta(rvec curr_x, const gmx_enfrotgrp *erg, int n)
665 {
666     return iprod(curr_x, erg->vec) - erg->rotg->slab_dist * n;
667 }
668
669
670 static inline real gaussian_weight(rvec curr_x, const gmx_enfrotgrp *erg, int n)
671 {
672     const real norm = GAUSS_NORM;
673     real       sigma;
674
675
676     /* Define the sigma value */
677     sigma = 0.7*erg->rotg->slab_dist;
678     /* Calculate the Gaussian value of slab n for position curr_x */
679     return norm * exp( -0.5 * gmx::square( calc_beta(curr_x, erg, n)/sigma ) );
680 }
681
682
683 /* Returns the weight in a single slab, also calculates the Gaussian- and mass-
684  * weighted sum of positions for that slab */
685 static real get_slab_weight(int j, const gmx_enfrotgrp *erg,
686                             rvec xc[], const real mc[], rvec *x_weighted_sum)
687 {
688     rvec            curr_x;           /* The position of an atom                      */
689     rvec            curr_x_weighted;  /* The gaussian-weighted position               */
690     real            gaussian;         /* A single gaussian weight                     */
691     real            wgauss;           /* gaussian times current mass                  */
692     real            slabweight = 0.0; /* The sum of weights in the slab               */
693
694     clear_rvec(*x_weighted_sum);
695
696     /* Loop over all atoms in the rotation group */
697     for (int i = 0; i < erg->rotg->nat; i++)
698     {
699         copy_rvec(xc[i], curr_x);
700         gaussian = gaussian_weight(curr_x, erg, j);
701         wgauss   = gaussian * mc[i];
702         svmul(wgauss, curr_x, curr_x_weighted);
703         rvec_add(*x_weighted_sum, curr_x_weighted, *x_weighted_sum);
704         slabweight += wgauss;
705     }  /* END of loop over rotation group atoms */
706
707     return slabweight;
708 }
709
710
711 static void get_slab_centers(
712         gmx_enfrotgrp *erg,        /* Enforced rotation group working data */
713         rvec          *xc,         /* The rotation group positions; will
714                                       typically be enfrotgrp->xc, but at first call
715                                       it is enfrotgrp->xc_ref                      */
716         real          *mc,         /* The masses of the rotation group atoms       */
717         real           time,       /* Used for output only                         */
718         FILE          *out_slabs,  /* For outputting center per slab information   */
719         gmx_bool       bOutStep,   /* Is this an output step?                      */
720         gmx_bool       bReference) /* If this routine is called from
721                                       init_rot_group we need to store
722                                       the reference slab centers                   */
723 {
724     /* Loop over slabs */
725     for (int j = erg->slab_first; j <= erg->slab_last; j++)
726     {
727         int slabIndex    = j - erg->slab_first;
728         erg->slab_weights[slabIndex] = get_slab_weight(j, erg, xc, mc, &erg->slab_center[slabIndex]);
729
730         /* We can do the calculations ONLY if there is weight in the slab! */
731         if (erg->slab_weights[slabIndex] > WEIGHT_MIN)
732         {
733             svmul(1.0/erg->slab_weights[slabIndex], erg->slab_center[slabIndex], erg->slab_center[slabIndex]);
734         }
735         else
736         {
737             /* We need to check this here, since we divide through slab_weights
738              * in the flexible low-level routines! */
739             gmx_fatal(FARGS, "Not enough weight in slab %d. Slab center cannot be determined!", j);
740         }
741
742         /* At first time step: save the centers of the reference structure */
743         if (bReference)
744         {
745             copy_rvec(erg->slab_center[slabIndex], erg->slab_center_ref[slabIndex]);
746         }
747     } /* END of loop over slabs */
748
749     /* Output on the master */
750     if ( (nullptr != out_slabs) && bOutStep)
751     {
752         fprintf(out_slabs, "%12.3e%6d", time, erg->groupIndex);
753         for (int j = erg->slab_first; j <= erg->slab_last; j++)
754         {
755             int slabIndex = j - erg->slab_first;
756             fprintf(out_slabs, "%6d%12.3e%12.3e%12.3e",
757                     j, erg->slab_center[slabIndex][XX], erg->slab_center[slabIndex][YY], erg->slab_center[slabIndex][ZZ]);
758         }
759         fprintf(out_slabs, "\n");
760     }
761 }
762
763
764 static void calc_rotmat(
765         const rvec vec,
766         real       degangle,  /* Angle alpha of rotation at time t in degrees       */
767         matrix     rotmat)    /* Rotation matrix                                    */
768 {
769     real radangle;            /* Rotation angle in radians */
770     real cosa;                /* cosine alpha              */
771     real sina;                /* sine alpha                */
772     real OMcosa;              /* 1 - cos(alpha)            */
773     real dumxy, dumxz, dumyz; /* save computations         */
774     rvec rot_vec;             /* Rotate around rot_vec ... */
775
776
777     radangle = degangle * M_PI/180.0;
778     copy_rvec(vec, rot_vec );
779
780     /* Precompute some variables: */
781     cosa   = cos(radangle);
782     sina   = sin(radangle);
783     OMcosa = 1.0 - cosa;
784     dumxy  = rot_vec[XX]*rot_vec[YY]*OMcosa;
785     dumxz  = rot_vec[XX]*rot_vec[ZZ]*OMcosa;
786     dumyz  = rot_vec[YY]*rot_vec[ZZ]*OMcosa;
787
788     /* Construct the rotation matrix for this rotation group: */
789     /* 1st column: */
790     rotmat[XX][XX] = cosa  + rot_vec[XX]*rot_vec[XX]*OMcosa;
791     rotmat[YY][XX] = dumxy + rot_vec[ZZ]*sina;
792     rotmat[ZZ][XX] = dumxz - rot_vec[YY]*sina;
793     /* 2nd column: */
794     rotmat[XX][YY] = dumxy - rot_vec[ZZ]*sina;
795     rotmat[YY][YY] = cosa  + rot_vec[YY]*rot_vec[YY]*OMcosa;
796     rotmat[ZZ][YY] = dumyz + rot_vec[XX]*sina;
797     /* 3rd column: */
798     rotmat[XX][ZZ] = dumxz + rot_vec[YY]*sina;
799     rotmat[YY][ZZ] = dumyz - rot_vec[XX]*sina;
800     rotmat[ZZ][ZZ] = cosa  + rot_vec[ZZ]*rot_vec[ZZ]*OMcosa;
801
802 #ifdef PRINTMATRIX
803     int iii, jjj;
804
805     for (iii = 0; iii < 3; iii++)
806     {
807         for (jjj = 0; jjj < 3; jjj++)
808         {
809             fprintf(stderr, " %10.8f ",  rotmat[iii][jjj]);
810         }
811         fprintf(stderr, "\n");
812     }
813 #endif
814 }
815
816
817 /* Calculates torque on the rotation axis tau = position x force */
818 static inline real torque(const rvec rotvec, /* rotation vector; MUST be normalized!          */
819                           rvec       force,  /* force                                                */
820                           rvec       x,      /* position of atom on which the force acts             */
821                           rvec       pivot)  /* pivot point of rotation axis                         */
822 {
823     rvec vectmp, tau;
824
825
826     /* Subtract offset */
827     rvec_sub(x, pivot, vectmp);
828
829     /* position x force */
830     cprod(vectmp, force, tau);
831
832     /* Return the part of the torque which is parallel to the rotation vector */
833     return iprod(tau, rotvec);
834 }
835
836
837 /* Right-aligned output of value with standard width */
838 static void print_aligned(FILE *fp, char const *str)
839 {
840     fprintf(fp, "%12s", str);
841 }
842
843
844 /* Right-aligned output of value with standard short width */
845 static void print_aligned_short(FILE *fp, char const *str)
846 {
847     fprintf(fp, "%6s", str);
848 }
849
850
851 static FILE *open_output_file(const char *fn, int steps, const char what[])
852 {
853     FILE *fp;
854
855
856     fp = gmx_ffopen(fn, "w");
857
858     fprintf(fp, "# Output of %s is written in intervals of %d time step%s.\n#\n",
859             what, steps, steps > 1 ? "s" : "");
860
861     return fp;
862 }
863
864
865 /* Open output file for slab center data. Call on master only */
866 static FILE *open_slab_out(const char *fn,
867                            gmx_enfrot *er)
868 {
869     FILE *fp;
870
871     if (er->appendFiles)
872     {
873         fp = gmx_fio_fopen(fn, "a");
874     }
875     else
876     {
877         fp = open_output_file(fn, er->nstsout, "gaussian weighted slab centers");
878
879         for (auto &ergRef : er->enfrotgrp)
880         {
881             gmx_enfrotgrp *erg = &ergRef;
882             if (ISFLEX(erg->rotg))
883             {
884                 fprintf(fp, "# Rotation group %d (%s), slab distance %f nm, %s.\n",
885                         erg->groupIndex, erotg_names[erg->rotg->eType], erg->rotg->slab_dist,
886                         erg->rotg->bMassW ? "centers of mass" : "geometrical centers");
887             }
888         }
889
890         fprintf(fp, "# Reference centers are listed first (t=-1).\n");
891         fprintf(fp, "# The following columns have the syntax:\n");
892         fprintf(fp, "#     ");
893         print_aligned_short(fp, "t");
894         print_aligned_short(fp, "grp");
895         /* Print legend for the first two entries only ... */
896         for (int i = 0; i < 2; i++)
897         {
898             print_aligned_short(fp, "slab");
899             print_aligned(fp, "X center");
900             print_aligned(fp, "Y center");
901             print_aligned(fp, "Z center");
902         }
903         fprintf(fp, " ...\n");
904         fflush(fp);
905     }
906
907     return fp;
908 }
909
910
911 /* Adds 'buf' to 'str' */
912 static void add_to_string(char **str, char *buf)
913 {
914     int len;
915
916
917     len = strlen(*str) + strlen(buf) + 1;
918     srenew(*str, len);
919     strcat(*str, buf);
920 }
921
922
923 static void add_to_string_aligned(char **str, char *buf)
924 {
925     char buf_aligned[STRLEN];
926
927     sprintf(buf_aligned, "%12s", buf);
928     add_to_string(str, buf_aligned);
929 }
930
931
932 /* Open output file and print some general information about the rotation groups.
933  * Call on master only */
934 static FILE *open_rot_out(const char             *fn,
935                           const gmx_output_env_t *oenv,
936                           gmx_enfrot             *er)
937 {
938     FILE           *fp;
939     int             nsets;
940     const char    **setname;
941     char            buf[50], buf2[75];
942     gmx_bool        bFlex;
943     char           *LegendStr = nullptr;
944     const t_rot    *rot       = er->rot;
945
946     if (er->appendFiles)
947     {
948         fp = gmx_fio_fopen(fn, "a");
949     }
950     else
951     {
952         fp = xvgropen(fn, "Rotation angles and energy", "Time (ps)", "angles (degrees) and energies (kJ/mol)", oenv);
953         fprintf(fp, "# Output of enforced rotation data is written in intervals of %d time step%s.\n#\n", er->nstrout, er->nstrout > 1 ? "s" : "");
954         fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector v.\n");
955         fprintf(fp, "# To obtain the vectorial torque, multiply tau with the group's rot-vec.\n");
956         fprintf(fp, "# For flexible groups, tau(t,n) from all slabs n have been summed in a single value tau(t) here.\n");
957         fprintf(fp, "# The torques tau(t,n) are found in the rottorque.log (-rt) output file\n");
958
959         for (int g = 0; g < rot->ngrp; g++)
960         {
961             const t_rotgrp      *rotg = &rot->grp[g];
962             const gmx_enfrotgrp *erg  = &er->enfrotgrp[g];
963             bFlex = ISFLEX(rotg);
964
965             fprintf(fp, "#\n");
966             fprintf(fp, "# ROTATION GROUP %d, potential type '%s':\n", g, erotg_names[rotg->eType]);
967             fprintf(fp, "# rot-massw%d          %s\n", g, yesno_names[rotg->bMassW]);
968             fprintf(fp, "# rot-vec%d            %12.5e %12.5e %12.5e\n", g, erg->vec[XX], erg->vec[YY], erg->vec[ZZ]);
969             fprintf(fp, "# rot-rate%d           %12.5e degrees/ps\n", g, rotg->rate);
970             fprintf(fp, "# rot-k%d              %12.5e kJ/(mol*nm^2)\n", g, rotg->k);
971             if (rotg->eType == erotgISO || rotg->eType == erotgPM || rotg->eType == erotgRM || rotg->eType == erotgRM2)
972             {
973                 fprintf(fp, "# rot-pivot%d          %12.5e %12.5e %12.5e  nm\n", g, rotg->pivot[XX], rotg->pivot[YY], rotg->pivot[ZZ]);
974             }
975
976             if (bFlex)
977             {
978                 fprintf(fp, "# rot-slab-distance%d   %f nm\n", g, rotg->slab_dist);
979                 fprintf(fp, "# rot-min-gaussian%d   %12.5e\n", g, rotg->min_gaussian);
980             }
981
982             /* Output the centers of the rotation groups for the pivot-free potentials */
983             if ((rotg->eType == erotgISOPF) || (rotg->eType == erotgPMPF) || (rotg->eType == erotgRMPF) || (rotg->eType == erotgRM2PF
984                                                                                                             || (rotg->eType == erotgFLEXT) || (rotg->eType == erotgFLEX2T)) )
985             {
986                 fprintf(fp, "# ref. grp. %d center  %12.5e %12.5e %12.5e\n", g,
987                         erg->xc_ref_center[XX], erg->xc_ref_center[YY], erg->xc_ref_center[ZZ]);
988
989                 fprintf(fp, "# grp. %d init.center  %12.5e %12.5e %12.5e\n", g,
990                         erg->xc_center[XX], erg->xc_center[YY], erg->xc_center[ZZ]);
991             }
992
993             if ( (rotg->eType == erotgRM2) || (rotg->eType == erotgFLEX2) || (rotg->eType == erotgFLEX2T) )
994             {
995                 fprintf(fp, "# rot-eps%d            %12.5e nm^2\n", g, rotg->eps);
996             }
997             if (erotgFitPOT == rotg->eFittype)
998             {
999                 fprintf(fp, "#\n");
1000                 fprintf(fp, "# theta_fit%d is determined by first evaluating the potential for %d angles around theta_ref%d.\n",
1001                         g, rotg->PotAngle_nstep, g);
1002                 fprintf(fp, "# The fit angle is the one with the smallest potential. It is given as the deviation\n");
1003                 fprintf(fp, "# from the reference angle, i.e. if theta_ref=X and theta_fit=Y, then the angle with\n");
1004                 fprintf(fp, "# minimal value of the potential is X+Y. Angular resolution is %g degrees.\n", rotg->PotAngle_step);
1005             }
1006         }
1007
1008         /* Print a nice legend */
1009         snew(LegendStr, 1);
1010         LegendStr[0] = '\0';
1011         sprintf(buf, "#     %6s", "time");
1012         add_to_string_aligned(&LegendStr, buf);
1013
1014         nsets = 0;
1015         snew(setname, 4*rot->ngrp);
1016
1017         for (int g = 0; g < rot->ngrp; g++)
1018         {
1019             sprintf(buf, "theta_ref%d", g);
1020             add_to_string_aligned(&LegendStr, buf);
1021
1022             sprintf(buf2, "%s (degrees)", buf);
1023             setname[nsets] = gmx_strdup(buf2);
1024             nsets++;
1025         }
1026         for (int g = 0; g < rot->ngrp; g++)
1027         {
1028             const t_rotgrp *rotg = &rot->grp[g];
1029             bFlex = ISFLEX(rotg);
1030
1031             /* For flexible axis rotation we use RMSD fitting to determine the
1032              * actual angle of the rotation group */
1033             if (bFlex || erotgFitPOT == rotg->eFittype)
1034             {
1035                 sprintf(buf, "theta_fit%d", g);
1036             }
1037             else
1038             {
1039                 sprintf(buf, "theta_av%d", g);
1040             }
1041             add_to_string_aligned(&LegendStr, buf);
1042             sprintf(buf2, "%s (degrees)", buf);
1043             setname[nsets] = gmx_strdup(buf2);
1044             nsets++;
1045
1046             sprintf(buf, "tau%d", g);
1047             add_to_string_aligned(&LegendStr, buf);
1048             sprintf(buf2, "%s (kJ/mol)", buf);
1049             setname[nsets] = gmx_strdup(buf2);
1050             nsets++;
1051
1052             sprintf(buf, "energy%d", g);
1053             add_to_string_aligned(&LegendStr, buf);
1054             sprintf(buf2, "%s (kJ/mol)", buf);
1055             setname[nsets] = gmx_strdup(buf2);
1056             nsets++;
1057         }
1058         fprintf(fp, "#\n");
1059
1060         if (nsets > 1)
1061         {
1062             xvgr_legend(fp, nsets, setname, oenv);
1063         }
1064         sfree(setname);
1065
1066         fprintf(fp, "#\n# Legend for the following data columns:\n");
1067         fprintf(fp, "%s\n", LegendStr);
1068         sfree(LegendStr);
1069
1070         fflush(fp);
1071     }
1072
1073     return fp;
1074 }
1075
1076
1077 /* Call on master only */
1078 static FILE *open_angles_out(const char *fn,
1079                              gmx_enfrot *er)
1080 {
1081     FILE        *fp;
1082     char         buf[100];
1083     const t_rot *rot = er->rot;
1084
1085     if (er->appendFiles)
1086     {
1087         fp = gmx_fio_fopen(fn, "a");
1088     }
1089     else
1090     {
1091         /* Open output file and write some information about it's structure: */
1092         fp = open_output_file(fn, er->nstsout, "rotation group angles");
1093         fprintf(fp, "# All angles given in degrees, time in ps.\n");
1094         for (int g = 0; g < rot->ngrp; g++)
1095         {
1096             const t_rotgrp      *rotg = &rot->grp[g];
1097             const gmx_enfrotgrp *erg  = &er->enfrotgrp[g];
1098
1099             /* Output for this group happens only if potential type is flexible or
1100              * if fit type is potential! */
1101             if (ISFLEX(rotg) || (erotgFitPOT == rotg->eFittype) )
1102             {
1103                 if (ISFLEX(rotg))
1104                 {
1105                     sprintf(buf, " slab distance %f nm, ", rotg->slab_dist);
1106                 }
1107                 else
1108                 {
1109                     buf[0] = '\0';
1110                 }
1111
1112                 fprintf(fp, "#\n# ROTATION GROUP %d '%s',%s fit type '%s'.\n",
1113                         g, erotg_names[rotg->eType], buf, erotg_fitnames[rotg->eFittype]);
1114
1115                 /* Special type of fitting using the potential minimum. This is
1116                  * done for the whole group only, not for the individual slabs. */
1117                 if (erotgFitPOT == rotg->eFittype)
1118                 {
1119                     fprintf(fp, "#    To obtain theta_fit%d, the potential is evaluated for %d angles around theta_ref%d\n", g, rotg->PotAngle_nstep, g);
1120                     fprintf(fp, "#    The fit angle in the rotation standard outfile is the one with minimal energy E(theta_fit) [kJ/mol].\n");
1121                     fprintf(fp, "#\n");
1122                 }
1123
1124                 fprintf(fp, "# Legend for the group %d data columns:\n", g);
1125                 fprintf(fp, "#     ");
1126                 print_aligned_short(fp, "time");
1127                 print_aligned_short(fp, "grp");
1128                 print_aligned(fp, "theta_ref");
1129
1130                 if (erotgFitPOT == rotg->eFittype)
1131                 {
1132                     /* Output the set of angles around the reference angle */
1133                     for (int i = 0; i < rotg->PotAngle_nstep; i++)
1134                     {
1135                         sprintf(buf, "E(%g)", erg->PotAngleFit->degangle[i]);
1136                         print_aligned(fp, buf);
1137                     }
1138                 }
1139                 else
1140                 {
1141                     /* Output fit angle for each slab */
1142                     print_aligned_short(fp, "slab");
1143                     print_aligned_short(fp, "atoms");
1144                     print_aligned(fp, "theta_fit");
1145                     print_aligned_short(fp, "slab");
1146                     print_aligned_short(fp, "atoms");
1147                     print_aligned(fp, "theta_fit");
1148                     fprintf(fp, " ...");
1149                 }
1150                 fprintf(fp, "\n");
1151             }
1152         }
1153         fflush(fp);
1154     }
1155
1156     return fp;
1157 }
1158
1159
1160 /* Open torque output file and write some information about it's structure.
1161  * Call on master only */
1162 static FILE *open_torque_out(const char *fn,
1163                              gmx_enfrot *er)
1164 {
1165     FILE        *fp;
1166     const t_rot *rot = er->rot;
1167
1168     if (er->appendFiles)
1169     {
1170         fp = gmx_fio_fopen(fn, "a");
1171     }
1172     else
1173     {
1174         fp = open_output_file(fn, er->nstsout, "torques");
1175
1176         for (int g = 0; g < rot->ngrp; g++)
1177         {
1178             const t_rotgrp      *rotg = &rot->grp[g];
1179             const gmx_enfrotgrp *erg  = &er->enfrotgrp[g];
1180             if (ISFLEX(rotg))
1181             {
1182                 fprintf(fp, "# Rotation group %d (%s), slab distance %f nm.\n", g, erotg_names[rotg->eType], rotg->slab_dist);
1183                 fprintf(fp, "# The scalar tau is the torque (kJ/mol) in the direction of the rotation vector.\n");
1184                 fprintf(fp, "# To obtain the vectorial torque, multiply tau with\n");
1185                 fprintf(fp, "# rot-vec%d            %10.3e %10.3e %10.3e\n", g, erg->vec[XX], erg->vec[YY], erg->vec[ZZ]);
1186                 fprintf(fp, "#\n");
1187             }
1188         }
1189         fprintf(fp, "# Legend for the following data columns: (tau=torque for that slab):\n");
1190         fprintf(fp, "#     ");
1191         print_aligned_short(fp, "t");
1192         print_aligned_short(fp, "grp");
1193         print_aligned_short(fp, "slab");
1194         print_aligned(fp, "tau");
1195         print_aligned_short(fp, "slab");
1196         print_aligned(fp, "tau");
1197         fprintf(fp, " ...\n");
1198         fflush(fp);
1199     }
1200
1201     return fp;
1202 }
1203
1204
1205 static void swap_val(double* vec, int i, int j)
1206 {
1207     double tmp = vec[j];
1208
1209
1210     vec[j] = vec[i];
1211     vec[i] = tmp;
1212 }
1213
1214
1215 static void swap_col(double **mat, int i, int j)
1216 {
1217     double tmp[3] = {mat[0][j], mat[1][j], mat[2][j]};
1218
1219
1220     mat[0][j] = mat[0][i];
1221     mat[1][j] = mat[1][i];
1222     mat[2][j] = mat[2][i];
1223
1224     mat[0][i] = tmp[0];
1225     mat[1][i] = tmp[1];
1226     mat[2][i] = tmp[2];
1227 }
1228
1229
1230 /* Eigenvectors are stored in columns of eigen_vec */
1231 static void diagonalize_symmetric(
1232         double **matrix,
1233         double **eigen_vec,
1234         double   eigenval[3])
1235 {
1236     int n_rot;
1237
1238
1239     jacobi(matrix, 3, eigenval, eigen_vec, &n_rot);
1240
1241     /* sort in ascending order */
1242     if (eigenval[0] > eigenval[1])
1243     {
1244         swap_val(eigenval, 0, 1);
1245         swap_col(eigen_vec, 0, 1);
1246     }
1247     if (eigenval[1] > eigenval[2])
1248     {
1249         swap_val(eigenval, 1, 2);
1250         swap_col(eigen_vec, 1, 2);
1251     }
1252     if (eigenval[0] > eigenval[1])
1253     {
1254         swap_val(eigenval, 0, 1);
1255         swap_col(eigen_vec, 0, 1);
1256     }
1257 }
1258
1259
1260 static void align_with_z(
1261         rvec* s,           /* Structure to align */
1262         int   natoms,
1263         rvec  axis)
1264 {
1265     int     i, j, k;
1266     rvec    zet         = {0.0, 0.0, 1.0};
1267     rvec    rot_axis    = {0.0, 0.0, 0.0};
1268     rvec   *rotated_str = nullptr;
1269     real    ooanorm;
1270     real    angle;
1271     matrix  rotmat;
1272
1273
1274     snew(rotated_str, natoms);
1275
1276     /* Normalize the axis */
1277     ooanorm = 1.0/norm(axis);
1278     svmul(ooanorm, axis, axis);
1279
1280     /* Calculate the angle for the fitting procedure */
1281     cprod(axis, zet, rot_axis);
1282     angle = acos(axis[2]);
1283     if (angle < 0.0)
1284     {
1285         angle += M_PI;
1286     }
1287
1288     /* Calculate the rotation matrix */
1289     calc_rotmat(rot_axis, angle*180.0/M_PI, rotmat);
1290
1291     /* Apply the rotation matrix to s */
1292     for (i = 0; i < natoms; i++)
1293     {
1294         for (j = 0; j < 3; j++)
1295         {
1296             for (k = 0; k < 3; k++)
1297             {
1298                 rotated_str[i][j] += rotmat[j][k]*s[i][k];
1299             }
1300         }
1301     }
1302
1303     /* Rewrite the rotated structure to s */
1304     for (i = 0; i < natoms; i++)
1305     {
1306         for (j = 0; j < 3; j++)
1307         {
1308             s[i][j] = rotated_str[i][j];
1309         }
1310     }
1311
1312     sfree(rotated_str);
1313 }
1314
1315
1316 static void calc_correl_matrix(rvec* Xstr, rvec* Ystr, double** Rmat, int natoms)
1317 {
1318     int i, j, k;
1319
1320
1321     for (i = 0; i < 3; i++)
1322     {
1323         for (j = 0; j < 3; j++)
1324         {
1325             Rmat[i][j] = 0.0;
1326         }
1327     }
1328
1329     for (i = 0; i < 3; i++)
1330     {
1331         for (j = 0; j < 3; j++)
1332         {
1333             for (k = 0; k < natoms; k++)
1334             {
1335                 Rmat[i][j] += Ystr[k][i] * Xstr[k][j];
1336             }
1337         }
1338     }
1339 }
1340
1341
1342 static void weigh_coords(rvec* str, real* weight, int natoms)
1343 {
1344     int i, j;
1345
1346
1347     for (i = 0; i < natoms; i++)
1348     {
1349         for (j = 0; j < 3; j++)
1350         {
1351             str[i][j] *= std::sqrt(weight[i]);
1352         }
1353     }
1354 }
1355
1356
1357 static real opt_angle_analytic(
1358         rvec      * ref_s,
1359         rvec      * act_s,
1360         real      * weight,
1361         int         natoms,
1362         const rvec  ref_com,
1363         const rvec  act_com,
1364         rvec        axis)
1365 {
1366     int      i, j, k;
1367     rvec    *ref_s_1 = nullptr;
1368     rvec    *act_s_1 = nullptr;
1369     rvec     shift;
1370     double **Rmat, **RtR, **eigvec;
1371     double   eigval[3];
1372     double   V[3][3], WS[3][3];
1373     double   rot_matrix[3][3];
1374     double   opt_angle;
1375
1376
1377     /* Do not change the original coordinates */
1378     snew(ref_s_1, natoms);
1379     snew(act_s_1, natoms);
1380     for (i = 0; i < natoms; i++)
1381     {
1382         copy_rvec(ref_s[i], ref_s_1[i]);
1383         copy_rvec(act_s[i], act_s_1[i]);
1384     }
1385
1386     /* Translate the structures to the origin */
1387     shift[XX] = -ref_com[XX];
1388     shift[YY] = -ref_com[YY];
1389     shift[ZZ] = -ref_com[ZZ];
1390     translate_x(ref_s_1, natoms, shift);
1391
1392     shift[XX] = -act_com[XX];
1393     shift[YY] = -act_com[YY];
1394     shift[ZZ] = -act_com[ZZ];
1395     translate_x(act_s_1, natoms, shift);
1396
1397     /* Align rotation axis with z */
1398     align_with_z(ref_s_1, natoms, axis);
1399     align_with_z(act_s_1, natoms, axis);
1400
1401     /* Correlation matrix */
1402     Rmat = allocate_square_matrix(3);
1403
1404     for (i = 0; i < natoms; i++)
1405     {
1406         ref_s_1[i][2] = 0.0;
1407         act_s_1[i][2] = 0.0;
1408     }
1409
1410     /* Weight positions with sqrt(weight) */
1411     if (nullptr != weight)
1412     {
1413         weigh_coords(ref_s_1, weight, natoms);
1414         weigh_coords(act_s_1, weight, natoms);
1415     }
1416
1417     /* Calculate correlation matrices R=YXt (X=ref_s; Y=act_s) */
1418     calc_correl_matrix(ref_s_1, act_s_1, Rmat, natoms);
1419
1420     /* Calculate RtR */
1421     RtR = allocate_square_matrix(3);
1422     for (i = 0; i < 3; i++)
1423     {
1424         for (j = 0; j < 3; j++)
1425         {
1426             for (k = 0; k < 3; k++)
1427             {
1428                 RtR[i][j] += Rmat[k][i] * Rmat[k][j];
1429             }
1430         }
1431     }
1432     /* Diagonalize RtR */
1433     snew(eigvec, 3);
1434     for (i = 0; i < 3; i++)
1435     {
1436         snew(eigvec[i], 3);
1437     }
1438
1439     diagonalize_symmetric(RtR, eigvec, eigval);
1440     swap_col(eigvec, 0, 1);
1441     swap_col(eigvec, 1, 2);
1442     swap_val(eigval, 0, 1);
1443     swap_val(eigval, 1, 2);
1444
1445     /* Calculate V */
1446     for (i = 0; i < 3; i++)
1447     {
1448         for (j = 0; j < 3; j++)
1449         {
1450             V[i][j]  = 0.0;
1451             WS[i][j] = 0.0;
1452         }
1453     }
1454
1455     for (i = 0; i < 2; i++)
1456     {
1457         for (j = 0; j < 2; j++)
1458         {
1459             WS[i][j] = eigvec[i][j] / std::sqrt(eigval[j]);
1460         }
1461     }
1462
1463     for (i = 0; i < 3; i++)
1464     {
1465         for (j = 0; j < 3; j++)
1466         {
1467             for (k = 0; k < 3; k++)
1468             {
1469                 V[i][j] += Rmat[i][k]*WS[k][j];
1470             }
1471         }
1472     }
1473     free_square_matrix(Rmat, 3);
1474
1475     /* Calculate optimal rotation matrix */
1476     for (i = 0; i < 3; i++)
1477     {
1478         for (j = 0; j < 3; j++)
1479         {
1480             rot_matrix[i][j] = 0.0;
1481         }
1482     }
1483
1484     for (i = 0; i < 3; i++)
1485     {
1486         for (j = 0; j < 3; j++)
1487         {
1488             for (k = 0; k < 3; k++)
1489             {
1490                 rot_matrix[i][j] += eigvec[i][k]*V[j][k];
1491             }
1492         }
1493     }
1494     rot_matrix[2][2] = 1.0;
1495
1496     /* In some cases abs(rot_matrix[0][0]) can be slighly larger
1497      * than unity due to numerical inacurracies. To be able to calculate
1498      * the acos function, we put these values back in range. */
1499     if (rot_matrix[0][0] > 1.0)
1500     {
1501         rot_matrix[0][0] = 1.0;
1502     }
1503     else if (rot_matrix[0][0] < -1.0)
1504     {
1505         rot_matrix[0][0] = -1.0;
1506     }
1507
1508     /* Determine the optimal rotation angle: */
1509     opt_angle = (-1.0)*acos(rot_matrix[0][0])*180.0/M_PI;
1510     if (rot_matrix[0][1] < 0.0)
1511     {
1512         opt_angle = (-1.0)*opt_angle;
1513     }
1514
1515     /* Give back some memory */
1516     free_square_matrix(RtR, 3);
1517     sfree(ref_s_1);
1518     sfree(act_s_1);
1519     for (i = 0; i < 3; i++)
1520     {
1521         sfree(eigvec[i]);
1522     }
1523     sfree(eigvec);
1524
1525     return static_cast<real>(opt_angle);
1526 }
1527
1528
1529 /* Determine angle of the group by RMSD fit to the reference */
1530 /* Not parallelized, call this routine only on the master */
1531 static real flex_fit_angle(gmx_enfrotgrp *erg)
1532 {
1533     rvec           *fitcoords = nullptr;
1534     rvec            center;     /* Center of positions passed to the fit routine */
1535     real            fitangle;   /* Angle of the rotation group derived by fitting */
1536     rvec            coord;
1537     real            scal;
1538
1539     /* Get the center of the rotation group.
1540      * Note, again, erg->xc has been sorted in do_flexible */
1541     get_center(erg->xc, erg->mc_sorted, erg->rotg->nat, center);
1542
1543     /* === Determine the optimal fit angle for the rotation group === */
1544     if (erg->rotg->eFittype == erotgFitNORM)
1545     {
1546         /* Normalize every position to it's reference length */
1547         for (int i = 0; i < erg->rotg->nat; i++)
1548         {
1549             /* Put the center of the positions into the origin */
1550             rvec_sub(erg->xc[i], center, coord);
1551             /* Determine the scaling factor for the length: */
1552             scal = erg->xc_ref_length[erg->xc_sortind[i]] / norm(coord);
1553             /* Get position, multiply with the scaling factor and save  */
1554             svmul(scal, coord, erg->xc_norm[i]);
1555         }
1556         fitcoords = erg->xc_norm;
1557     }
1558     else
1559     {
1560         fitcoords = erg->xc;
1561     }
1562     /* From the point of view of the current positions, the reference has rotated
1563      * backwards. Since we output the angle relative to the fixed reference,
1564      * we need the minus sign. */
1565     fitangle = -opt_angle_analytic(erg->xc_ref_sorted, fitcoords, erg->mc_sorted,
1566                                    erg->rotg->nat, erg->xc_ref_center, center, erg->vec);
1567
1568     return fitangle;
1569 }
1570
1571
1572 /* Determine actual angle of each slab by RMSD fit to the reference */
1573 /* Not parallelized, call this routine only on the master */
1574 static void flex_fit_angle_perslab(
1575         gmx_enfrotgrp *erg,
1576         double         t,
1577         real           degangle,
1578         FILE          *fp)
1579 {
1580     rvec            curr_x, ref_x;
1581     rvec            act_center; /* Center of actual positions that are passed to the fit routine */
1582     rvec            ref_center; /* Same for the reference positions */
1583     real            fitangle;   /* Angle of a slab derived from an RMSD fit to
1584                                  * the reference structure at t=0  */
1585     gmx_slabdata   *sd;
1586     real            OOm_av;     /* 1/average_mass of a rotation group atom */
1587     real            m_rel;      /* Relative mass of a rotation group atom  */
1588
1589
1590     /* Average mass of a rotation group atom: */
1591     OOm_av = erg->invmass*erg->rotg->nat;
1592
1593     /**********************************/
1594     /* First collect the data we need */
1595     /**********************************/
1596
1597     /* Collect the data for the individual slabs */
1598     for (int n = erg->slab_first; n <= erg->slab_last; n++)
1599     {
1600         int slabIndex = n - erg->slab_first; /* slab index */
1601         sd      = &(erg->slab_data[slabIndex]);
1602         sd->nat = erg->lastatom[slabIndex]-erg->firstatom[slabIndex]+1;
1603         int ind     = 0;
1604
1605         /* Loop over the relevant atoms in the slab */
1606         for (int l = erg->firstatom[slabIndex]; l <= erg->lastatom[slabIndex]; l++)
1607         {
1608             /* Current position of this atom: x[ii][XX/YY/ZZ] */
1609             copy_rvec(erg->xc[l], curr_x);
1610
1611             /* The (unrotated) reference position of this atom is copied to ref_x.
1612              * Beware, the xc coords have been sorted in do_flexible */
1613             copy_rvec(erg->xc_ref_sorted[l], ref_x);
1614
1615             /* Save data for doing angular RMSD fit later */
1616             /* Save the current atom position */
1617             copy_rvec(curr_x, sd->x[ind]);
1618             /* Save the corresponding reference position */
1619             copy_rvec(ref_x, sd->ref[ind]);
1620
1621             /* Maybe also mass-weighting was requested. If yes, additionally
1622              * multiply the weights with the relative mass of the atom. If not,
1623              * multiply with unity. */
1624             m_rel = erg->mc_sorted[l]*OOm_av;
1625
1626             /* Save the weight for this atom in this slab */
1627             sd->weight[ind] = gaussian_weight(curr_x, erg, n) * m_rel;
1628
1629             /* Next atom in this slab */
1630             ind++;
1631         }
1632     }
1633
1634     /******************************/
1635     /* Now do the fit calculation */
1636     /******************************/
1637
1638     fprintf(fp, "%12.3e%6d%12.3f", t, erg->groupIndex, degangle);
1639
1640     /* === Now do RMSD fitting for each slab === */
1641     /* We require at least SLAB_MIN_ATOMS in a slab, such that the fit makes sense. */
1642 #define SLAB_MIN_ATOMS 4
1643
1644     for (int n = erg->slab_first; n <= erg->slab_last; n++)
1645     {
1646         int slabIndex = n - erg->slab_first; /* slab index */
1647         sd    = &(erg->slab_data[slabIndex]);
1648         if (sd->nat >= SLAB_MIN_ATOMS)
1649         {
1650             /* Get the center of the slabs reference and current positions */
1651             get_center(sd->ref, sd->weight, sd->nat, ref_center);
1652             get_center(sd->x, sd->weight, sd->nat, act_center);
1653             if (erg->rotg->eFittype == erotgFitNORM)
1654             {
1655                 /* Normalize every position to it's reference length
1656                  * prior to performing the fit */
1657                 for (int i = 0; i < sd->nat; i++) /* Center */
1658                 {
1659                     rvec_dec(sd->ref[i], ref_center);
1660                     rvec_dec(sd->x[i], act_center);
1661                     /* Normalize x_i such that it gets the same length as ref_i */
1662                     svmul( norm(sd->ref[i])/norm(sd->x[i]), sd->x[i], sd->x[i] );
1663                 }
1664                 /* We already subtracted the centers */
1665                 clear_rvec(ref_center);
1666                 clear_rvec(act_center);
1667             }
1668             fitangle = -opt_angle_analytic(sd->ref, sd->x, sd->weight, sd->nat,
1669                                            ref_center, act_center, erg->vec);
1670             fprintf(fp, "%6d%6d%12.3f", n, sd->nat, fitangle);
1671         }
1672     }
1673     fprintf(fp, "\n");
1674
1675 #undef SLAB_MIN_ATOMS
1676 }
1677
1678
1679 /* Shift x with is */
1680 static inline void shift_single_coord(const matrix box, rvec x, const ivec is)
1681 {
1682     int tx, ty, tz;
1683
1684
1685     tx = is[XX];
1686     ty = is[YY];
1687     tz = is[ZZ];
1688
1689     if (TRICLINIC(box))
1690     {
1691         x[XX] += tx*box[XX][XX]+ty*box[YY][XX]+tz*box[ZZ][XX];
1692         x[YY] += ty*box[YY][YY]+tz*box[ZZ][YY];
1693         x[ZZ] += tz*box[ZZ][ZZ];
1694     }
1695     else
1696     {
1697         x[XX] += tx*box[XX][XX];
1698         x[YY] += ty*box[YY][YY];
1699         x[ZZ] += tz*box[ZZ][ZZ];
1700     }
1701 }
1702
1703
1704 /* Determine the 'home' slab of this atom which is the
1705  * slab with the highest Gaussian weight of all */
1706 #define round(a) int((a)+0.5)
1707 static inline int get_homeslab(
1708         rvec       curr_x,   /* The position for which the home slab shall be determined */
1709         const rvec rotvec,   /* The rotation vector */
1710         real       slabdist) /* The slab distance */
1711 {
1712     real dist;
1713
1714
1715     /* The distance of the atom to the coordinate center (where the
1716      * slab with index 0) is */
1717     dist = iprod(rotvec, curr_x);
1718
1719     return round(dist / slabdist);
1720 }
1721
1722
1723 /* For a local atom determine the relevant slabs, i.e. slabs in
1724  * which the gaussian is larger than min_gaussian
1725  */
1726 static int get_single_atom_gaussians(
1727         rvec           curr_x,
1728         gmx_enfrotgrp *erg)
1729 {
1730
1731     /* Determine the 'home' slab of this atom: */
1732     int homeslab = get_homeslab(curr_x, erg->vec, erg->rotg->slab_dist);
1733
1734     /* First determine the weight in the atoms home slab: */
1735     real g     = gaussian_weight(curr_x, erg, homeslab);
1736     int  count = 0;
1737     erg->gn_atom[count]    = g;
1738     erg->gn_slabind[count] = homeslab;
1739     count++;
1740
1741
1742     /* Determine the max slab */
1743     int slab = homeslab;
1744     while (g > erg->rotg->min_gaussian)
1745     {
1746         slab++;
1747         g = gaussian_weight(curr_x, erg, slab);
1748         erg->gn_slabind[count] = slab;
1749         erg->gn_atom[count]    = g;
1750         count++;
1751     }
1752     count--;
1753
1754     /* Determine the min slab */
1755     slab = homeslab;
1756     do
1757     {
1758         slab--;
1759         g = gaussian_weight(curr_x, erg, slab);
1760         erg->gn_slabind[count] = slab;
1761         erg->gn_atom[count]    = g;
1762         count++;
1763     }
1764     while (g > erg->rotg->min_gaussian);
1765     count--;
1766
1767     return count;
1768 }
1769
1770
1771 static void flex2_precalc_inner_sum(const gmx_enfrotgrp *erg)
1772 {
1773     rvec            xi;       /* positions in the i-sum                        */
1774     rvec            xcn, ycn; /* the current and the reference slab centers    */
1775     real            gaussian_xi;
1776     rvec            yi0;
1777     rvec            rin;     /* Helper variables                              */
1778     real            fac, fac2;
1779     rvec            innersumvec;
1780     real            OOpsii, OOpsiistar;
1781     real            sin_rin; /* s_ii.r_ii */
1782     rvec            s_in, tmpvec, tmpvec2;
1783     real            mi, wi;  /* Mass-weighting of the positions                 */
1784     real            N_M;     /* N/M                                             */
1785
1786
1787     N_M = erg->rotg->nat * erg->invmass;
1788
1789     /* Loop over all slabs that contain something */
1790     for (int n = erg->slab_first; n <= erg->slab_last; n++)
1791     {
1792         int slabIndex = n - erg->slab_first; /* slab index */
1793
1794         /* The current center of this slab is saved in xcn: */
1795         copy_rvec(erg->slab_center[slabIndex], xcn);
1796         /* ... and the reference center in ycn: */
1797         copy_rvec(erg->slab_center_ref[slabIndex+erg->slab_buffer], ycn);
1798
1799         /*** D. Calculate the whole inner sum used for second and third sum */
1800         /* For slab n, we need to loop over all atoms i again. Since we sorted
1801          * the atoms with respect to the rotation vector, we know that it is sufficient
1802          * to calculate from firstatom to lastatom only. All other contributions will
1803          * be very small. */
1804         clear_rvec(innersumvec);
1805         for (int i = erg->firstatom[slabIndex]; i <= erg->lastatom[slabIndex]; i++)
1806         {
1807             /* Coordinate xi of this atom */
1808             copy_rvec(erg->xc[i], xi);
1809
1810             /* The i-weights */
1811             gaussian_xi = gaussian_weight(xi, erg, n);
1812             mi          = erg->mc_sorted[i]; /* need the sorted mass here */
1813             wi          = N_M*mi;
1814
1815             /* Calculate rin */
1816             copy_rvec(erg->xc_ref_sorted[i], yi0); /* Reference position yi0   */
1817             rvec_sub(yi0, ycn, tmpvec2);           /* tmpvec2 = yi0 - ycn      */
1818             mvmul(erg->rotmat, tmpvec2, rin);      /* rin = Omega.(yi0 - ycn)  */
1819
1820             /* Calculate psi_i* and sin */
1821             rvec_sub(xi, xcn, tmpvec2);                /* tmpvec2 = xi - xcn       */
1822             cprod(erg->vec, tmpvec2, tmpvec);          /* tmpvec = v x (xi - xcn)  */
1823             OOpsiistar = norm2(tmpvec)+erg->rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
1824             OOpsii     = norm(tmpvec);                 /* OOpsii = 1 / psii = |v x (xi - xcn)| */
1825
1826             /*                           *         v x (xi - xcn)          */
1827             unitv(tmpvec, s_in);        /*  sin = ----------------         */
1828                                         /*        |v x (xi - xcn)|         */
1829
1830             sin_rin = iprod(s_in, rin); /* sin_rin = sin . rin             */
1831
1832             /* Now the whole sum */
1833             fac = OOpsii/OOpsiistar;
1834             svmul(fac, rin, tmpvec);
1835             fac2 = fac*fac*OOpsii;
1836             svmul(fac2*sin_rin, s_in, tmpvec2);
1837             rvec_dec(tmpvec, tmpvec2);
1838
1839             svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
1840
1841             rvec_inc(innersumvec, tmpvec2);
1842         } /* now we have the inner sum, used both for sum2 and sum3 */
1843
1844         /* Save it to be used in do_flex2_lowlevel */
1845         copy_rvec(innersumvec, erg->slab_innersumvec[slabIndex]);
1846     } /* END of loop over slabs */
1847 }
1848
1849
1850 static void flex_precalc_inner_sum(const gmx_enfrotgrp *erg)
1851 {
1852     rvec            xi;       /* position                                      */
1853     rvec            xcn, ycn; /* the current and the reference slab centers    */
1854     rvec            qin, rin; /* q_i^n and r_i^n                               */
1855     real            bin;
1856     rvec            tmpvec;
1857     rvec            innersumvec; /* Inner part of sum_n2                          */
1858     real            gaussian_xi; /* Gaussian weight gn(xi)                        */
1859     real            mi, wi;      /* Mass-weighting of the positions               */
1860     real            N_M;         /* N/M                                           */
1861
1862     N_M = erg->rotg->nat * erg->invmass;
1863
1864     /* Loop over all slabs that contain something */
1865     for (int n = erg->slab_first; n <= erg->slab_last; n++)
1866     {
1867         int slabIndex = n - erg->slab_first; /* slab index */
1868
1869         /* The current center of this slab is saved in xcn: */
1870         copy_rvec(erg->slab_center[slabIndex], xcn);
1871         /* ... and the reference center in ycn: */
1872         copy_rvec(erg->slab_center_ref[slabIndex+erg->slab_buffer], ycn);
1873
1874         /* For slab n, we need to loop over all atoms i again. Since we sorted
1875          * the atoms with respect to the rotation vector, we know that it is sufficient
1876          * to calculate from firstatom to lastatom only. All other contributions will
1877          * be very small. */
1878         clear_rvec(innersumvec);
1879         for (int i = erg->firstatom[slabIndex]; i <= erg->lastatom[slabIndex]; i++)
1880         {
1881             /* Coordinate xi of this atom */
1882             copy_rvec(erg->xc[i], xi);
1883
1884             /* The i-weights */
1885             gaussian_xi = gaussian_weight(xi, erg, n);
1886             mi          = erg->mc_sorted[i]; /* need the sorted mass here */
1887             wi          = N_M*mi;
1888
1889             /* Calculate rin and qin */
1890             rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
1891             mvmul(erg->rotmat, tmpvec, rin);              /* rin = Omega.(yi0 - ycn)  */
1892             cprod(erg->vec, rin, tmpvec);                 /* tmpvec = v x Omega*(yi0-ycn) */
1893
1894             /*                                *        v x Omega*(yi0-ycn)    */
1895             unitv(tmpvec, qin);              /* qin = ---------------------   */
1896                                              /*       |v x Omega*(yi0-ycn)|   */
1897
1898             /* Calculate bin */
1899             rvec_sub(xi, xcn, tmpvec);            /* tmpvec = xi-xcn          */
1900             bin = iprod(qin, tmpvec);             /* bin  = qin*(xi-xcn)      */
1901
1902             svmul(wi*gaussian_xi*bin, qin, tmpvec);
1903
1904             /* Add this contribution to the inner sum: */
1905             rvec_add(innersumvec, tmpvec, innersumvec);
1906         } /* now we have the inner sum vector S^n for this slab */
1907           /* Save it to be used in do_flex_lowlevel */
1908         copy_rvec(innersumvec, erg->slab_innersumvec[slabIndex]);
1909     }
1910 }
1911
1912
1913 static real do_flex2_lowlevel(
1914         gmx_enfrotgrp *erg,
1915         real           sigma, /* The Gaussian width sigma */
1916         rvec           x[],
1917         gmx_bool       bOutstepRot,
1918         gmx_bool       bOutstepSlab,
1919         matrix         box)
1920 {
1921     int             count, ii, iigrp;
1922     rvec            xj;          /* position in the i-sum                         */
1923     rvec            yj0;         /* the reference position in the j-sum           */
1924     rvec            xcn, ycn;    /* the current and the reference slab centers    */
1925     real            V;           /* This node's part of the rotation pot. energy  */
1926     real            gaussian_xj; /* Gaussian weight                               */
1927     real            beta;
1928
1929     real            numerator, fit_numerator;
1930     rvec            rjn, fit_rjn; /* Helper variables                              */
1931     real            fac, fac2;
1932
1933     real            OOpsij, OOpsijstar;
1934     real            OOsigma2; /* 1/(sigma^2)                                   */
1935     real            sjn_rjn;
1936     real            betasigpsi;
1937     rvec            sjn, tmpvec, tmpvec2, yj0_ycn;
1938     rvec            sum1vec_part, sum1vec, sum2vec_part, sum2vec, sum3vec, sum4vec, innersumvec;
1939     real            sum3, sum4;
1940     real            mj, wj;  /* Mass-weighting of the positions               */
1941     real            N_M;     /* N/M                                           */
1942     real            Wjn;     /* g_n(x_j) m_j / Mjn                            */
1943     gmx_bool        bCalcPotFit;
1944
1945     /* To calculate the torque per slab */
1946     rvec slab_force;         /* Single force from slab n on one atom          */
1947     rvec slab_sum1vec_part;
1948     real slab_sum3part, slab_sum4part;
1949     rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
1950
1951     /* Pre-calculate the inner sums, so that we do not have to calculate
1952      * them again for every atom */
1953     flex2_precalc_inner_sum(erg);
1954
1955     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
1956
1957     /********************************************************/
1958     /* Main loop over all local atoms of the rotation group */
1959     /********************************************************/
1960     N_M      = erg->rotg->nat * erg->invmass;
1961     V        = 0.0;
1962     OOsigma2 = 1.0 / (sigma*sigma);
1963     const auto &localRotationGroupIndex      = erg->atomSet->localIndex();
1964     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
1965
1966     for (gmx::index j = 0; j < localRotationGroupIndex.size(); j++)
1967     {
1968         /* Local index of a rotation group atom  */
1969         ii = localRotationGroupIndex[j];
1970         /* Position of this atom in the collective array */
1971         iigrp = collectiveRotationGroupIndex[j];
1972         /* Mass-weighting */
1973         mj = erg->mc[iigrp];  /* need the unsorted mass here */
1974         wj = N_M*mj;
1975
1976         /* Current position of this atom: x[ii][XX/YY/ZZ]
1977          * Note that erg->xc_center contains the center of mass in case the flex2-t
1978          * potential was chosen. For the flex2 potential erg->xc_center must be
1979          * zero. */
1980         rvec_sub(x[ii], erg->xc_center, xj);
1981
1982         /* Shift this atom such that it is near its reference */
1983         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
1984
1985         /* Determine the slabs to loop over, i.e. the ones with contributions
1986          * larger than min_gaussian */
1987         count = get_single_atom_gaussians(xj, erg);
1988
1989         clear_rvec(sum1vec_part);
1990         clear_rvec(sum2vec_part);
1991         sum3 = 0.0;
1992         sum4 = 0.0;
1993         /* Loop over the relevant slabs for this atom */
1994         for (int ic = 0; ic < count; ic++)
1995         {
1996             int n = erg->gn_slabind[ic];
1997
1998             /* Get the precomputed Gaussian value of curr_slab for curr_x */
1999             gaussian_xj = erg->gn_atom[ic];
2000
2001             int slabIndex = n - erg->slab_first; /* slab index */
2002
2003             /* The (unrotated) reference position of this atom is copied to yj0: */
2004             copy_rvec(erg->rotg->x_ref[iigrp], yj0);
2005
2006             beta = calc_beta(xj, erg, n);
2007
2008             /* The current center of this slab is saved in xcn: */
2009             copy_rvec(erg->slab_center[slabIndex], xcn);
2010             /* ... and the reference center in ycn: */
2011             copy_rvec(erg->slab_center_ref[slabIndex+erg->slab_buffer], ycn);
2012
2013             rvec_sub(yj0, ycn, yj0_ycn);          /* yj0_ycn = yj0 - ycn      */
2014
2015             /* Rotate: */
2016             mvmul(erg->rotmat, yj0_ycn, rjn);     /* rjn = Omega.(yj0 - ycn)  */
2017
2018             /* Subtract the slab center from xj */
2019             rvec_sub(xj, xcn, tmpvec2);           /* tmpvec2 = xj - xcn       */
2020
2021             /* In rare cases, when an atom position coincides with a slab center
2022              * (tmpvec2 == 0) we cannot compute the vector product for sjn.
2023              * However, since the atom is located directly on the pivot, this
2024              * slab's contribution to the force on that atom will be zero
2025              * anyway. Therefore, we directly move on to the next slab.       */
2026             if (gmx_numzero(norm(tmpvec2))) /* 0 == norm(xj - xcn) */
2027             {
2028                 continue;
2029             }
2030
2031             /* Calculate sjn */
2032             cprod(erg->vec, tmpvec2, tmpvec);          /* tmpvec = v x (xj - xcn)  */
2033
2034             OOpsijstar = norm2(tmpvec)+erg->rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
2035
2036             numerator = gmx::square(iprod(tmpvec, rjn));
2037
2038             /*********************************/
2039             /* Add to the rotation potential */
2040             /*********************************/
2041             V += 0.5*erg->rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
2042
2043             /* If requested, also calculate the potential for a set of angles
2044              * near the current reference angle */
2045             if (bCalcPotFit)
2046             {
2047                 for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2048                 {
2049                     mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, fit_rjn);
2050                     fit_numerator              = gmx::square(iprod(tmpvec, fit_rjn));
2051                     erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*gaussian_xj*fit_numerator/OOpsijstar;
2052                 }
2053             }
2054
2055             /*************************************/
2056             /* Now calculate the force on atom j */
2057             /*************************************/
2058
2059             OOpsij = norm(tmpvec);    /* OOpsij = 1 / psij = |v x (xj - xcn)| */
2060
2061             /*                              *         v x (xj - xcn)          */
2062             unitv(tmpvec, sjn);            /*  sjn = ----------------         */
2063                                            /*        |v x (xj - xcn)|         */
2064
2065             sjn_rjn = iprod(sjn, rjn);     /* sjn_rjn = sjn . rjn             */
2066
2067
2068             /*** A. Calculate the first of the four sum terms: ****************/
2069             fac = OOpsij/OOpsijstar;
2070             svmul(fac, rjn, tmpvec);
2071             fac2 = fac*fac*OOpsij;
2072             svmul(fac2*sjn_rjn, sjn, tmpvec2);
2073             rvec_dec(tmpvec, tmpvec2);
2074             fac2 = wj*gaussian_xj; /* also needed for sum4 */
2075             svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
2076             /********************/
2077             /*** Add to sum1: ***/
2078             /********************/
2079             rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
2080
2081             /*** B. Calculate the forth of the four sum terms: ****************/
2082             betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
2083             /********************/
2084             /*** Add to sum4: ***/
2085             /********************/
2086             slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
2087             sum4         += slab_sum4part;
2088
2089             /*** C. Calculate Wjn for second and third sum */
2090             /* Note that we can safely divide by slab_weights since we check in
2091              * get_slab_centers that it is non-zero. */
2092             Wjn = gaussian_xj*mj/erg->slab_weights[slabIndex];
2093
2094             /* We already have precalculated the inner sum for slab n */
2095             copy_rvec(erg->slab_innersumvec[slabIndex], innersumvec);
2096
2097             /* Weigh the inner sum vector with Wjn */
2098             svmul(Wjn, innersumvec, innersumvec);
2099
2100             /*** E. Calculate the second of the four sum terms: */
2101             /********************/
2102             /*** Add to sum2: ***/
2103             /********************/
2104             rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
2105
2106             /*** F. Calculate the third of the four sum terms: */
2107             slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
2108             sum3         += slab_sum3part; /* still needs to be multiplied with v */
2109
2110             /*** G. Calculate the torque on the local slab's axis: */
2111             if (bOutstepRot)
2112             {
2113                 /* Sum1 */
2114                 cprod(slab_sum1vec_part, erg->vec, slab_sum1vec);
2115                 /* Sum2 */
2116                 cprod(innersumvec, erg->vec, slab_sum2vec);
2117                 /* Sum3 */
2118                 svmul(slab_sum3part, erg->vec, slab_sum3vec);
2119                 /* Sum4 */
2120                 svmul(slab_sum4part, erg->vec, slab_sum4vec);
2121
2122                 /* The force on atom ii from slab n only: */
2123                 for (int m = 0; m < DIM; m++)
2124                 {
2125                     slab_force[m] = erg->rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
2126                 }
2127
2128                 erg->slab_torque_v[slabIndex] += torque(erg->vec, slab_force, xj, xcn);
2129             }
2130         } /* END of loop over slabs */
2131
2132         /* Construct the four individual parts of the vector sum: */
2133         cprod(sum1vec_part, erg->vec, sum1vec);      /* sum1vec =   { } x v  */
2134         cprod(sum2vec_part, erg->vec, sum2vec);      /* sum2vec =   { } x v  */
2135         svmul(sum3, erg->vec, sum3vec);              /* sum3vec =   { } . v  */
2136         svmul(sum4, erg->vec, sum4vec);              /* sum4vec =   { } . v  */
2137
2138         /* Store the additional force so that it can be added to the force
2139          * array after the normal forces have been evaluated */
2140         for (int m = 0; m < DIM; m++)
2141         {
2142             erg->f_rot_loc[j][m] = erg->rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
2143         }
2144
2145 #ifdef SUM_PARTS
2146         fprintf(stderr, "sum1: %15.8f %15.8f %15.8f\n",    -erg->rotg->k*sum1vec[XX],    -erg->rotg->k*sum1vec[YY],    -erg->rotg->k*sum1vec[ZZ]);
2147         fprintf(stderr, "sum2: %15.8f %15.8f %15.8f\n",     erg->rotg->k*sum2vec[XX],     erg->rotg->k*sum2vec[YY],     erg->rotg->k*sum2vec[ZZ]);
2148         fprintf(stderr, "sum3: %15.8f %15.8f %15.8f\n",    -erg->rotg->k*sum3vec[XX],    -erg->rotg->k*sum3vec[YY],    -erg->rotg->k*sum3vec[ZZ]);
2149         fprintf(stderr, "sum4: %15.8f %15.8f %15.8f\n", 0.5*erg->rotg->k*sum4vec[XX], 0.5*erg->rotg->k*sum4vec[YY], 0.5*erg->rotg->k*sum4vec[ZZ]);
2150 #endif
2151
2152         PRINT_FORCE_J
2153
2154     } /* END of loop over local atoms */
2155
2156     return V;
2157 }
2158
2159
2160 static real do_flex_lowlevel(
2161         gmx_enfrotgrp *erg,
2162         real           sigma, /* The Gaussian width sigma                      */
2163         rvec           x[],
2164         gmx_bool       bOutstepRot,
2165         gmx_bool       bOutstepSlab,
2166         matrix         box)
2167 {
2168     int             count, iigrp;
2169     rvec            xj, yj0;                /* current and reference position                */
2170     rvec            xcn, ycn;               /* the current and the reference slab centers    */
2171     rvec            yj0_ycn;                /* yj0 - ycn                                     */
2172     rvec            xj_xcn;                 /* xj - xcn                                      */
2173     rvec            qjn, fit_qjn;           /* q_i^n                                         */
2174     rvec            sum_n1, sum_n2;         /* Two contributions to the rotation force       */
2175     rvec            innersumvec;            /* Inner part of sum_n2                          */
2176     rvec            s_n;
2177     rvec            force_n;                /* Single force from slab n on one atom          */
2178     rvec            force_n1, force_n2;     /* First and second part of force_n              */
2179     rvec            tmpvec, tmpvec2, tmp_f; /* Helper variables                              */
2180     real            V;                      /* The rotation potential energy                 */
2181     real            OOsigma2;               /* 1/(sigma^2)                                   */
2182     real            beta;                   /* beta_n(xj)                                    */
2183     real            bjn, fit_bjn;           /* b_j^n                                         */
2184     real            gaussian_xj;            /* Gaussian weight gn(xj)                        */
2185     real            betan_xj_sigma2;
2186     real            mj, wj;                 /* Mass-weighting of the positions               */
2187     real            N_M;                    /* N/M                                           */
2188     gmx_bool        bCalcPotFit;
2189
2190     /* Pre-calculate the inner sums, so that we do not have to calculate
2191      * them again for every atom */
2192     flex_precalc_inner_sum(erg);
2193
2194     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
2195
2196     /********************************************************/
2197     /* Main loop over all local atoms of the rotation group */
2198     /********************************************************/
2199     OOsigma2 = 1.0/(sigma*sigma);
2200     N_M      = erg->rotg->nat * erg->invmass;
2201     V        = 0.0;
2202     const auto &localRotationGroupIndex      = erg->atomSet->localIndex();
2203     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
2204
2205     for (gmx::index j = 0; j < localRotationGroupIndex.size(); j++)
2206     {
2207         /* Local index of a rotation group atom  */
2208         int ii = localRotationGroupIndex[j];
2209         /* Position of this atom in the collective array */
2210         iigrp = collectiveRotationGroupIndex[j];
2211         /* Mass-weighting */
2212         mj = erg->mc[iigrp];  /* need the unsorted mass here */
2213         wj = N_M*mj;
2214
2215         /* Current position of this atom: x[ii][XX/YY/ZZ]
2216          * Note that erg->xc_center contains the center of mass in case the flex-t
2217          * potential was chosen. For the flex potential erg->xc_center must be
2218          * zero. */
2219         rvec_sub(x[ii], erg->xc_center, xj);
2220
2221         /* Shift this atom such that it is near its reference */
2222         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
2223
2224         /* Determine the slabs to loop over, i.e. the ones with contributions
2225          * larger than min_gaussian */
2226         count = get_single_atom_gaussians(xj, erg);
2227
2228         clear_rvec(sum_n1);
2229         clear_rvec(sum_n2);
2230
2231         /* Loop over the relevant slabs for this atom */
2232         for (int ic = 0; ic < count; ic++)
2233         {
2234             int n = erg->gn_slabind[ic];
2235
2236             /* Get the precomputed Gaussian for xj in slab n */
2237             gaussian_xj = erg->gn_atom[ic];
2238
2239             int slabIndex = n - erg->slab_first; /* slab index */
2240
2241             /* The (unrotated) reference position of this atom is saved in yj0: */
2242             copy_rvec(erg->rotg->x_ref[iigrp], yj0);
2243
2244             beta = calc_beta(xj, erg, n);
2245
2246             /* The current center of this slab is saved in xcn: */
2247             copy_rvec(erg->slab_center[slabIndex], xcn);
2248             /* ... and the reference center in ycn: */
2249             copy_rvec(erg->slab_center_ref[slabIndex+erg->slab_buffer], ycn);
2250
2251             rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
2252
2253             /* In rare cases, when an atom position coincides with a reference slab
2254              * center (yj0_ycn == 0) we cannot compute the normal vector qjn.
2255              * However, since the atom is located directly on the pivot, this
2256              * slab's contribution to the force on that atom will be zero
2257              * anyway. Therefore, we directly move on to the next slab.       */
2258             if (gmx_numzero(norm(yj0_ycn))) /* 0 == norm(yj0 - ycn) */
2259             {
2260                 continue;
2261             }
2262
2263             /* Rotate: */
2264             mvmul(erg->rotmat, yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
2265
2266             /* Subtract the slab center from xj */
2267             rvec_sub(xj, xcn, xj_xcn);           /* xj_xcn = xj - xcn         */
2268
2269             /* Calculate qjn */
2270             cprod(erg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
2271
2272             /*                         *         v x Omega.(yj0-ycn)    */
2273             unitv(tmpvec, qjn);       /*  qjn = ---------------------   */
2274                                       /*        |v x Omega.(yj0-ycn)|   */
2275
2276             bjn = iprod(qjn, xj_xcn); /* bjn = qjn * (xj - xcn) */
2277
2278             /*********************************/
2279             /* Add to the rotation potential */
2280             /*********************************/
2281             V += 0.5*erg->rotg->k*wj*gaussian_xj*gmx::square(bjn);
2282
2283             /* If requested, also calculate the potential for a set of angles
2284              * near the current reference angle */
2285             if (bCalcPotFit)
2286             {
2287                 for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2288                 {
2289                     /* As above calculate Omega.(yj0-ycn), now for the other angles */
2290                     mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
2291                     /* As above calculate qjn */
2292                     cprod(erg->vec, tmpvec2, tmpvec);                        /* tmpvec= v x Omega.(yj0-ycn) */
2293                     /*                                                        *             v x Omega.(yj0-ycn)    */
2294                     unitv(tmpvec, fit_qjn);                                  /*  fit_qjn = ---------------------   */
2295                                                                              /*            |v x Omega.(yj0-ycn)|   */
2296                     fit_bjn = iprod(fit_qjn, xj_xcn);                        /* fit_bjn = fit_qjn * (xj - xcn) */
2297                     /* Add to the rotation potential for this angle */
2298                     erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*gaussian_xj*gmx::square(fit_bjn);
2299                 }
2300             }
2301
2302             /****************************************************************/
2303             /* sum_n1 will typically be the main contribution to the force: */
2304             /****************************************************************/
2305             betan_xj_sigma2 = beta*OOsigma2;  /*  beta_n(xj)/sigma^2  */
2306
2307             /* The next lines calculate
2308              *  qjn - (bjn*beta(xj)/(2sigma^2))v  */
2309             svmul(bjn*0.5*betan_xj_sigma2, erg->vec, tmpvec2);
2310             rvec_sub(qjn, tmpvec2, tmpvec);
2311
2312             /* Multiply with gn(xj)*bjn: */
2313             svmul(gaussian_xj*bjn, tmpvec, tmpvec2);
2314
2315             /* Sum over n: */
2316             rvec_inc(sum_n1, tmpvec2);
2317
2318             /* We already have precalculated the Sn term for slab n */
2319             copy_rvec(erg->slab_innersumvec[slabIndex], s_n);
2320             /*                                                             *          beta_n(xj)              */
2321             svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), erg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
2322                                                                          /*            sigma^2               */
2323
2324             rvec_sub(s_n, tmpvec, innersumvec);
2325
2326             /* We can safely divide by slab_weights since we check in get_slab_centers
2327              * that it is non-zero. */
2328             svmul(gaussian_xj/erg->slab_weights[slabIndex], innersumvec, innersumvec);
2329
2330             rvec_add(sum_n2, innersumvec, sum_n2);
2331
2332             /* Calculate the torque: */
2333             if (bOutstepRot)
2334             {
2335                 /* The force on atom ii from slab n only: */
2336                 svmul(-erg->rotg->k*wj, tmpvec2, force_n1);     /* part 1 */
2337                 svmul( erg->rotg->k*mj, innersumvec, force_n2); /* part 2 */
2338                 rvec_add(force_n1, force_n2, force_n);
2339                 erg->slab_torque_v[slabIndex] += torque(erg->vec, force_n, xj, xcn);
2340             }
2341         } /* END of loop over slabs */
2342
2343         /* Put both contributions together: */
2344         svmul(wj, sum_n1, sum_n1);
2345         svmul(mj, sum_n2, sum_n2);
2346         rvec_sub(sum_n2, sum_n1, tmp_f); /* F = -grad V */
2347
2348         /* Store the additional force so that it can be added to the force
2349          * array after the normal forces have been evaluated */
2350         for (int m = 0; m < DIM; m++)
2351         {
2352             erg->f_rot_loc[j][m] = erg->rotg->k*tmp_f[m];
2353         }
2354
2355         PRINT_FORCE_J
2356
2357     } /* END of loop over local atoms */
2358
2359     return V;
2360 }
2361
2362 static void sort_collective_coordinates(
2363         gmx_enfrotgrp    *erg,
2364         sort_along_vec_t *data) /* Buffer for sorting the positions */
2365 {
2366     /* The projection of the position vector on the rotation vector is
2367      * the relevant value for sorting. Fill the 'data' structure */
2368     for (int i = 0; i < erg->rotg->nat; i++)
2369     {
2370         data[i].xcproj = iprod(erg->xc[i], erg->vec);  /* sort criterium */
2371         data[i].m      = erg->mc[i];
2372         data[i].ind    = i;
2373         copy_rvec(erg->xc[i], data[i].x    );
2374         copy_rvec(erg->rotg->x_ref[i], data[i].x_ref);
2375     }
2376     /* Sort the 'data' structure */
2377     std::sort(data, data+erg->rotg->nat,
2378               [](const sort_along_vec_t &a, const sort_along_vec_t &b)
2379               {
2380                   return a.xcproj < b.xcproj;
2381               });
2382
2383     /* Copy back the sorted values */
2384     for (int i = 0; i < erg->rotg->nat; i++)
2385     {
2386         copy_rvec(data[i].x, erg->xc[i]           );
2387         copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
2388         erg->mc_sorted[i]  = data[i].m;
2389         erg->xc_sortind[i] = data[i].ind;
2390     }
2391 }
2392
2393
2394 /* For each slab, get the first and the last index of the sorted atom
2395  * indices */
2396 static void get_firstlast_atom_per_slab(const gmx_enfrotgrp *erg)
2397 {
2398     real beta;
2399
2400     /* Find the first atom that needs to enter the calculation for each slab */
2401     int n = erg->slab_first; /* slab */
2402     int i = 0;               /* start with the first atom */
2403     do
2404     {
2405         /* Find the first atom that significantly contributes to this slab */
2406         do /* move forward in position until a large enough beta is found */
2407         {
2408             beta = calc_beta(erg->xc[i], erg, n);
2409             i++;
2410         }
2411         while ((beta < -erg->max_beta) && (i < erg->rotg->nat));
2412         i--;
2413         int slabIndex             = n - erg->slab_first; /* slab index */
2414         erg->firstatom[slabIndex] = i;
2415         /* Proceed to the next slab */
2416         n++;
2417     }
2418     while (n <= erg->slab_last);
2419
2420     /* Find the last atom for each slab */
2421     n = erg->slab_last;   /* start with last slab */
2422     i = erg->rotg->nat-1; /* start with the last atom */
2423     do
2424     {
2425         do  /* move backward in position until a large enough beta is found */
2426         {
2427             beta = calc_beta(erg->xc[i], erg, n);
2428             i--;
2429         }
2430         while ((beta > erg->max_beta) && (i > -1));
2431         i++;
2432         int slabIndex            = n - erg->slab_first; /* slab index */
2433         erg->lastatom[slabIndex] = i;
2434         /* Proceed to the next slab */
2435         n--;
2436     }
2437     while (n >= erg->slab_first);
2438 }
2439
2440
2441 /* Determine the very first and very last slab that needs to be considered
2442  * For the first slab that needs to be considered, we have to find the smallest
2443  * n that obeys:
2444  *
2445  * x_first * v - n*Delta_x <= beta_max
2446  *
2447  * slab index n, slab distance Delta_x, rotation vector v. For the last slab we
2448  * have to find the largest n that obeys
2449  *
2450  * x_last * v - n*Delta_x >= -beta_max
2451  *
2452  */
2453 static inline int get_first_slab(
2454         const gmx_enfrotgrp *erg,
2455         rvec                 firstatom) /* First atom after sorting along the rotation vector v */
2456 {
2457     /* Find the first slab for the first atom */
2458     return static_cast<int>(ceil(static_cast<double>((iprod(firstatom, erg->vec) - erg->max_beta)/erg->rotg->slab_dist)));
2459 }
2460
2461
2462 static inline int get_last_slab(
2463         const gmx_enfrotgrp *erg,
2464         rvec                 lastatom) /* Last atom along v */
2465 {
2466     /* Find the last slab for the last atom */
2467     return static_cast<int>(floor(static_cast<double>((iprod(lastatom, erg->vec) + erg->max_beta)/erg->rotg->slab_dist)));
2468 }
2469
2470
2471 static void get_firstlast_slab_check(
2472         gmx_enfrotgrp   *erg,       /* The rotation group (data only accessible in this file) */
2473         rvec             firstatom, /* First atom after sorting along the rotation vector v */
2474         rvec             lastatom)  /* Last atom along v */
2475 {
2476     erg->slab_first = get_first_slab(erg, firstatom);
2477     erg->slab_last  = get_last_slab(erg, lastatom);
2478
2479     /* Calculate the slab buffer size, which changes when slab_first changes */
2480     erg->slab_buffer = erg->slab_first - erg->slab_first_ref;
2481
2482     /* Check whether we have reference data to compare against */
2483     if (erg->slab_first < erg->slab_first_ref)
2484     {
2485         gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
2486                   RotStr, erg->slab_first);
2487     }
2488
2489     /* Check whether we have reference data to compare against */
2490     if (erg->slab_last > erg->slab_last_ref)
2491     {
2492         gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
2493                   RotStr, erg->slab_last);
2494     }
2495 }
2496
2497
2498 /* Enforced rotation with a flexible axis */
2499 static void do_flexible(
2500         gmx_bool        bMaster,
2501         gmx_enfrot     *enfrot,       /* Other rotation data                        */
2502         gmx_enfrotgrp  *erg,
2503         rvec            x[],          /* The local positions                        */
2504         matrix          box,
2505         double          t,            /* Time in picoseconds                        */
2506         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
2507         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
2508 {
2509     int             nslabs;
2510     real            sigma;    /* The Gaussian width sigma */
2511
2512     /* Define the sigma value */
2513     sigma = 0.7*erg->rotg->slab_dist;
2514
2515     /* Sort the collective coordinates erg->xc along the rotation vector. This is
2516      * an optimization for the inner loop. */
2517     sort_collective_coordinates(erg, enfrot->data);
2518
2519     /* Determine the first relevant slab for the first atom and the last
2520      * relevant slab for the last atom */
2521     get_firstlast_slab_check(erg, erg->xc[0], erg->xc[erg->rotg->nat-1]);
2522
2523     /* Determine for each slab depending on the min_gaussian cutoff criterium,
2524      * a first and a last atom index inbetween stuff needs to be calculated */
2525     get_firstlast_atom_per_slab(erg);
2526
2527     /* Determine the gaussian-weighted center of positions for all slabs */
2528     get_slab_centers(erg, erg->xc, erg->mc_sorted, t, enfrot->out_slabs, bOutstepSlab, FALSE);
2529
2530     /* Clear the torque per slab from last time step: */
2531     nslabs = erg->slab_last - erg->slab_first + 1;
2532     for (int l = 0; l < nslabs; l++)
2533     {
2534         erg->slab_torque_v[l] = 0.0;
2535     }
2536
2537     /* Call the rotational forces kernel */
2538     if (erg->rotg->eType == erotgFLEX || erg->rotg->eType == erotgFLEXT)
2539     {
2540         erg->V = do_flex_lowlevel(erg, sigma, x, bOutstepRot, bOutstepSlab, box);
2541     }
2542     else if (erg->rotg->eType == erotgFLEX2 || erg->rotg->eType == erotgFLEX2T)
2543     {
2544         erg->V = do_flex2_lowlevel(erg, sigma, x, bOutstepRot, bOutstepSlab, box);
2545     }
2546     else
2547     {
2548         gmx_fatal(FARGS, "Unknown flexible rotation type");
2549     }
2550
2551     /* Determine angle by RMSD fit to the reference - Let's hope this */
2552     /* only happens once in a while, since this is not parallelized! */
2553     if (bMaster && (erotgFitPOT != erg->rotg->eFittype) )
2554     {
2555         if (bOutstepRot)
2556         {
2557             /* Fit angle of the whole rotation group */
2558             erg->angle_v = flex_fit_angle(erg);
2559         }
2560         if (bOutstepSlab)
2561         {
2562             /* Fit angle of each slab */
2563             flex_fit_angle_perslab(erg, t, erg->degangle, enfrot->out_angles);
2564         }
2565     }
2566
2567     /* Lump together the torques from all slabs: */
2568     erg->torque_v = 0.0;
2569     for (int l = 0; l < nslabs; l++)
2570     {
2571         erg->torque_v += erg->slab_torque_v[l];
2572     }
2573 }
2574
2575
2576 /* Calculate the angle between reference and actual rotation group atom,
2577  * both projected into a plane perpendicular to the rotation vector: */
2578 static void angle(const gmx_enfrotgrp *erg,
2579                   rvec                 x_act,
2580                   rvec                 x_ref,
2581                   real                *alpha,
2582                   real                *weight) /* atoms near the rotation axis should count less than atoms far away */
2583 {
2584     rvec xp, xrp;                              /* current and reference positions projected on a plane perpendicular to pg->vec */
2585     rvec dum;
2586
2587
2588     /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
2589     /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
2590     svmul(iprod(erg->vec, x_ref), erg->vec, dum);
2591     rvec_sub(x_ref, dum, xrp);
2592     /* Project x_act: */
2593     svmul(iprod(erg->vec, x_act), erg->vec, dum);
2594     rvec_sub(x_act, dum, xp);
2595
2596     /* Retrieve information about which vector precedes. gmx_angle always
2597      * returns a positive angle. */
2598     cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
2599
2600     if (iprod(erg->vec, dum) >= 0)
2601     {
2602         *alpha = -gmx_angle(xrp, xp);
2603     }
2604     else
2605     {
2606         *alpha = +gmx_angle(xrp, xp);
2607     }
2608
2609     /* Also return the weight */
2610     *weight = norm(xp);
2611 }
2612
2613
2614 /* Project first vector onto a plane perpendicular to the second vector
2615  * dr = dr - (dr.v)v
2616  * Note that v must be of unit length.
2617  */
2618 static inline void project_onto_plane(rvec dr, const rvec v)
2619 {
2620     rvec tmp;
2621
2622
2623     svmul(iprod(dr, v), v, tmp); /* tmp = (dr.v)v */
2624     rvec_dec(dr, tmp);           /* dr = dr - (dr.v)v */
2625 }
2626
2627
2628 /* Fixed rotation: The rotation reference group rotates around the v axis. */
2629 /* The atoms of the actual rotation group are attached with imaginary  */
2630 /* springs to the reference atoms.                                     */
2631 static void do_fixed(
2632         gmx_enfrotgrp  *erg,
2633         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
2634         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
2635 {
2636     rvec            dr;
2637     rvec            tmp_f;     /* Force */
2638     real            alpha;     /* a single angle between an actual and a reference position */
2639     real            weight;    /* single weight for a single angle */
2640     rvec            xi_xc;     /* xi - xc */
2641     gmx_bool        bCalcPotFit;
2642     rvec            fit_xr_loc;
2643
2644     /* for mass weighting: */
2645     real      wi;              /* Mass-weighting of the positions */
2646     real      N_M;             /* N/M */
2647     real      k_wi;            /* k times wi */
2648
2649     gmx_bool  bProject;
2650
2651     bProject    = (erg->rotg->eType == erotgPM) || (erg->rotg->eType == erotgPMPF);
2652     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
2653
2654     N_M = erg->rotg->nat * erg->invmass;
2655     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
2656     /* Each process calculates the forces on its local atoms */
2657     for (size_t j = 0; j < erg->atomSet->numAtomsLocal(); j++)
2658     {
2659         /* Calculate (x_i-x_c) resp. (x_i-u) */
2660         rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xi_xc);
2661
2662         /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
2663         rvec_sub(erg->xr_loc[j], xi_xc, dr);
2664
2665         if (bProject)
2666         {
2667             project_onto_plane(dr, erg->vec);
2668         }
2669
2670         /* Mass-weighting */
2671         wi = N_M*erg->m_loc[j];
2672
2673         /* Store the additional force so that it can be added to the force
2674          * array after the normal forces have been evaluated */
2675         k_wi = erg->rotg->k*wi;
2676         for (int m = 0; m < DIM; m++)
2677         {
2678             tmp_f[m]             = k_wi*dr[m];
2679             erg->f_rot_loc[j][m] = tmp_f[m];
2680             erg->V              += 0.5*k_wi*gmx::square(dr[m]);
2681         }
2682
2683         /* If requested, also calculate the potential for a set of angles
2684          * near the current reference angle */
2685         if (bCalcPotFit)
2686         {
2687             for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2688             {
2689                 /* Index of this rotation group atom with respect to the whole rotation group */
2690                 int jj = collectiveRotationGroupIndex[j];
2691
2692                 /* Rotate with the alternative angle. Like rotate_local_reference(),
2693                  * just for a single local atom */
2694                 mvmul(erg->PotAngleFit->rotmat[ifit], erg->rotg->x_ref[jj], fit_xr_loc); /* fit_xr_loc = Omega*(y_i-y_c) */
2695
2696                 /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
2697                 rvec_sub(fit_xr_loc, xi_xc, dr);
2698
2699                 if (bProject)
2700                 {
2701                     project_onto_plane(dr, erg->vec);
2702                 }
2703
2704                 /* Add to the rotation potential for this angle: */
2705                 erg->PotAngleFit->V[ifit] += 0.5*k_wi*norm2(dr);
2706             }
2707         }
2708
2709         if (bOutstepRot)
2710         {
2711             /* Add to the torque of this rotation group */
2712             erg->torque_v += torque(erg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
2713
2714             /* Calculate the angle between reference and actual rotation group atom. */
2715             angle(erg, xi_xc, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
2716             erg->angle_v  += alpha * weight;
2717             erg->weight_v += weight;
2718         }
2719         /* If you want enforced rotation to contribute to the virial,
2720          * activate the following lines:
2721             if (MASTER(cr))
2722             {
2723                Add the rotation contribution to the virial
2724               for(j=0; j<DIM; j++)
2725                 for(m=0;m<DIM;m++)
2726                   vir[j][m] += 0.5*f[ii][j]*dr[m];
2727             }
2728          */
2729
2730         PRINT_FORCE_J
2731
2732     } /* end of loop over local rotation group atoms */
2733 }
2734
2735
2736 /* Calculate the radial motion potential and forces */
2737 static void do_radial_motion(
2738         gmx_enfrotgrp  *erg,
2739         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
2740         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
2741 {
2742     rvec            tmp_f;            /* Force */
2743     real            alpha;            /* a single angle between an actual and a reference position */
2744     real            weight;           /* single weight for a single angle */
2745     rvec            xj_u;             /* xj - u */
2746     rvec            tmpvec, fit_tmpvec;
2747     real            fac, fac2, sum = 0.0;
2748     rvec            pj;
2749     gmx_bool        bCalcPotFit;
2750
2751     /* For mass weighting: */
2752     real      wj;              /* Mass-weighting of the positions */
2753     real      N_M;             /* N/M */
2754
2755     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
2756
2757     N_M = erg->rotg->nat * erg->invmass;
2758     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
2759     /* Each process calculates the forces on its local atoms */
2760     for (size_t j = 0; j < erg->atomSet->numAtomsLocal(); j++)
2761     {
2762         /* Calculate (xj-u) */
2763         rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u);  /* xj_u = xj-u */
2764
2765         /* Calculate Omega.(yj0-u) */
2766         cprod(erg->vec, erg->xr_loc[j], tmpvec);  /* tmpvec = v x Omega.(yj0-u) */
2767
2768         /*                       *         v x Omega.(yj0-u)     */
2769         unitv(tmpvec, pj);      /*  pj = ---------------------   */
2770                                 /*       | v x Omega.(yj0-u) |   */
2771
2772         fac  = iprod(pj, xj_u); /* fac = pj.(xj-u) */
2773         fac2 = fac*fac;
2774
2775         /* Mass-weighting */
2776         wj = N_M*erg->m_loc[j];
2777
2778         /* Store the additional force so that it can be added to the force
2779          * array after the normal forces have been evaluated */
2780         svmul(-erg->rotg->k*wj*fac, pj, tmp_f);
2781         copy_rvec(tmp_f, erg->f_rot_loc[j]);
2782         sum += wj*fac2;
2783
2784         /* If requested, also calculate the potential for a set of angles
2785          * near the current reference angle */
2786         if (bCalcPotFit)
2787         {
2788             for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2789             {
2790                 /* Index of this rotation group atom with respect to the whole rotation group */
2791                 int jj = collectiveRotationGroupIndex[j];
2792
2793                 /* Rotate with the alternative angle. Like rotate_local_reference(),
2794                  * just for a single local atom */
2795                 mvmul(erg->PotAngleFit->rotmat[ifit], erg->rotg->x_ref[jj], fit_tmpvec); /* fit_tmpvec = Omega*(yj0-u) */
2796
2797                 /* Calculate Omega.(yj0-u) */
2798                 cprod(erg->vec, fit_tmpvec, tmpvec); /* tmpvec = v x Omega.(yj0-u) */
2799                 /*                                     *         v x Omega.(yj0-u)     */
2800                 unitv(tmpvec, pj);                   /*  pj = ---------------------   */
2801                                                      /*       | v x Omega.(yj0-u) |   */
2802
2803                 fac  = iprod(pj, xj_u);              /* fac = pj.(xj-u) */
2804                 fac2 = fac*fac;
2805
2806                 /* Add to the rotation potential for this angle: */
2807                 erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*fac2;
2808             }
2809         }
2810
2811         if (bOutstepRot)
2812         {
2813             /* Add to the torque of this rotation group */
2814             erg->torque_v += torque(erg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
2815
2816             /* Calculate the angle between reference and actual rotation group atom. */
2817             angle(erg, xj_u, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
2818             erg->angle_v  += alpha * weight;
2819             erg->weight_v += weight;
2820         }
2821
2822         PRINT_FORCE_J
2823
2824     } /* end of loop over local rotation group atoms */
2825     erg->V = 0.5*erg->rotg->k*sum;
2826 }
2827
2828
2829 /* Calculate the radial motion pivot-free potential and forces */
2830 static void do_radial_motion_pf(
2831         gmx_enfrotgrp  *erg,
2832         rvec            x[],          /* The positions                              */
2833         matrix          box,          /* The simulation box                         */
2834         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
2835         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
2836 {
2837     rvec            xj;               /* Current position */
2838     rvec            xj_xc;            /* xj  - xc  */
2839     rvec            yj0_yc0;          /* yj0 - yc0 */
2840     rvec            tmp_f;            /* Force */
2841     real            alpha;            /* a single angle between an actual and a reference position */
2842     real            weight;           /* single weight for a single angle */
2843     rvec            tmpvec, tmpvec2;
2844     rvec            innersumvec;      /* Precalculation of the inner sum */
2845     rvec            innersumveckM;
2846     real            fac, fac2, V = 0.0;
2847     rvec            qi, qj;
2848     gmx_bool        bCalcPotFit;
2849
2850     /* For mass weighting: */
2851     real      mj, wi, wj;      /* Mass-weighting of the positions */
2852     real      N_M;             /* N/M */
2853
2854     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
2855
2856     N_M = erg->rotg->nat * erg->invmass;
2857
2858     /* Get the current center of the rotation group: */
2859     get_center(erg->xc, erg->mc, erg->rotg->nat, erg->xc_center);
2860
2861     /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
2862     clear_rvec(innersumvec);
2863     for (int i = 0; i < erg->rotg->nat; i++)
2864     {
2865         /* Mass-weighting */
2866         wi = N_M*erg->mc[i];
2867
2868         /* Calculate qi. Note that xc_ref_center has already been subtracted from
2869          * x_ref in init_rot_group.*/
2870         mvmul(erg->rotmat, erg->rotg->x_ref[i], tmpvec); /* tmpvec  = Omega.(yi0-yc0) */
2871
2872         cprod(erg->vec, tmpvec, tmpvec2);                /* tmpvec2 = v x Omega.(yi0-yc0) */
2873
2874         /*                                             *         v x Omega.(yi0-yc0)     */
2875         unitv(tmpvec2, qi);                           /*  qi = -----------------------   */
2876                                                       /*       | v x Omega.(yi0-yc0) |   */
2877
2878         rvec_sub(erg->xc[i], erg->xc_center, tmpvec); /* tmpvec = xi-xc */
2879
2880         svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
2881
2882         rvec_inc(innersumvec, tmpvec2);
2883     }
2884     svmul(erg->rotg->k*erg->invmass, innersumvec, innersumveckM);
2885
2886     /* Each process calculates the forces on its local atoms */
2887     const auto &localRotationGroupIndex      = erg->atomSet->localIndex();
2888     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
2889     for (gmx::index j = 0; j < localRotationGroupIndex.size(); j++)
2890     {
2891         /* Local index of a rotation group atom  */
2892         int ii = localRotationGroupIndex[j];
2893         /* Position of this atom in the collective array */
2894         int iigrp = collectiveRotationGroupIndex[j];
2895         /* Mass-weighting */
2896         mj = erg->mc[iigrp];  /* need the unsorted mass here */
2897         wj = N_M*mj;
2898
2899         /* Current position of this atom: x[ii][XX/YY/ZZ] */
2900         copy_rvec(x[ii], xj);
2901
2902         /* Shift this atom such that it is near its reference */
2903         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
2904
2905         /* The (unrotated) reference position is yj0. yc0 has already
2906          * been subtracted in init_rot_group */
2907         copy_rvec(erg->rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0      */
2908
2909         /* Calculate Omega.(yj0-yc0) */
2910         mvmul(erg->rotmat, yj0_yc0, tmpvec2); /* tmpvec2 = Omega.(yj0 - yc0)  */
2911
2912         cprod(erg->vec, tmpvec2, tmpvec);     /* tmpvec = v x Omega.(yj0-yc0) */
2913
2914         /*                     *         v x Omega.(yj0-yc0)     */
2915         unitv(tmpvec, qj);    /*  qj = -----------------------   */
2916                               /*       | v x Omega.(yj0-yc0) |   */
2917
2918         /* Calculate (xj-xc) */
2919         rvec_sub(xj, erg->xc_center, xj_xc); /* xj_xc = xj-xc */
2920
2921         fac  = iprod(qj, xj_xc);             /* fac = qj.(xj-xc) */
2922         fac2 = fac*fac;
2923
2924         /* Store the additional force so that it can be added to the force
2925          * array after the normal forces have been evaluated */
2926         svmul(-erg->rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
2927         svmul(mj, innersumveckM, tmpvec);       /* part 2 of force */
2928         rvec_inc(tmp_f, tmpvec);
2929         copy_rvec(tmp_f, erg->f_rot_loc[j]);
2930         V += wj*fac2;
2931
2932         /* If requested, also calculate the potential for a set of angles
2933          * near the current reference angle */
2934         if (bCalcPotFit)
2935         {
2936             for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2937             {
2938                 /* Rotate with the alternative angle. Like rotate_local_reference(),
2939                  * just for a single local atom */
2940                 mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, tmpvec2); /* tmpvec2 = Omega*(yj0-yc0) */
2941
2942                 /* Calculate Omega.(yj0-u) */
2943                 cprod(erg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(yj0-yc0) */
2944                 /*                                  *         v x Omega.(yj0-yc0)     */
2945                 unitv(tmpvec, qj);                /*  qj = -----------------------   */
2946                                                   /*       | v x Omega.(yj0-yc0) |   */
2947
2948                 fac  = iprod(qj, xj_xc);          /* fac = qj.(xj-xc) */
2949                 fac2 = fac*fac;
2950
2951                 /* Add to the rotation potential for this angle: */
2952                 erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*fac2;
2953             }
2954         }
2955
2956         if (bOutstepRot)
2957         {
2958             /* Add to the torque of this rotation group */
2959             erg->torque_v += torque(erg->vec, tmp_f, xj, erg->xc_center);
2960
2961             /* Calculate the angle between reference and actual rotation group atom. */
2962             angle(erg, xj_xc, yj0_yc0, &alpha, &weight);  /* angle in rad, weighted */
2963             erg->angle_v  += alpha * weight;
2964             erg->weight_v += weight;
2965         }
2966
2967         PRINT_FORCE_J
2968
2969     } /* end of loop over local rotation group atoms */
2970     erg->V = 0.5*erg->rotg->k*V;
2971 }
2972
2973
2974 /* Precalculate the inner sum for the radial motion 2 forces */
2975 static void radial_motion2_precalc_inner_sum(const gmx_enfrotgrp *erg,
2976                                              rvec                 innersumvec)
2977 {
2978     int             i;
2979     rvec            xi_xc;     /* xj - xc */
2980     rvec            tmpvec, tmpvec2;
2981     real            fac;
2982     rvec            ri, si;
2983     real            siri;
2984     rvec            v_xi_xc;   /* v x (xj - u) */
2985     real            psii, psiistar;
2986     real            wi;        /* Mass-weighting of the positions */
2987     real            N_M;       /* N/M */
2988     rvec            sumvec;
2989
2990     N_M = erg->rotg->nat * erg->invmass;
2991
2992     /* Loop over the collective set of positions */
2993     clear_rvec(sumvec);
2994     for (i = 0; i < erg->rotg->nat; i++)
2995     {
2996         /* Mass-weighting */
2997         wi = N_M*erg->mc[i];
2998
2999         rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc         */
3000
3001         /* Calculate ri. Note that xc_ref_center has already been subtracted from
3002          * x_ref in init_rot_group.*/
3003         mvmul(erg->rotmat, erg->rotg->x_ref[i], ri); /* ri  = Omega.(yi0-yc0) */
3004
3005         cprod(erg->vec, xi_xc, v_xi_xc);             /* v_xi_xc = v x (xi-u)  */
3006
3007         fac = norm2(v_xi_xc);
3008         /*                                 *                      1           */
3009         psiistar = 1.0/(fac + erg->rotg->eps); /* psiistar = --------------------- */
3010         /*            |v x (xi-xc)|^2 + eps */
3011
3012         psii = gmx::invsqrt(fac);         /*                 1                */
3013                                           /*  psii    = -------------         */
3014                                           /*            |v x (xi-xc)|         */
3015
3016         svmul(psii, v_xi_xc, si);         /*  si = psii * (v x (xi-xc) )     */
3017
3018         siri = iprod(si, ri);             /* siri = si.ri           */
3019
3020         svmul(psiistar/psii, ri, tmpvec);
3021         svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
3022         rvec_dec(tmpvec, tmpvec2);
3023         cprod(tmpvec, erg->vec, tmpvec2);
3024
3025         svmul(wi*siri, tmpvec2, tmpvec);
3026
3027         rvec_inc(sumvec, tmpvec);
3028     }
3029     svmul(erg->rotg->k*erg->invmass, sumvec, innersumvec);
3030 }
3031
3032
3033 /* Calculate the radial motion 2 potential and forces */
3034 static void do_radial_motion2(
3035         gmx_enfrotgrp  *erg,
3036         rvec            x[],          /* The positions                              */
3037         matrix          box,          /* The simulation box                         */
3038         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
3039         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
3040 {
3041     rvec            xj;               /* Position */
3042     real            alpha;            /* a single angle between an actual and a reference position */
3043     real            weight;           /* single weight for a single angle */
3044     rvec            xj_u;             /* xj - u */
3045     rvec            yj0_yc0;          /* yj0 -yc0 */
3046     rvec            tmpvec, tmpvec2;
3047     real            fac, fit_fac, fac2, Vpart = 0.0;
3048     rvec            rj, fit_rj, sj;
3049     real            sjrj;
3050     rvec            v_xj_u;    /* v x (xj - u) */
3051     real            psij, psijstar;
3052     real            mj, wj;    /* For mass-weighting of the positions */
3053     real            N_M;       /* N/M */
3054     gmx_bool        bPF;
3055     rvec            innersumvec;
3056     gmx_bool        bCalcPotFit;
3057
3058     bPF         = erg->rotg->eType == erotgRM2PF;
3059     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
3060
3061     clear_rvec(yj0_yc0); /* Make the compiler happy */
3062
3063     clear_rvec(innersumvec);
3064     if (bPF)
3065     {
3066         /* For the pivot-free variant we have to use the current center of
3067          * mass of the rotation group instead of the pivot u */
3068         get_center(erg->xc, erg->mc, erg->rotg->nat, erg->xc_center);
3069
3070         /* Also, we precalculate the second term of the forces that is identical
3071          * (up to the weight factor mj) for all forces */
3072         radial_motion2_precalc_inner_sum(erg, innersumvec);
3073     }
3074
3075     N_M = erg->rotg->nat * erg->invmass;
3076
3077     /* Each process calculates the forces on its local atoms */
3078     const auto &localRotationGroupIndex      = erg->atomSet->localIndex();
3079     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
3080     for (gmx::index j = 0; j < localRotationGroupIndex.size(); j++)
3081     {
3082         if (bPF)
3083         {
3084             /* Local index of a rotation group atom  */
3085             int ii = localRotationGroupIndex[j];
3086             /* Position of this atom in the collective array */
3087             int iigrp = collectiveRotationGroupIndex[j];
3088             /* Mass-weighting */
3089             mj = erg->mc[iigrp];
3090
3091             /* Current position of this atom: x[ii] */
3092             copy_rvec(x[ii], xj);
3093
3094             /* Shift this atom such that it is near its reference */
3095             shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
3096
3097             /* The (unrotated) reference position is yj0. yc0 has already
3098              * been subtracted in init_rot_group */
3099             copy_rvec(erg->rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0  */
3100
3101             /* Calculate Omega.(yj0-yc0) */
3102             mvmul(erg->rotmat, yj0_yc0, rj);         /* rj = Omega.(yj0-yc0)  */
3103         }
3104         else
3105         {
3106             mj = erg->m_loc[j];
3107             copy_rvec(erg->x_loc_pbc[j], xj);
3108             copy_rvec(erg->xr_loc[j], rj);           /* rj = Omega.(yj0-u)    */
3109         }
3110         /* Mass-weighting */
3111         wj = N_M*mj;
3112
3113         /* Calculate (xj-u) resp. (xj-xc) */
3114         rvec_sub(xj, erg->xc_center, xj_u);         /* xj_u = xj-u           */
3115
3116         cprod(erg->vec, xj_u, v_xj_u);              /* v_xj_u = v x (xj-u)   */
3117
3118         fac = norm2(v_xj_u);
3119         /*                                      *                      1           */
3120         psijstar = 1.0/(fac + erg->rotg->eps); /*  psistar = --------------------  */
3121         /*                                      *            |v x (xj-u)|^2 + eps  */
3122
3123         psij = gmx::invsqrt(fac);         /*                 1                */
3124                                           /*  psij    = ------------          */
3125                                           /*            |v x (xj-u)|          */
3126
3127         svmul(psij, v_xj_u, sj);          /*  sj = psij * (v x (xj-u) )       */
3128
3129         fac  = iprod(v_xj_u, rj);         /* fac = (v x (xj-u)).rj */
3130         fac2 = fac*fac;
3131
3132         sjrj = iprod(sj, rj);                        /* sjrj = sj.rj          */
3133
3134         svmul(psijstar/psij, rj, tmpvec);
3135         svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
3136         rvec_dec(tmpvec, tmpvec2);
3137         cprod(tmpvec, erg->vec, tmpvec2);
3138
3139         /* Store the additional force so that it can be added to the force
3140          * array after the normal forces have been evaluated */
3141         svmul(-erg->rotg->k*wj*sjrj, tmpvec2, tmpvec);
3142         svmul(mj, innersumvec, tmpvec2);  /* This is != 0 only for the pivot-free variant */
3143
3144         rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
3145         Vpart += wj*psijstar*fac2;
3146
3147         /* If requested, also calculate the potential for a set of angles
3148          * near the current reference angle */
3149         if (bCalcPotFit)
3150         {
3151             for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
3152             {
3153                 if (bPF)
3154                 {
3155                     mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, fit_rj); /* fit_rj = Omega.(yj0-yc0) */
3156                 }
3157                 else
3158                 {
3159                     /* Position of this atom in the collective array */
3160                     int iigrp = collectiveRotationGroupIndex[j];
3161                     /* Rotate with the alternative angle. Like rotate_local_reference(),
3162                      * just for a single local atom */
3163                     mvmul(erg->PotAngleFit->rotmat[ifit], erg->rotg->x_ref[iigrp], fit_rj); /* fit_rj = Omega*(yj0-u) */
3164                 }
3165                 fit_fac = iprod(v_xj_u, fit_rj);                                            /* fac = (v x (xj-u)).fit_rj */
3166                 /* Add to the rotation potential for this angle: */
3167                 erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*psijstar*fit_fac*fit_fac;
3168             }
3169         }
3170
3171         if (bOutstepRot)
3172         {
3173             /* Add to the torque of this rotation group */
3174             erg->torque_v += torque(erg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
3175
3176             /* Calculate the angle between reference and actual rotation group atom. */
3177             angle(erg, xj_u, rj, &alpha, &weight);  /* angle in rad, weighted */
3178             erg->angle_v  += alpha * weight;
3179             erg->weight_v += weight;
3180         }
3181
3182         PRINT_FORCE_J
3183
3184     } /* end of loop over local rotation group atoms */
3185     erg->V = 0.5*erg->rotg->k*Vpart;
3186 }
3187
3188
3189 /* Determine the smallest and largest position vector (with respect to the
3190  * rotation vector) for the reference group */
3191 static void get_firstlast_atom_ref(
3192         const gmx_enfrotgrp *erg,
3193         int                 *firstindex,
3194         int                 *lastindex)
3195 {
3196     int             i;
3197     real            xcproj;           /* The projection of a reference position on the
3198                                          rotation vector */
3199     real            minproj, maxproj; /* Smallest and largest projection on v */
3200
3201     /* Start with some value */
3202     minproj = iprod(erg->rotg->x_ref[0], erg->vec);
3203     maxproj = minproj;
3204
3205     /* This is just to ensure that it still works if all the atoms of the
3206      * reference structure are situated in a plane perpendicular to the rotation
3207      * vector */
3208     *firstindex = 0;
3209     *lastindex  = erg->rotg->nat-1;
3210
3211     /* Loop over all atoms of the reference group,
3212      * project them on the rotation vector to find the extremes */
3213     for (i = 0; i < erg->rotg->nat; i++)
3214     {
3215         xcproj = iprod(erg->rotg->x_ref[i], erg->vec);
3216         if (xcproj < minproj)
3217         {
3218             minproj     = xcproj;
3219             *firstindex = i;
3220         }
3221         if (xcproj > maxproj)
3222         {
3223             maxproj    = xcproj;
3224             *lastindex = i;
3225         }
3226     }
3227 }
3228
3229
3230 /* Allocate memory for the slabs */
3231 static void allocate_slabs(
3232         gmx_enfrotgrp *erg,
3233         FILE          *fplog,
3234         gmx_bool       bVerbose)
3235 {
3236     /* More slabs than are defined for the reference are never needed */
3237     int nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
3238
3239     /* Remember how many we allocated */
3240     erg->nslabs_alloc = nslabs;
3241
3242     if ( (nullptr != fplog) && bVerbose)
3243     {
3244         fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
3245                 RotStr, nslabs, erg->groupIndex);
3246     }
3247     snew(erg->slab_center, nslabs);
3248     snew(erg->slab_center_ref, nslabs);
3249     snew(erg->slab_weights, nslabs);
3250     snew(erg->slab_torque_v, nslabs);
3251     snew(erg->slab_data, nslabs);
3252     snew(erg->gn_atom, nslabs);
3253     snew(erg->gn_slabind, nslabs);
3254     snew(erg->slab_innersumvec, nslabs);
3255     for (int i = 0; i < nslabs; i++)
3256     {
3257         snew(erg->slab_data[i].x, erg->rotg->nat);
3258         snew(erg->slab_data[i].ref, erg->rotg->nat);
3259         snew(erg->slab_data[i].weight, erg->rotg->nat);
3260     }
3261     snew(erg->xc_ref_sorted, erg->rotg->nat);
3262     snew(erg->xc_sortind, erg->rotg->nat);
3263     snew(erg->firstatom, nslabs);
3264     snew(erg->lastatom, nslabs);
3265 }
3266
3267
3268 /* From the extreme positions of the reference group, determine the first
3269  * and last slab of the reference. We can never have more slabs in the real
3270  * simulation than calculated here for the reference.
3271  */
3272 static void get_firstlast_slab_ref(gmx_enfrotgrp *erg,
3273                                    real mc[], int ref_firstindex, int ref_lastindex)
3274 {
3275     rvec dummy;
3276
3277     int  first      = get_first_slab(erg, erg->rotg->x_ref[ref_firstindex]);
3278     int  last       = get_last_slab(erg, erg->rotg->x_ref[ref_lastindex ]);
3279
3280     while (get_slab_weight(first, erg, erg->rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
3281     {
3282         first--;
3283     }
3284     erg->slab_first_ref = first+1;
3285     while (get_slab_weight(last, erg, erg->rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
3286     {
3287         last++;
3288     }
3289     erg->slab_last_ref  = last-1;
3290 }
3291
3292
3293 /* Special version of copy_rvec:
3294  * During the copy procedure of xcurr to b, the correct PBC image is chosen
3295  * such that the copied vector ends up near its reference position xref */
3296 static inline void copy_correct_pbc_image(
3297         const rvec   xcurr,  /* copy vector xcurr ...                */
3298         rvec         b,      /* ... to b ...                         */
3299         const rvec   xref,   /* choosing the PBC image such that b ends up near xref */
3300         const matrix box,
3301         int          npbcdim)
3302 {
3303     rvec  dx;
3304     int   d, m;
3305     ivec  shift;
3306
3307
3308     /* Shortest PBC distance between the atom and its reference */
3309     rvec_sub(xcurr, xref, dx);
3310
3311     /* Determine the shift for this atom */
3312     clear_ivec(shift);
3313     for (m = npbcdim-1; m >= 0; m--)
3314     {
3315         while (dx[m] < -0.5*box[m][m])
3316         {
3317             for (d = 0; d < DIM; d++)
3318             {
3319                 dx[d] += box[m][d];
3320             }
3321             shift[m]++;
3322         }
3323         while (dx[m] >= 0.5*box[m][m])
3324         {
3325             for (d = 0; d < DIM; d++)
3326             {
3327                 dx[d] -= box[m][d];
3328             }
3329             shift[m]--;
3330         }
3331     }
3332
3333     /* Apply the shift to the position */
3334     copy_rvec(xcurr, b);
3335     shift_single_coord(box, b, shift);
3336 }
3337
3338
3339 static void init_rot_group(FILE *fplog, const t_commrec *cr,
3340                            gmx_enfrotgrp *erg,
3341                            rvec *x, gmx_mtop_t *mtop, gmx_bool bVerbose, FILE *out_slabs, const matrix box,
3342                            t_inputrec *ir, gmx_bool bOutputCenters)
3343 {
3344     rvec            coord, xref, *xdum;
3345     gmx_bool        bFlex, bColl;
3346     int             ref_firstindex, ref_lastindex;
3347     real            mass, totalmass;
3348     real            start = 0.0;
3349     double          t_start;
3350     const t_rotgrp *rotg = erg->rotg;
3351
3352
3353     /* Do we have a flexible axis? */
3354     bFlex = ISFLEX(rotg);
3355     /* Do we use a global set of coordinates? */
3356     bColl = ISCOLL(rotg);
3357
3358     /* Allocate space for collective coordinates if needed */
3359     if (bColl)
3360     {
3361         snew(erg->xc, erg->rotg->nat);
3362         snew(erg->xc_shifts, erg->rotg->nat);
3363         snew(erg->xc_eshifts, erg->rotg->nat);
3364         snew(erg->xc_old, erg->rotg->nat);
3365
3366         if (erg->rotg->eFittype == erotgFitNORM)
3367         {
3368             snew(erg->xc_ref_length, erg->rotg->nat); /* in case fit type NORM is chosen */
3369             snew(erg->xc_norm, erg->rotg->nat);
3370         }
3371     }
3372     else
3373     {
3374         snew(erg->xr_loc, erg->rotg->nat);
3375         snew(erg->x_loc_pbc, erg->rotg->nat);
3376     }
3377
3378     copy_rvec(erg->rotg->inputVec, erg->vec);
3379     snew(erg->f_rot_loc, erg->rotg->nat);
3380
3381     /* Make space for the calculation of the potential at other angles (used
3382      * for fitting only) */
3383     if (erotgFitPOT == erg->rotg->eFittype)
3384     {
3385         snew(erg->PotAngleFit, 1);
3386         snew(erg->PotAngleFit->degangle, erg->rotg->PotAngle_nstep);
3387         snew(erg->PotAngleFit->V, erg->rotg->PotAngle_nstep);
3388         snew(erg->PotAngleFit->rotmat, erg->rotg->PotAngle_nstep);
3389
3390         /* Get the set of angles around the reference angle */
3391         start = -0.5 * (erg->rotg->PotAngle_nstep - 1)*erg->rotg->PotAngle_step;
3392         for (int i = 0; i < erg->rotg->PotAngle_nstep; i++)
3393         {
3394             erg->PotAngleFit->degangle[i] = start + i*erg->rotg->PotAngle_step;
3395         }
3396     }
3397     else
3398     {
3399         erg->PotAngleFit = nullptr;
3400     }
3401
3402     /* Copy the masses so that the center can be determined. For all types of
3403      * enforced rotation, we store the masses in the erg->mc array. */
3404     snew(erg->mc, erg->rotg->nat);
3405     if (bFlex)
3406     {
3407         snew(erg->mc_sorted, erg->rotg->nat);
3408     }
3409     if (!bColl)
3410     {
3411         snew(erg->m_loc, erg->rotg->nat);
3412     }
3413     totalmass = 0.0;
3414     int molb  = 0;
3415     for (int i = 0; i < erg->rotg->nat; i++)
3416     {
3417         if (erg->rotg->bMassW)
3418         {
3419             mass = mtopGetAtomMass(mtop, erg->rotg->ind[i], &molb);
3420         }
3421         else
3422         {
3423             mass = 1.0;
3424         }
3425         erg->mc[i] = mass;
3426         totalmass += mass;
3427     }
3428     erg->invmass = 1.0/totalmass;
3429
3430     /* Set xc_ref_center for any rotation potential */
3431     if ((erg->rotg->eType == erotgISO) || (erg->rotg->eType == erotgPM) || (erg->rotg->eType == erotgRM) || (erg->rotg->eType == erotgRM2))
3432     {
3433         /* Set the pivot point for the fixed, stationary-axis potentials. This
3434          * won't change during the simulation */
3435         copy_rvec(erg->rotg->pivot, erg->xc_ref_center);
3436         copy_rvec(erg->rotg->pivot, erg->xc_center    );
3437     }
3438     else
3439     {
3440         /* Center of the reference positions */
3441         get_center(erg->rotg->x_ref, erg->mc, erg->rotg->nat, erg->xc_ref_center);
3442
3443         /* Center of the actual positions */
3444         if (MASTER(cr))
3445         {
3446             snew(xdum, erg->rotg->nat);
3447             for (int i = 0; i < erg->rotg->nat; i++)
3448             {
3449                 int ii = erg->rotg->ind[i];
3450                 copy_rvec(x[ii], xdum[i]);
3451             }
3452             get_center(xdum, erg->mc, erg->rotg->nat, erg->xc_center);
3453             sfree(xdum);
3454         }
3455 #if GMX_MPI
3456         if (PAR(cr))
3457         {
3458             gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
3459         }
3460 #endif
3461     }
3462
3463     if (bColl)
3464     {
3465         /* Save the original (whole) set of positions in xc_old such that at later
3466          * steps the rotation group can always be made whole again. If the simulation is
3467          * restarted, we compute the starting reference positions (given the time)
3468          * and assume that the correct PBC image of each position is the one nearest
3469          * to the current reference */
3470         if (MASTER(cr))
3471         {
3472             /* Calculate the rotation matrix for this angle: */
3473             t_start       = ir->init_t + ir->init_step*ir->delta_t;
3474             erg->degangle = erg->rotg->rate * t_start;
3475             calc_rotmat(erg->vec, erg->degangle, erg->rotmat);
3476
3477             for (int i = 0; i < erg->rotg->nat; i++)
3478             {
3479                 int ii = erg->rotg->ind[i];
3480
3481                 /* Subtract pivot, rotate, and add pivot again. This will yield the
3482                  * reference position for time t */
3483                 rvec_sub(erg->rotg->x_ref[i], erg->xc_ref_center, coord);
3484                 mvmul(erg->rotmat, coord, xref);
3485                 rvec_inc(xref, erg->xc_ref_center);
3486
3487                 copy_correct_pbc_image(x[ii], erg->xc_old[i], xref, box, 3);
3488             }
3489         }
3490 #if GMX_MPI
3491         if (PAR(cr))
3492         {
3493             gmx_bcast(erg->rotg->nat*sizeof(erg->xc_old[0]), erg->xc_old, cr);
3494         }
3495 #endif
3496     }
3497
3498     if ( (erg->rotg->eType != erotgFLEX) && (erg->rotg->eType != erotgFLEX2) )
3499     {
3500         /* Put the reference positions into origin: */
3501         for (int i = 0; i < erg->rotg->nat; i++)
3502         {
3503             rvec_dec(erg->rotg->x_ref[i], erg->xc_ref_center);
3504         }
3505     }
3506
3507     /* Enforced rotation with flexible axis */
3508     if (bFlex)
3509     {
3510         /* Calculate maximum beta value from minimum gaussian (performance opt.) */
3511         erg->max_beta = calc_beta_max(erg->rotg->min_gaussian, erg->rotg->slab_dist);
3512
3513         /* Determine the smallest and largest coordinate with respect to the rotation vector */
3514         get_firstlast_atom_ref(erg, &ref_firstindex, &ref_lastindex);
3515
3516         /* From the extreme positions of the reference group, determine the first
3517          * and last slab of the reference. */
3518         get_firstlast_slab_ref(erg, erg->mc, ref_firstindex, ref_lastindex);
3519
3520         /* Allocate memory for the slabs */
3521         allocate_slabs(erg, fplog, bVerbose);
3522
3523         /* Flexible rotation: determine the reference centers for the rest of the simulation */
3524         erg->slab_first = erg->slab_first_ref;
3525         erg->slab_last  = erg->slab_last_ref;
3526         get_slab_centers(erg, erg->rotg->x_ref, erg->mc, -1, out_slabs, bOutputCenters, TRUE);
3527
3528         /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
3529         if (erg->rotg->eFittype == erotgFitNORM)
3530         {
3531             for (int i = 0; i < erg->rotg->nat; i++)
3532             {
3533                 rvec_sub(erg->rotg->x_ref[i], erg->xc_ref_center, coord);
3534                 erg->xc_ref_length[i] = norm(coord);
3535             }
3536         }
3537     }
3538 }
3539
3540 /* Calculate the size of the MPI buffer needed in reduce_output() */
3541 static int calc_mpi_bufsize(const gmx_enfrot *er)
3542
3543 {
3544     int count_total = 0;
3545     for (int g = 0; g < er->rot->ngrp; g++)
3546     {
3547         const t_rotgrp      *rotg = &er->rot->grp[g];
3548         const gmx_enfrotgrp *erg  = &er->enfrotgrp[g];
3549
3550         /* Count the items that are transferred for this group: */
3551         int count_group = 4; /* V, torque, angle, weight */
3552
3553         /* Add the maximum number of slabs for flexible groups */
3554         if (ISFLEX(rotg))
3555         {
3556             count_group += erg->slab_last_ref - erg->slab_first_ref + 1;
3557         }
3558
3559         /* Add space for the potentials at different angles: */
3560         if (erotgFitPOT == erg->rotg->eFittype)
3561         {
3562             count_group += erg->rotg->PotAngle_nstep;
3563         }
3564
3565         /* Add to the total number: */
3566         count_total += count_group;
3567     }
3568
3569     return count_total;
3570 }
3571
3572
3573 std::unique_ptr<gmx::EnforcedRotation>
3574 init_rot(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
3575          const t_commrec *cr, gmx::LocalAtomSetManager * atomSets, const t_state *globalState, gmx_mtop_t *mtop, const gmx_output_env_t *oenv,
3576          const MdrunOptions &mdrunOptions)
3577 {
3578     int             nat_max = 0;       /* Size of biggest rotation group */
3579     rvec           *x_pbc   = nullptr; /* Space for the pbc-correct atom positions */
3580
3581     if (MASTER(cr) && mdrunOptions.verbose)
3582     {
3583         fprintf(stdout, "%s Initializing ...\n", RotStr);
3584     }
3585
3586     auto        enforcedRotation = gmx::compat::make_unique<gmx::EnforcedRotation>();
3587     gmx_enfrot *er               = enforcedRotation->getLegacyEnfrot();
3588     // TODO When this module implements IMdpOptions, the ownership will become more clear.
3589     er->rot         = ir->rot;
3590     er->appendFiles = mdrunOptions.continuationOptions.appendFiles;
3591
3592     /* When appending, skip first output to avoid duplicate entries in the data files */
3593     if (er->appendFiles)
3594     {
3595         er->bOut = FALSE;
3596     }
3597     else
3598     {
3599         er->bOut = TRUE;
3600     }
3601
3602     if (MASTER(cr) && er->bOut)
3603     {
3604         please_cite(fplog, "Kutzner2011");
3605     }
3606
3607     /* Output every step for reruns */
3608     if (mdrunOptions.rerun)
3609     {
3610         if (nullptr != fplog)
3611         {
3612             fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
3613         }
3614         er->nstrout = 1;
3615         er->nstsout = 1;
3616     }
3617     else
3618     {
3619         er->nstrout = er->rot->nstrout;
3620         er->nstsout = er->rot->nstsout;
3621     }
3622
3623     er->out_slabs = nullptr;
3624     if (MASTER(cr) && HaveFlexibleGroups(er->rot) )
3625     {
3626         er->out_slabs = open_slab_out(opt2fn("-rs", nfile, fnm), er);
3627     }
3628
3629     if (MASTER(cr))
3630     {
3631         /* Remove pbc, make molecule whole.
3632          * When ir->bContinuation=TRUE this has already been done, but ok. */
3633         snew(x_pbc, mtop->natoms);
3634         copy_rvecn(as_rvec_array(globalState->x.data()), x_pbc, 0, mtop->natoms);
3635         do_pbc_first_mtop(nullptr, ir->ePBC, globalState->box, mtop, x_pbc);
3636         /* All molecules will be whole now, but not necessarily in the home box.
3637          * Additionally, if a rotation group consists of more than one molecule
3638          * (e.g. two strands of DNA), each one of them can end up in a different
3639          * periodic box. This is taken care of in init_rot_group.  */
3640     }
3641
3642     /* Allocate space for the per-rotation-group data: */
3643     er->enfrotgrp.resize(er->rot->ngrp);
3644     int groupIndex = 0;
3645     for (auto &ergRef : er->enfrotgrp)
3646     {
3647         gmx_enfrotgrp *erg = &ergRef;
3648         erg->rotg       = &er->rot->grp[groupIndex];
3649         erg->atomSet    = gmx::compat::make_unique<gmx::LocalAtomSet>(atomSets->add({erg->rotg->ind, erg->rotg->ind + erg->rotg->nat}));
3650         erg->groupIndex = groupIndex;
3651
3652         if (nullptr != fplog)
3653         {
3654             fprintf(fplog, "%s group %d type '%s'\n", RotStr, groupIndex, erotg_names[erg->rotg->eType]);
3655         }
3656
3657         if (erg->rotg->nat > 0)
3658         {
3659             nat_max = std::max(nat_max, erg->rotg->nat);
3660
3661             init_rot_group(fplog, cr, erg, x_pbc, mtop, mdrunOptions.verbose, er->out_slabs, MASTER(cr) ? globalState->box : nullptr, ir,
3662                            !er->appendFiles); /* Do not output the reference centers
3663                                                * again if we are appending */
3664         }
3665         ++groupIndex;
3666     }
3667
3668     /* Allocate space for enforced rotation buffer variables */
3669     er->bufsize = nat_max;
3670     snew(er->data, nat_max);
3671     snew(er->xbuf, nat_max);
3672     snew(er->mbuf, nat_max);
3673
3674     /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
3675     if (PAR(cr))
3676     {
3677         er->mpi_bufsize = calc_mpi_bufsize(er) + 100; /* larger to catch errors */
3678         snew(er->mpi_inbuf, er->mpi_bufsize);
3679         snew(er->mpi_outbuf, er->mpi_bufsize);
3680     }
3681     else
3682     {
3683         er->mpi_bufsize = 0;
3684         er->mpi_inbuf   = nullptr;
3685         er->mpi_outbuf  = nullptr;
3686     }
3687
3688     /* Only do I/O on the MASTER */
3689     er->out_angles  = nullptr;
3690     er->out_rot     = nullptr;
3691     er->out_torque  = nullptr;
3692     if (MASTER(cr))
3693     {
3694         er->out_rot = open_rot_out(opt2fn("-ro", nfile, fnm), oenv, er);
3695
3696         if (er->nstsout > 0)
3697         {
3698             if (HaveFlexibleGroups(er->rot) || HavePotFitGroups(er->rot) )
3699             {
3700                 er->out_angles  = open_angles_out(opt2fn("-ra", nfile, fnm), er);
3701             }
3702             if (HaveFlexibleGroups(er->rot) )
3703             {
3704                 er->out_torque  = open_torque_out(opt2fn("-rt", nfile, fnm), er);
3705             }
3706         }
3707
3708         sfree(x_pbc);
3709     }
3710     return enforcedRotation;
3711 }
3712
3713 /* Rotate the local reference positions and store them in
3714  * erg->xr_loc[0...(nat_loc-1)]
3715  *
3716  * Note that we already subtracted u or y_c from the reference positions
3717  * in init_rot_group().
3718  */
3719 static void rotate_local_reference(gmx_enfrotgrp *erg)
3720 {
3721     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
3722     for (size_t i = 0; i < erg->atomSet->numAtomsLocal(); i++)
3723     {
3724         /* Index of this rotation group atom with respect to the whole rotation group */
3725         int ii = collectiveRotationGroupIndex[i];
3726         /* Rotate */
3727         mvmul(erg->rotmat, erg->rotg->x_ref[ii], erg->xr_loc[i]);
3728     }
3729 }
3730
3731
3732 /* Select the PBC representation for each local x position and store that
3733  * for later usage. We assume the right PBC image of an x is the one nearest to
3734  * its rotated reference */
3735 static void choose_pbc_image(rvec x[],
3736                              gmx_enfrotgrp *erg,
3737                              matrix box, int npbcdim)
3738 {
3739     const auto &localRotationGroupIndex = erg->atomSet->localIndex();
3740     for (gmx::index i = 0; i < localRotationGroupIndex.size(); i++)
3741     {
3742         /* Index of a rotation group atom  */
3743         int ii = localRotationGroupIndex[i];
3744
3745         /* Get the correctly rotated reference position. The pivot was already
3746          * subtracted in init_rot_group() from the reference positions. Also,
3747          * the reference positions have already been rotated in
3748          * rotate_local_reference(). For the current reference position we thus
3749          * only need to add the pivot again. */
3750         rvec xref;
3751         copy_rvec(erg->xr_loc[i], xref);
3752         rvec_inc(xref, erg->xc_ref_center);
3753
3754         copy_correct_pbc_image(x[ii], erg->x_loc_pbc[i], xref, box, npbcdim);
3755     }
3756 }
3757
3758
3759 void do_rotation(const t_commrec       *cr,
3760                  gmx_enfrot            *er,
3761                  matrix                 box,
3762                  rvec                   x[],
3763                  real                   t,
3764                  int64_t                step,
3765                  gmx_bool               bNS)
3766 {
3767     gmx_bool        outstep_slab, outstep_rot;
3768     gmx_bool        bColl;
3769     rvec            transvec;
3770     gmx_potfit     *fit = nullptr; /* For fit type 'potential' determine the fit
3771                                         angle via the potential minimum            */
3772
3773 #ifdef TAKETIME
3774     double t0;
3775 #endif
3776
3777     /* When to output in main rotation output file */
3778     outstep_rot  = do_per_step(step, er->nstrout) && er->bOut;
3779     /* When to output per-slab data */
3780     outstep_slab = do_per_step(step, er->nstsout) && er->bOut;
3781
3782     /* Output time into rotation output file */
3783     if (outstep_rot && MASTER(cr))
3784     {
3785         fprintf(er->out_rot, "%12.3e", t);
3786     }
3787
3788     /**************************************************************************/
3789     /* First do ALL the communication! */
3790     for (auto &ergRef : er->enfrotgrp)
3791     {
3792         gmx_enfrotgrp  *erg  = &ergRef;
3793         const t_rotgrp *rotg = erg->rotg;
3794
3795         /* Do we use a collective (global) set of coordinates? */
3796         bColl = ISCOLL(rotg);
3797
3798         /* Calculate the rotation matrix for this angle: */
3799         erg->degangle = rotg->rate * t;
3800         calc_rotmat(erg->vec, erg->degangle, erg->rotmat);
3801
3802         if (bColl)
3803         {
3804             /* Transfer the rotation group's positions such that every node has
3805              * all of them. Every node contributes its local positions x and stores
3806              * it in the collective erg->xc array. */
3807             communicate_group_positions(cr, erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
3808                                         x, rotg->nat, erg->atomSet->numAtomsLocal(), erg->atomSet->localIndex().data(), erg->atomSet->collectiveIndex().data(), erg->xc_old, box);
3809         }
3810         else
3811         {
3812             /* Fill the local masses array;
3813              * this array changes in DD/neighborsearching steps */
3814             if (bNS)
3815             {
3816                 const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
3817                 for (gmx::index i = 0; i < collectiveRotationGroupIndex.size(); i++)
3818                 {
3819                     /* Index of local atom w.r.t. the collective rotation group */
3820                     int ii        = collectiveRotationGroupIndex[i];
3821                     erg->m_loc[i] = erg->mc[ii];
3822                 }
3823             }
3824
3825             /* Calculate Omega*(y_i-y_c) for the local positions */
3826             rotate_local_reference(erg);
3827
3828             /* Choose the nearest PBC images of the group atoms with respect
3829              * to the rotated reference positions */
3830             choose_pbc_image(x, erg, box, 3);
3831
3832             /* Get the center of the rotation group */
3833             if ( (rotg->eType == erotgISOPF) || (rotg->eType == erotgPMPF) )
3834             {
3835                 get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->atomSet->numAtomsLocal(), rotg->nat, erg->xc_center);
3836             }
3837         }
3838
3839     } /* End of loop over rotation groups */
3840
3841     /**************************************************************************/
3842     /* Done communicating, we can start to count cycles for the load balancing now ... */
3843     if (DOMAINDECOMP(cr))
3844     {
3845         ddReopenBalanceRegionCpu(cr->dd);
3846     }
3847
3848 #ifdef TAKETIME
3849     t0 = MPI_Wtime();
3850 #endif
3851
3852     for (auto &ergRef : er->enfrotgrp)
3853     {
3854         gmx_enfrotgrp  *erg  = &ergRef;
3855         const t_rotgrp *rotg = erg->rotg;
3856
3857         if (outstep_rot && MASTER(cr))
3858         {
3859             fprintf(er->out_rot, "%12.4f", erg->degangle);
3860         }
3861
3862         /* Calculate angles and rotation matrices for potential fitting: */
3863         if ( (outstep_rot || outstep_slab) && (erotgFitPOT == rotg->eFittype) )
3864         {
3865             fit = erg->PotAngleFit;
3866             for (int i = 0; i < rotg->PotAngle_nstep; i++)
3867             {
3868                 calc_rotmat(erg->vec, erg->degangle + fit->degangle[i], fit->rotmat[i]);
3869
3870                 /* Clear value from last step */
3871                 erg->PotAngleFit->V[i] = 0.0;
3872             }
3873         }
3874
3875         /* Clear values from last time step */
3876         erg->V        = 0.0;
3877         erg->torque_v = 0.0;
3878         erg->angle_v  = 0.0;
3879         erg->weight_v = 0.0;
3880
3881         switch (rotg->eType)
3882         {
3883             case erotgISO:
3884             case erotgISOPF:
3885             case erotgPM:
3886             case erotgPMPF:
3887                 do_fixed(erg, outstep_rot, outstep_slab);
3888                 break;
3889             case erotgRM:
3890                 do_radial_motion(erg, outstep_rot, outstep_slab);
3891                 break;
3892             case erotgRMPF:
3893                 do_radial_motion_pf(erg, x, box, outstep_rot, outstep_slab);
3894                 break;
3895             case erotgRM2:
3896             case erotgRM2PF:
3897                 do_radial_motion2(erg, x, box, outstep_rot, outstep_slab);
3898                 break;
3899             case erotgFLEXT:
3900             case erotgFLEX2T:
3901                 /* Subtract the center of the rotation group from the collective positions array
3902                  * Also store the center in erg->xc_center since it needs to be subtracted
3903                  * in the low level routines from the local coordinates as well */
3904                 get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
3905                 svmul(-1.0, erg->xc_center, transvec);
3906                 translate_x(erg->xc, rotg->nat, transvec);
3907                 do_flexible(MASTER(cr), er, erg, x, box, t, outstep_rot, outstep_slab);
3908                 break;
3909             case erotgFLEX:
3910             case erotgFLEX2:
3911                 /* Do NOT subtract the center of mass in the low level routines! */
3912                 clear_rvec(erg->xc_center);
3913                 do_flexible(MASTER(cr), er, erg, x, box, t, outstep_rot, outstep_slab);
3914                 break;
3915             default:
3916                 gmx_fatal(FARGS, "No such rotation potential.");
3917         }
3918     }
3919
3920 #ifdef TAKETIME
3921     if (MASTER(cr))
3922     {
3923         fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
3924     }
3925 #endif
3926 }