Eliminate mdlib/mdrun.h
[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,2019, 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 <cstdio>
44 #include <cstdlib>
45 #include <cstring>
46
47 #include <algorithm>
48 #include <memory>
49
50 #include "gromacs/commandline/filenm.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/sim_util.h"
65 #include "gromacs/mdtypes/commrec.h"
66 #include "gromacs/mdtypes/inputrec.h"
67 #include "gromacs/mdtypes/md_enums.h"
68 #include "gromacs/mdtypes/mdrunoptions.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.ssize(); 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)", min_gaussian);
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 static inline int get_homeslab(
1707         rvec       curr_x,   /* The position for which the home slab shall be determined */
1708         const rvec rotvec,   /* The rotation vector */
1709         real       slabdist) /* The slab distance */
1710 {
1711     real dist;
1712
1713
1714     /* The distance of the atom to the coordinate center (where the
1715      * slab with index 0) is */
1716     dist = iprod(rotvec, curr_x);
1717
1718     return gmx::roundToInt(dist / slabdist);
1719 }
1720
1721
1722 /* For a local atom determine the relevant slabs, i.e. slabs in
1723  * which the gaussian is larger than min_gaussian
1724  */
1725 static int get_single_atom_gaussians(
1726         rvec           curr_x,
1727         gmx_enfrotgrp *erg)
1728 {
1729
1730     /* Determine the 'home' slab of this atom: */
1731     int homeslab = get_homeslab(curr_x, erg->vec, erg->rotg->slab_dist);
1732
1733     /* First determine the weight in the atoms home slab: */
1734     real g     = gaussian_weight(curr_x, erg, homeslab);
1735     int  count = 0;
1736     erg->gn_atom[count]    = g;
1737     erg->gn_slabind[count] = homeslab;
1738     count++;
1739
1740
1741     /* Determine the max slab */
1742     int slab = homeslab;
1743     while (g > erg->rotg->min_gaussian)
1744     {
1745         slab++;
1746         g = gaussian_weight(curr_x, erg, slab);
1747         erg->gn_slabind[count] = slab;
1748         erg->gn_atom[count]    = g;
1749         count++;
1750     }
1751     count--;
1752
1753     /* Determine the min slab */
1754     slab = homeslab;
1755     do
1756     {
1757         slab--;
1758         g = gaussian_weight(curr_x, erg, slab);
1759         erg->gn_slabind[count] = slab;
1760         erg->gn_atom[count]    = g;
1761         count++;
1762     }
1763     while (g > erg->rotg->min_gaussian);
1764     count--;
1765
1766     return count;
1767 }
1768
1769
1770 static void flex2_precalc_inner_sum(const gmx_enfrotgrp *erg)
1771 {
1772     rvec            xi;       /* positions in the i-sum                        */
1773     rvec            xcn, ycn; /* the current and the reference slab centers    */
1774     real            gaussian_xi;
1775     rvec            yi0;
1776     rvec            rin;     /* Helper variables                              */
1777     real            fac, fac2;
1778     rvec            innersumvec;
1779     real            OOpsii, OOpsiistar;
1780     real            sin_rin; /* s_ii.r_ii */
1781     rvec            s_in, tmpvec, tmpvec2;
1782     real            mi, wi;  /* Mass-weighting of the positions                 */
1783     real            N_M;     /* N/M                                             */
1784
1785
1786     N_M = erg->rotg->nat * erg->invmass;
1787
1788     /* Loop over all slabs that contain something */
1789     for (int n = erg->slab_first; n <= erg->slab_last; n++)
1790     {
1791         int slabIndex = n - erg->slab_first; /* slab index */
1792
1793         /* The current center of this slab is saved in xcn: */
1794         copy_rvec(erg->slab_center[slabIndex], xcn);
1795         /* ... and the reference center in ycn: */
1796         copy_rvec(erg->slab_center_ref[slabIndex+erg->slab_buffer], ycn);
1797
1798         /*** D. Calculate the whole inner sum used for second and third sum */
1799         /* For slab n, we need to loop over all atoms i again. Since we sorted
1800          * the atoms with respect to the rotation vector, we know that it is sufficient
1801          * to calculate from firstatom to lastatom only. All other contributions will
1802          * be very small. */
1803         clear_rvec(innersumvec);
1804         for (int i = erg->firstatom[slabIndex]; i <= erg->lastatom[slabIndex]; i++)
1805         {
1806             /* Coordinate xi of this atom */
1807             copy_rvec(erg->xc[i], xi);
1808
1809             /* The i-weights */
1810             gaussian_xi = gaussian_weight(xi, erg, n);
1811             mi          = erg->mc_sorted[i]; /* need the sorted mass here */
1812             wi          = N_M*mi;
1813
1814             /* Calculate rin */
1815             copy_rvec(erg->xc_ref_sorted[i], yi0); /* Reference position yi0   */
1816             rvec_sub(yi0, ycn, tmpvec2);           /* tmpvec2 = yi0 - ycn      */
1817             mvmul(erg->rotmat, tmpvec2, rin);      /* rin = Omega.(yi0 - ycn)  */
1818
1819             /* Calculate psi_i* and sin */
1820             rvec_sub(xi, xcn, tmpvec2);                /* tmpvec2 = xi - xcn       */
1821
1822             /* In rare cases, when an atom position coincides with a slab center
1823              * (tmpvec2 == 0) we cannot compute the vector product for s_in.
1824              * However, since the atom is located directly on the pivot, this
1825              * slab's contribution to the force on that atom will be zero
1826              * anyway. Therefore, we continue with the next atom. */
1827             if (gmx_numzero(norm(tmpvec2))) /* 0 == norm(xi - xcn) */
1828             {
1829                 continue;
1830             }
1831
1832             cprod(erg->vec, tmpvec2, tmpvec);          /* tmpvec = v x (xi - xcn)  */
1833             OOpsiistar = norm2(tmpvec)+erg->rotg->eps; /* OOpsii* = 1/psii* = |v x (xi-xcn)|^2 + eps */
1834             OOpsii     = norm(tmpvec);                 /* OOpsii = 1 / psii = |v x (xi - xcn)| */
1835
1836             /*                           *         v x (xi - xcn)          */
1837             unitv(tmpvec, s_in);        /*  sin = ----------------         */
1838                                         /*        |v x (xi - xcn)|         */
1839
1840             sin_rin = iprod(s_in, rin); /* sin_rin = sin . rin             */
1841
1842             /* Now the whole sum */
1843             fac = OOpsii/OOpsiistar;
1844             svmul(fac, rin, tmpvec);
1845             fac2 = fac*fac*OOpsii;
1846             svmul(fac2*sin_rin, s_in, tmpvec2);
1847             rvec_dec(tmpvec, tmpvec2);
1848
1849             svmul(wi*gaussian_xi*sin_rin, tmpvec, tmpvec2);
1850
1851             rvec_inc(innersumvec, tmpvec2);
1852         } /* now we have the inner sum, used both for sum2 and sum3 */
1853
1854         /* Save it to be used in do_flex2_lowlevel */
1855         copy_rvec(innersumvec, erg->slab_innersumvec[slabIndex]);
1856     } /* END of loop over slabs */
1857 }
1858
1859
1860 static void flex_precalc_inner_sum(const gmx_enfrotgrp *erg)
1861 {
1862     rvec            xi;       /* position                                      */
1863     rvec            xcn, ycn; /* the current and the reference slab centers    */
1864     rvec            qin, rin; /* q_i^n and r_i^n                               */
1865     real            bin;
1866     rvec            tmpvec;
1867     rvec            innersumvec; /* Inner part of sum_n2                          */
1868     real            gaussian_xi; /* Gaussian weight gn(xi)                        */
1869     real            mi, wi;      /* Mass-weighting of the positions               */
1870     real            N_M;         /* N/M                                           */
1871
1872     N_M = erg->rotg->nat * erg->invmass;
1873
1874     /* Loop over all slabs that contain something */
1875     for (int n = erg->slab_first; n <= erg->slab_last; n++)
1876     {
1877         int slabIndex = n - erg->slab_first; /* slab index */
1878
1879         /* The current center of this slab is saved in xcn: */
1880         copy_rvec(erg->slab_center[slabIndex], xcn);
1881         /* ... and the reference center in ycn: */
1882         copy_rvec(erg->slab_center_ref[slabIndex+erg->slab_buffer], ycn);
1883
1884         /* For slab n, we need to loop over all atoms i again. Since we sorted
1885          * the atoms with respect to the rotation vector, we know that it is sufficient
1886          * to calculate from firstatom to lastatom only. All other contributions will
1887          * be very small. */
1888         clear_rvec(innersumvec);
1889         for (int i = erg->firstatom[slabIndex]; i <= erg->lastatom[slabIndex]; i++)
1890         {
1891             /* Coordinate xi of this atom */
1892             copy_rvec(erg->xc[i], xi);
1893
1894             /* The i-weights */
1895             gaussian_xi = gaussian_weight(xi, erg, n);
1896             mi          = erg->mc_sorted[i]; /* need the sorted mass here */
1897             wi          = N_M*mi;
1898
1899             /* Calculate rin and qin */
1900             rvec_sub(erg->xc_ref_sorted[i], ycn, tmpvec); /* tmpvec = yi0-ycn */
1901
1902             /* In rare cases, when an atom position coincides with a slab center
1903              * (tmpvec == 0) we cannot compute the vector product for qin.
1904              * However, since the atom is located directly on the pivot, this
1905              * slab's contribution to the force on that atom will be zero
1906              * anyway. Therefore, we continue with the next atom. */
1907             if (gmx_numzero(norm(tmpvec))) /* 0 == norm(yi0 - ycn) */
1908             {
1909                 continue;
1910             }
1911
1912             mvmul(erg->rotmat, tmpvec, rin);              /* rin = Omega.(yi0 - ycn)  */
1913             cprod(erg->vec, rin, tmpvec);                 /* tmpvec = v x Omega*(yi0-ycn) */
1914
1915             /*                                *        v x Omega*(yi0-ycn)    */
1916             unitv(tmpvec, qin);              /* qin = ---------------------   */
1917                                              /*       |v x Omega*(yi0-ycn)|   */
1918
1919             /* Calculate bin */
1920             rvec_sub(xi, xcn, tmpvec);            /* tmpvec = xi-xcn          */
1921             bin = iprod(qin, tmpvec);             /* bin  = qin*(xi-xcn)      */
1922
1923             svmul(wi*gaussian_xi*bin, qin, tmpvec);
1924
1925             /* Add this contribution to the inner sum: */
1926             rvec_add(innersumvec, tmpvec, innersumvec);
1927         } /* now we have the inner sum vector S^n for this slab */
1928           /* Save it to be used in do_flex_lowlevel */
1929         copy_rvec(innersumvec, erg->slab_innersumvec[slabIndex]);
1930     }
1931 }
1932
1933
1934 static real do_flex2_lowlevel(
1935         gmx_enfrotgrp *erg,
1936         real           sigma, /* The Gaussian width sigma */
1937         rvec           x[],
1938         gmx_bool       bOutstepRot,
1939         gmx_bool       bOutstepSlab,
1940         matrix         box)
1941 {
1942     int             count, ii, iigrp;
1943     rvec            xj;          /* position in the i-sum                         */
1944     rvec            yj0;         /* the reference position in the j-sum           */
1945     rvec            xcn, ycn;    /* the current and the reference slab centers    */
1946     real            V;           /* This node's part of the rotation pot. energy  */
1947     real            gaussian_xj; /* Gaussian weight                               */
1948     real            beta;
1949
1950     real            numerator, fit_numerator;
1951     rvec            rjn, fit_rjn; /* Helper variables                              */
1952     real            fac, fac2;
1953
1954     real            OOpsij, OOpsijstar;
1955     real            OOsigma2; /* 1/(sigma^2)                                   */
1956     real            sjn_rjn;
1957     real            betasigpsi;
1958     rvec            sjn, tmpvec, tmpvec2, yj0_ycn;
1959     rvec            sum1vec_part, sum1vec, sum2vec_part, sum2vec, sum3vec, sum4vec, innersumvec;
1960     real            sum3, sum4;
1961     real            mj, wj;  /* Mass-weighting of the positions               */
1962     real            N_M;     /* N/M                                           */
1963     real            Wjn;     /* g_n(x_j) m_j / Mjn                            */
1964     gmx_bool        bCalcPotFit;
1965
1966     /* To calculate the torque per slab */
1967     rvec slab_force;         /* Single force from slab n on one atom          */
1968     rvec slab_sum1vec_part;
1969     real slab_sum3part, slab_sum4part;
1970     rvec slab_sum1vec, slab_sum2vec, slab_sum3vec, slab_sum4vec;
1971
1972     /* Pre-calculate the inner sums, so that we do not have to calculate
1973      * them again for every atom */
1974     flex2_precalc_inner_sum(erg);
1975
1976     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
1977
1978     /********************************************************/
1979     /* Main loop over all local atoms of the rotation group */
1980     /********************************************************/
1981     N_M      = erg->rotg->nat * erg->invmass;
1982     V        = 0.0;
1983     OOsigma2 = 1.0 / (sigma*sigma);
1984     const auto &localRotationGroupIndex      = erg->atomSet->localIndex();
1985     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
1986
1987     for (gmx::index j = 0; j < localRotationGroupIndex.ssize(); j++)
1988     {
1989         /* Local index of a rotation group atom  */
1990         ii = localRotationGroupIndex[j];
1991         /* Position of this atom in the collective array */
1992         iigrp = collectiveRotationGroupIndex[j];
1993         /* Mass-weighting */
1994         mj = erg->mc[iigrp];  /* need the unsorted mass here */
1995         wj = N_M*mj;
1996
1997         /* Current position of this atom: x[ii][XX/YY/ZZ]
1998          * Note that erg->xc_center contains the center of mass in case the flex2-t
1999          * potential was chosen. For the flex2 potential erg->xc_center must be
2000          * zero. */
2001         rvec_sub(x[ii], erg->xc_center, xj);
2002
2003         /* Shift this atom such that it is near its reference */
2004         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
2005
2006         /* Determine the slabs to loop over, i.e. the ones with contributions
2007          * larger than min_gaussian */
2008         count = get_single_atom_gaussians(xj, erg);
2009
2010         clear_rvec(sum1vec_part);
2011         clear_rvec(sum2vec_part);
2012         sum3 = 0.0;
2013         sum4 = 0.0;
2014         /* Loop over the relevant slabs for this atom */
2015         for (int ic = 0; ic < count; ic++)
2016         {
2017             int n = erg->gn_slabind[ic];
2018
2019             /* Get the precomputed Gaussian value of curr_slab for curr_x */
2020             gaussian_xj = erg->gn_atom[ic];
2021
2022             int slabIndex = n - erg->slab_first; /* slab index */
2023
2024             /* The (unrotated) reference position of this atom is copied to yj0: */
2025             copy_rvec(erg->rotg->x_ref[iigrp], yj0);
2026
2027             beta = calc_beta(xj, erg, n);
2028
2029             /* The current center of this slab is saved in xcn: */
2030             copy_rvec(erg->slab_center[slabIndex], xcn);
2031             /* ... and the reference center in ycn: */
2032             copy_rvec(erg->slab_center_ref[slabIndex+erg->slab_buffer], ycn);
2033
2034             rvec_sub(yj0, ycn, yj0_ycn);          /* yj0_ycn = yj0 - ycn      */
2035
2036             /* Rotate: */
2037             mvmul(erg->rotmat, yj0_ycn, rjn);     /* rjn = Omega.(yj0 - ycn)  */
2038
2039             /* Subtract the slab center from xj */
2040             rvec_sub(xj, xcn, tmpvec2);           /* tmpvec2 = xj - xcn       */
2041
2042             /* In rare cases, when an atom position coincides with a slab center
2043              * (tmpvec2 == 0) we cannot compute the vector product for sjn.
2044              * However, since the atom is located directly on the pivot, this
2045              * slab's contribution to the force on that atom will be zero
2046              * anyway. Therefore, we directly move on to the next slab.       */
2047             if (gmx_numzero(norm(tmpvec2))) /* 0 == norm(xj - xcn) */
2048             {
2049                 continue;
2050             }
2051
2052             /* Calculate sjn */
2053             cprod(erg->vec, tmpvec2, tmpvec);          /* tmpvec = v x (xj - xcn)  */
2054
2055             OOpsijstar = norm2(tmpvec)+erg->rotg->eps; /* OOpsij* = 1/psij* = |v x (xj-xcn)|^2 + eps */
2056
2057             numerator = gmx::square(iprod(tmpvec, rjn));
2058
2059             /*********************************/
2060             /* Add to the rotation potential */
2061             /*********************************/
2062             V += 0.5*erg->rotg->k*wj*gaussian_xj*numerator/OOpsijstar;
2063
2064             /* If requested, also calculate the potential for a set of angles
2065              * near the current reference angle */
2066             if (bCalcPotFit)
2067             {
2068                 for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2069                 {
2070                     mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, fit_rjn);
2071                     fit_numerator              = gmx::square(iprod(tmpvec, fit_rjn));
2072                     erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*gaussian_xj*fit_numerator/OOpsijstar;
2073                 }
2074             }
2075
2076             /*************************************/
2077             /* Now calculate the force on atom j */
2078             /*************************************/
2079
2080             OOpsij = norm(tmpvec);    /* OOpsij = 1 / psij = |v x (xj - xcn)| */
2081
2082             /*                              *         v x (xj - xcn)          */
2083             unitv(tmpvec, sjn);            /*  sjn = ----------------         */
2084                                            /*        |v x (xj - xcn)|         */
2085
2086             sjn_rjn = iprod(sjn, rjn);     /* sjn_rjn = sjn . rjn             */
2087
2088
2089             /*** A. Calculate the first of the four sum terms: ****************/
2090             fac = OOpsij/OOpsijstar;
2091             svmul(fac, rjn, tmpvec);
2092             fac2 = fac*fac*OOpsij;
2093             svmul(fac2*sjn_rjn, sjn, tmpvec2);
2094             rvec_dec(tmpvec, tmpvec2);
2095             fac2 = wj*gaussian_xj; /* also needed for sum4 */
2096             svmul(fac2*sjn_rjn, tmpvec, slab_sum1vec_part);
2097             /********************/
2098             /*** Add to sum1: ***/
2099             /********************/
2100             rvec_inc(sum1vec_part, slab_sum1vec_part); /* sum1 still needs to vector multiplied with v */
2101
2102             /*** B. Calculate the forth of the four sum terms: ****************/
2103             betasigpsi = beta*OOsigma2*OOpsij; /* this is also needed for sum3 */
2104             /********************/
2105             /*** Add to sum4: ***/
2106             /********************/
2107             slab_sum4part = fac2*betasigpsi*fac*sjn_rjn*sjn_rjn; /* Note that fac is still valid from above */
2108             sum4         += slab_sum4part;
2109
2110             /*** C. Calculate Wjn for second and third sum */
2111             /* Note that we can safely divide by slab_weights since we check in
2112              * get_slab_centers that it is non-zero. */
2113             Wjn = gaussian_xj*mj/erg->slab_weights[slabIndex];
2114
2115             /* We already have precalculated the inner sum for slab n */
2116             copy_rvec(erg->slab_innersumvec[slabIndex], innersumvec);
2117
2118             /* Weigh the inner sum vector with Wjn */
2119             svmul(Wjn, innersumvec, innersumvec);
2120
2121             /*** E. Calculate the second of the four sum terms: */
2122             /********************/
2123             /*** Add to sum2: ***/
2124             /********************/
2125             rvec_inc(sum2vec_part, innersumvec); /* sum2 still needs to be vector crossproduct'ed with v */
2126
2127             /*** F. Calculate the third of the four sum terms: */
2128             slab_sum3part = betasigpsi * iprod(sjn, innersumvec);
2129             sum3         += slab_sum3part; /* still needs to be multiplied with v */
2130
2131             /*** G. Calculate the torque on the local slab's axis: */
2132             if (bOutstepRot)
2133             {
2134                 /* Sum1 */
2135                 cprod(slab_sum1vec_part, erg->vec, slab_sum1vec);
2136                 /* Sum2 */
2137                 cprod(innersumvec, erg->vec, slab_sum2vec);
2138                 /* Sum3 */
2139                 svmul(slab_sum3part, erg->vec, slab_sum3vec);
2140                 /* Sum4 */
2141                 svmul(slab_sum4part, erg->vec, slab_sum4vec);
2142
2143                 /* The force on atom ii from slab n only: */
2144                 for (int m = 0; m < DIM; m++)
2145                 {
2146                     slab_force[m] = erg->rotg->k * (-slab_sum1vec[m] + slab_sum2vec[m] - slab_sum3vec[m] + 0.5*slab_sum4vec[m]);
2147                 }
2148
2149                 erg->slab_torque_v[slabIndex] += torque(erg->vec, slab_force, xj, xcn);
2150             }
2151         } /* END of loop over slabs */
2152
2153         /* Construct the four individual parts of the vector sum: */
2154         cprod(sum1vec_part, erg->vec, sum1vec);      /* sum1vec =   { } x v  */
2155         cprod(sum2vec_part, erg->vec, sum2vec);      /* sum2vec =   { } x v  */
2156         svmul(sum3, erg->vec, sum3vec);              /* sum3vec =   { } . v  */
2157         svmul(sum4, erg->vec, sum4vec);              /* sum4vec =   { } . v  */
2158
2159         /* Store the additional force so that it can be added to the force
2160          * array after the normal forces have been evaluated */
2161         for (int m = 0; m < DIM; m++)
2162         {
2163             erg->f_rot_loc[j][m] = erg->rotg->k * (-sum1vec[m] + sum2vec[m] - sum3vec[m] + 0.5*sum4vec[m]);
2164         }
2165
2166 #ifdef SUM_PARTS
2167         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]);
2168         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]);
2169         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]);
2170         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]);
2171 #endif
2172
2173         PRINT_FORCE_J
2174
2175     } /* END of loop over local atoms */
2176
2177     return V;
2178 }
2179
2180
2181 static real do_flex_lowlevel(
2182         gmx_enfrotgrp *erg,
2183         real           sigma, /* The Gaussian width sigma                      */
2184         rvec           x[],
2185         gmx_bool       bOutstepRot,
2186         gmx_bool       bOutstepSlab,
2187         matrix         box)
2188 {
2189     int             count, iigrp;
2190     rvec            xj, yj0;                /* current and reference position                */
2191     rvec            xcn, ycn;               /* the current and the reference slab centers    */
2192     rvec            yj0_ycn;                /* yj0 - ycn                                     */
2193     rvec            xj_xcn;                 /* xj - xcn                                      */
2194     rvec            qjn, fit_qjn;           /* q_i^n                                         */
2195     rvec            sum_n1, sum_n2;         /* Two contributions to the rotation force       */
2196     rvec            innersumvec;            /* Inner part of sum_n2                          */
2197     rvec            s_n;
2198     rvec            force_n;                /* Single force from slab n on one atom          */
2199     rvec            force_n1, force_n2;     /* First and second part of force_n              */
2200     rvec            tmpvec, tmpvec2, tmp_f; /* Helper variables                              */
2201     real            V;                      /* The rotation potential energy                 */
2202     real            OOsigma2;               /* 1/(sigma^2)                                   */
2203     real            beta;                   /* beta_n(xj)                                    */
2204     real            bjn, fit_bjn;           /* b_j^n                                         */
2205     real            gaussian_xj;            /* Gaussian weight gn(xj)                        */
2206     real            betan_xj_sigma2;
2207     real            mj, wj;                 /* Mass-weighting of the positions               */
2208     real            N_M;                    /* N/M                                           */
2209     gmx_bool        bCalcPotFit;
2210
2211     /* Pre-calculate the inner sums, so that we do not have to calculate
2212      * them again for every atom */
2213     flex_precalc_inner_sum(erg);
2214
2215     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
2216
2217     /********************************************************/
2218     /* Main loop over all local atoms of the rotation group */
2219     /********************************************************/
2220     OOsigma2 = 1.0/(sigma*sigma);
2221     N_M      = erg->rotg->nat * erg->invmass;
2222     V        = 0.0;
2223     const auto &localRotationGroupIndex      = erg->atomSet->localIndex();
2224     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
2225
2226     for (gmx::index j = 0; j < localRotationGroupIndex.ssize(); j++)
2227     {
2228         /* Local index of a rotation group atom  */
2229         int ii = localRotationGroupIndex[j];
2230         /* Position of this atom in the collective array */
2231         iigrp = collectiveRotationGroupIndex[j];
2232         /* Mass-weighting */
2233         mj = erg->mc[iigrp];  /* need the unsorted mass here */
2234         wj = N_M*mj;
2235
2236         /* Current position of this atom: x[ii][XX/YY/ZZ]
2237          * Note that erg->xc_center contains the center of mass in case the flex-t
2238          * potential was chosen. For the flex potential erg->xc_center must be
2239          * zero. */
2240         rvec_sub(x[ii], erg->xc_center, xj);
2241
2242         /* Shift this atom such that it is near its reference */
2243         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
2244
2245         /* Determine the slabs to loop over, i.e. the ones with contributions
2246          * larger than min_gaussian */
2247         count = get_single_atom_gaussians(xj, erg);
2248
2249         clear_rvec(sum_n1);
2250         clear_rvec(sum_n2);
2251
2252         /* Loop over the relevant slabs for this atom */
2253         for (int ic = 0; ic < count; ic++)
2254         {
2255             int n = erg->gn_slabind[ic];
2256
2257             /* Get the precomputed Gaussian for xj in slab n */
2258             gaussian_xj = erg->gn_atom[ic];
2259
2260             int slabIndex = n - erg->slab_first; /* slab index */
2261
2262             /* The (unrotated) reference position of this atom is saved in yj0: */
2263             copy_rvec(erg->rotg->x_ref[iigrp], yj0);
2264
2265             beta = calc_beta(xj, erg, n);
2266
2267             /* The current center of this slab is saved in xcn: */
2268             copy_rvec(erg->slab_center[slabIndex], xcn);
2269             /* ... and the reference center in ycn: */
2270             copy_rvec(erg->slab_center_ref[slabIndex+erg->slab_buffer], ycn);
2271
2272             rvec_sub(yj0, ycn, yj0_ycn); /* yj0_ycn = yj0 - ycn */
2273
2274             /* In rare cases, when an atom position coincides with a reference slab
2275              * center (yj0_ycn == 0) we cannot compute the normal vector qjn.
2276              * However, since the atom is located directly on the pivot, this
2277              * slab's contribution to the force on that atom will be zero
2278              * anyway. Therefore, we directly move on to the next slab.       */
2279             if (gmx_numzero(norm(yj0_ycn))) /* 0 == norm(yj0 - ycn) */
2280             {
2281                 continue;
2282             }
2283
2284             /* Rotate: */
2285             mvmul(erg->rotmat, yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
2286
2287             /* Subtract the slab center from xj */
2288             rvec_sub(xj, xcn, xj_xcn);           /* xj_xcn = xj - xcn         */
2289
2290             /* Calculate qjn */
2291             cprod(erg->vec, tmpvec2, tmpvec); /* tmpvec= v x Omega.(yj0-ycn) */
2292
2293             /*                         *         v x Omega.(yj0-ycn)    */
2294             unitv(tmpvec, qjn);       /*  qjn = ---------------------   */
2295                                       /*        |v x Omega.(yj0-ycn)|   */
2296
2297             bjn = iprod(qjn, xj_xcn); /* bjn = qjn * (xj - xcn) */
2298
2299             /*********************************/
2300             /* Add to the rotation potential */
2301             /*********************************/
2302             V += 0.5*erg->rotg->k*wj*gaussian_xj*gmx::square(bjn);
2303
2304             /* If requested, also calculate the potential for a set of angles
2305              * near the current reference angle */
2306             if (bCalcPotFit)
2307             {
2308                 for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2309                 {
2310                     /* As above calculate Omega.(yj0-ycn), now for the other angles */
2311                     mvmul(erg->PotAngleFit->rotmat[ifit], yj0_ycn, tmpvec2); /* tmpvec2= Omega.(yj0-ycn) */
2312                     /* As above calculate qjn */
2313                     cprod(erg->vec, tmpvec2, tmpvec);                        /* tmpvec= v x Omega.(yj0-ycn) */
2314                     /*                                                        *             v x Omega.(yj0-ycn)    */
2315                     unitv(tmpvec, fit_qjn);                                  /*  fit_qjn = ---------------------   */
2316                                                                              /*            |v x Omega.(yj0-ycn)|   */
2317                     fit_bjn = iprod(fit_qjn, xj_xcn);                        /* fit_bjn = fit_qjn * (xj - xcn) */
2318                     /* Add to the rotation potential for this angle */
2319                     erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*gaussian_xj*gmx::square(fit_bjn);
2320                 }
2321             }
2322
2323             /****************************************************************/
2324             /* sum_n1 will typically be the main contribution to the force: */
2325             /****************************************************************/
2326             betan_xj_sigma2 = beta*OOsigma2;  /*  beta_n(xj)/sigma^2  */
2327
2328             /* The next lines calculate
2329              *  qjn - (bjn*beta(xj)/(2sigma^2))v  */
2330             svmul(bjn*0.5*betan_xj_sigma2, erg->vec, tmpvec2);
2331             rvec_sub(qjn, tmpvec2, tmpvec);
2332
2333             /* Multiply with gn(xj)*bjn: */
2334             svmul(gaussian_xj*bjn, tmpvec, tmpvec2);
2335
2336             /* Sum over n: */
2337             rvec_inc(sum_n1, tmpvec2);
2338
2339             /* We already have precalculated the Sn term for slab n */
2340             copy_rvec(erg->slab_innersumvec[slabIndex], s_n);
2341             /*                                                             *          beta_n(xj)              */
2342             svmul(betan_xj_sigma2*iprod(s_n, xj_xcn), erg->vec, tmpvec); /* tmpvec = ---------- s_n (xj-xcn) */
2343                                                                          /*            sigma^2               */
2344
2345             rvec_sub(s_n, tmpvec, innersumvec);
2346
2347             /* We can safely divide by slab_weights since we check in get_slab_centers
2348              * that it is non-zero. */
2349             svmul(gaussian_xj/erg->slab_weights[slabIndex], innersumvec, innersumvec);
2350
2351             rvec_add(sum_n2, innersumvec, sum_n2);
2352
2353             /* Calculate the torque: */
2354             if (bOutstepRot)
2355             {
2356                 /* The force on atom ii from slab n only: */
2357                 svmul(-erg->rotg->k*wj, tmpvec2, force_n1);     /* part 1 */
2358                 svmul( erg->rotg->k*mj, innersumvec, force_n2); /* part 2 */
2359                 rvec_add(force_n1, force_n2, force_n);
2360                 erg->slab_torque_v[slabIndex] += torque(erg->vec, force_n, xj, xcn);
2361             }
2362         } /* END of loop over slabs */
2363
2364         /* Put both contributions together: */
2365         svmul(wj, sum_n1, sum_n1);
2366         svmul(mj, sum_n2, sum_n2);
2367         rvec_sub(sum_n2, sum_n1, tmp_f); /* F = -grad V */
2368
2369         /* Store the additional force so that it can be added to the force
2370          * array after the normal forces have been evaluated */
2371         for (int m = 0; m < DIM; m++)
2372         {
2373             erg->f_rot_loc[j][m] = erg->rotg->k*tmp_f[m];
2374         }
2375
2376         PRINT_FORCE_J
2377
2378     } /* END of loop over local atoms */
2379
2380     return V;
2381 }
2382
2383 static void sort_collective_coordinates(
2384         gmx_enfrotgrp    *erg,
2385         sort_along_vec_t *data) /* Buffer for sorting the positions */
2386 {
2387     /* The projection of the position vector on the rotation vector is
2388      * the relevant value for sorting. Fill the 'data' structure */
2389     for (int i = 0; i < erg->rotg->nat; i++)
2390     {
2391         data[i].xcproj = iprod(erg->xc[i], erg->vec);  /* sort criterium */
2392         data[i].m      = erg->mc[i];
2393         data[i].ind    = i;
2394         copy_rvec(erg->xc[i], data[i].x    );
2395         copy_rvec(erg->rotg->x_ref[i], data[i].x_ref);
2396     }
2397     /* Sort the 'data' structure */
2398     std::sort(data, data+erg->rotg->nat,
2399               [](const sort_along_vec_t &a, const sort_along_vec_t &b)
2400               {
2401                   return a.xcproj < b.xcproj;
2402               });
2403
2404     /* Copy back the sorted values */
2405     for (int i = 0; i < erg->rotg->nat; i++)
2406     {
2407         copy_rvec(data[i].x, erg->xc[i]           );
2408         copy_rvec(data[i].x_ref, erg->xc_ref_sorted[i]);
2409         erg->mc_sorted[i]  = data[i].m;
2410         erg->xc_sortind[i] = data[i].ind;
2411     }
2412 }
2413
2414
2415 /* For each slab, get the first and the last index of the sorted atom
2416  * indices */
2417 static void get_firstlast_atom_per_slab(const gmx_enfrotgrp *erg)
2418 {
2419     real beta;
2420
2421     /* Find the first atom that needs to enter the calculation for each slab */
2422     int n = erg->slab_first; /* slab */
2423     int i = 0;               /* start with the first atom */
2424     do
2425     {
2426         /* Find the first atom that significantly contributes to this slab */
2427         do /* move forward in position until a large enough beta is found */
2428         {
2429             beta = calc_beta(erg->xc[i], erg, n);
2430             i++;
2431         }
2432         while ((beta < -erg->max_beta) && (i < erg->rotg->nat));
2433         i--;
2434         int slabIndex             = n - erg->slab_first; /* slab index */
2435         erg->firstatom[slabIndex] = i;
2436         /* Proceed to the next slab */
2437         n++;
2438     }
2439     while (n <= erg->slab_last);
2440
2441     /* Find the last atom for each slab */
2442     n = erg->slab_last;   /* start with last slab */
2443     i = erg->rotg->nat-1; /* start with the last atom */
2444     do
2445     {
2446         do  /* move backward in position until a large enough beta is found */
2447         {
2448             beta = calc_beta(erg->xc[i], erg, n);
2449             i--;
2450         }
2451         while ((beta > erg->max_beta) && (i > -1));
2452         i++;
2453         int slabIndex            = n - erg->slab_first; /* slab index */
2454         erg->lastatom[slabIndex] = i;
2455         /* Proceed to the next slab */
2456         n--;
2457     }
2458     while (n >= erg->slab_first);
2459 }
2460
2461
2462 /* Determine the very first and very last slab that needs to be considered
2463  * For the first slab that needs to be considered, we have to find the smallest
2464  * n that obeys:
2465  *
2466  * x_first * v - n*Delta_x <= beta_max
2467  *
2468  * slab index n, slab distance Delta_x, rotation vector v. For the last slab we
2469  * have to find the largest n that obeys
2470  *
2471  * x_last * v - n*Delta_x >= -beta_max
2472  *
2473  */
2474 static inline int get_first_slab(
2475         const gmx_enfrotgrp *erg,
2476         rvec                 firstatom) /* First atom after sorting along the rotation vector v */
2477 {
2478     /* Find the first slab for the first atom */
2479     return static_cast<int>(ceil(static_cast<double>((iprod(firstatom, erg->vec) - erg->max_beta)/erg->rotg->slab_dist)));
2480 }
2481
2482
2483 static inline int get_last_slab(
2484         const gmx_enfrotgrp *erg,
2485         rvec                 lastatom) /* Last atom along v */
2486 {
2487     /* Find the last slab for the last atom */
2488     return static_cast<int>(floor(static_cast<double>((iprod(lastatom, erg->vec) + erg->max_beta)/erg->rotg->slab_dist)));
2489 }
2490
2491
2492 static void get_firstlast_slab_check(
2493         gmx_enfrotgrp   *erg,       /* The rotation group (data only accessible in this file) */
2494         rvec             firstatom, /* First atom after sorting along the rotation vector v */
2495         rvec             lastatom)  /* Last atom along v */
2496 {
2497     erg->slab_first = get_first_slab(erg, firstatom);
2498     erg->slab_last  = get_last_slab(erg, lastatom);
2499
2500     /* Calculate the slab buffer size, which changes when slab_first changes */
2501     erg->slab_buffer = erg->slab_first - erg->slab_first_ref;
2502
2503     /* Check whether we have reference data to compare against */
2504     if (erg->slab_first < erg->slab_first_ref)
2505     {
2506         gmx_fatal(FARGS, "%s No reference data for first slab (n=%d), unable to proceed.",
2507                   RotStr, erg->slab_first);
2508     }
2509
2510     /* Check whether we have reference data to compare against */
2511     if (erg->slab_last > erg->slab_last_ref)
2512     {
2513         gmx_fatal(FARGS, "%s No reference data for last slab (n=%d), unable to proceed.",
2514                   RotStr, erg->slab_last);
2515     }
2516 }
2517
2518
2519 /* Enforced rotation with a flexible axis */
2520 static void do_flexible(
2521         gmx_bool        bMaster,
2522         gmx_enfrot     *enfrot,       /* Other rotation data                        */
2523         gmx_enfrotgrp  *erg,
2524         rvec            x[],          /* The local positions                        */
2525         matrix          box,
2526         double          t,            /* Time in picoseconds                        */
2527         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
2528         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
2529 {
2530     int             nslabs;
2531     real            sigma;    /* The Gaussian width sigma */
2532
2533     /* Define the sigma value */
2534     sigma = 0.7*erg->rotg->slab_dist;
2535
2536     /* Sort the collective coordinates erg->xc along the rotation vector. This is
2537      * an optimization for the inner loop. */
2538     sort_collective_coordinates(erg, enfrot->data);
2539
2540     /* Determine the first relevant slab for the first atom and the last
2541      * relevant slab for the last atom */
2542     get_firstlast_slab_check(erg, erg->xc[0], erg->xc[erg->rotg->nat-1]);
2543
2544     /* Determine for each slab depending on the min_gaussian cutoff criterium,
2545      * a first and a last atom index inbetween stuff needs to be calculated */
2546     get_firstlast_atom_per_slab(erg);
2547
2548     /* Determine the gaussian-weighted center of positions for all slabs */
2549     get_slab_centers(erg, erg->xc, erg->mc_sorted, t, enfrot->out_slabs, bOutstepSlab, FALSE);
2550
2551     /* Clear the torque per slab from last time step: */
2552     nslabs = erg->slab_last - erg->slab_first + 1;
2553     for (int l = 0; l < nslabs; l++)
2554     {
2555         erg->slab_torque_v[l] = 0.0;
2556     }
2557
2558     /* Call the rotational forces kernel */
2559     if (erg->rotg->eType == erotgFLEX || erg->rotg->eType == erotgFLEXT)
2560     {
2561         erg->V = do_flex_lowlevel(erg, sigma, x, bOutstepRot, bOutstepSlab, box);
2562     }
2563     else if (erg->rotg->eType == erotgFLEX2 || erg->rotg->eType == erotgFLEX2T)
2564     {
2565         erg->V = do_flex2_lowlevel(erg, sigma, x, bOutstepRot, bOutstepSlab, box);
2566     }
2567     else
2568     {
2569         gmx_fatal(FARGS, "Unknown flexible rotation type");
2570     }
2571
2572     /* Determine angle by RMSD fit to the reference - Let's hope this */
2573     /* only happens once in a while, since this is not parallelized! */
2574     if (bMaster && (erotgFitPOT != erg->rotg->eFittype) )
2575     {
2576         if (bOutstepRot)
2577         {
2578             /* Fit angle of the whole rotation group */
2579             erg->angle_v = flex_fit_angle(erg);
2580         }
2581         if (bOutstepSlab)
2582         {
2583             /* Fit angle of each slab */
2584             flex_fit_angle_perslab(erg, t, erg->degangle, enfrot->out_angles);
2585         }
2586     }
2587
2588     /* Lump together the torques from all slabs: */
2589     erg->torque_v = 0.0;
2590     for (int l = 0; l < nslabs; l++)
2591     {
2592         erg->torque_v += erg->slab_torque_v[l];
2593     }
2594 }
2595
2596
2597 /* Calculate the angle between reference and actual rotation group atom,
2598  * both projected into a plane perpendicular to the rotation vector: */
2599 static void angle(const gmx_enfrotgrp *erg,
2600                   rvec                 x_act,
2601                   rvec                 x_ref,
2602                   real                *alpha,
2603                   real                *weight) /* atoms near the rotation axis should count less than atoms far away */
2604 {
2605     rvec xp, xrp;                              /* current and reference positions projected on a plane perpendicular to pg->vec */
2606     rvec dum;
2607
2608
2609     /* Project x_ref and x into a plane through the origin perpendicular to rot_vec: */
2610     /* Project x_ref: xrp = x_ref - (vec * x_ref) * vec */
2611     svmul(iprod(erg->vec, x_ref), erg->vec, dum);
2612     rvec_sub(x_ref, dum, xrp);
2613     /* Project x_act: */
2614     svmul(iprod(erg->vec, x_act), erg->vec, dum);
2615     rvec_sub(x_act, dum, xp);
2616
2617     /* Retrieve information about which vector precedes. gmx_angle always
2618      * returns a positive angle. */
2619     cprod(xp, xrp, dum); /* if reference precedes, this is pointing into the same direction as vec */
2620
2621     if (iprod(erg->vec, dum) >= 0)
2622     {
2623         *alpha = -gmx_angle(xrp, xp);
2624     }
2625     else
2626     {
2627         *alpha = +gmx_angle(xrp, xp);
2628     }
2629
2630     /* Also return the weight */
2631     *weight = norm(xp);
2632 }
2633
2634
2635 /* Project first vector onto a plane perpendicular to the second vector
2636  * dr = dr - (dr.v)v
2637  * Note that v must be of unit length.
2638  */
2639 static inline void project_onto_plane(rvec dr, const rvec v)
2640 {
2641     rvec tmp;
2642
2643
2644     svmul(iprod(dr, v), v, tmp); /* tmp = (dr.v)v */
2645     rvec_dec(dr, tmp);           /* dr = dr - (dr.v)v */
2646 }
2647
2648
2649 /* Fixed rotation: The rotation reference group rotates around the v axis. */
2650 /* The atoms of the actual rotation group are attached with imaginary  */
2651 /* springs to the reference atoms.                                     */
2652 static void do_fixed(
2653         gmx_enfrotgrp  *erg,
2654         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
2655         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
2656 {
2657     rvec            dr;
2658     rvec            tmp_f;     /* Force */
2659     real            alpha;     /* a single angle between an actual and a reference position */
2660     real            weight;    /* single weight for a single angle */
2661     rvec            xi_xc;     /* xi - xc */
2662     gmx_bool        bCalcPotFit;
2663     rvec            fit_xr_loc;
2664
2665     /* for mass weighting: */
2666     real      wi;              /* Mass-weighting of the positions */
2667     real      N_M;             /* N/M */
2668     real      k_wi;            /* k times wi */
2669
2670     gmx_bool  bProject;
2671
2672     bProject    = (erg->rotg->eType == erotgPM) || (erg->rotg->eType == erotgPMPF);
2673     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
2674
2675     N_M = erg->rotg->nat * erg->invmass;
2676     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
2677     /* Each process calculates the forces on its local atoms */
2678     for (size_t j = 0; j < erg->atomSet->numAtomsLocal(); j++)
2679     {
2680         /* Calculate (x_i-x_c) resp. (x_i-u) */
2681         rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xi_xc);
2682
2683         /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
2684         rvec_sub(erg->xr_loc[j], xi_xc, dr);
2685
2686         if (bProject)
2687         {
2688             project_onto_plane(dr, erg->vec);
2689         }
2690
2691         /* Mass-weighting */
2692         wi = N_M*erg->m_loc[j];
2693
2694         /* Store the additional force so that it can be added to the force
2695          * array after the normal forces have been evaluated */
2696         k_wi = erg->rotg->k*wi;
2697         for (int m = 0; m < DIM; m++)
2698         {
2699             tmp_f[m]             = k_wi*dr[m];
2700             erg->f_rot_loc[j][m] = tmp_f[m];
2701             erg->V              += 0.5*k_wi*gmx::square(dr[m]);
2702         }
2703
2704         /* If requested, also calculate the potential for a set of angles
2705          * near the current reference angle */
2706         if (bCalcPotFit)
2707         {
2708             for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2709             {
2710                 /* Index of this rotation group atom with respect to the whole rotation group */
2711                 int jj = collectiveRotationGroupIndex[j];
2712
2713                 /* Rotate with the alternative angle. Like rotate_local_reference(),
2714                  * just for a single local atom */
2715                 mvmul(erg->PotAngleFit->rotmat[ifit], erg->rotg->x_ref[jj], fit_xr_loc); /* fit_xr_loc = Omega*(y_i-y_c) */
2716
2717                 /* Calculate Omega*(y_i-y_c)-(x_i-x_c) */
2718                 rvec_sub(fit_xr_loc, xi_xc, dr);
2719
2720                 if (bProject)
2721                 {
2722                     project_onto_plane(dr, erg->vec);
2723                 }
2724
2725                 /* Add to the rotation potential for this angle: */
2726                 erg->PotAngleFit->V[ifit] += 0.5*k_wi*norm2(dr);
2727             }
2728         }
2729
2730         if (bOutstepRot)
2731         {
2732             /* Add to the torque of this rotation group */
2733             erg->torque_v += torque(erg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
2734
2735             /* Calculate the angle between reference and actual rotation group atom. */
2736             angle(erg, xi_xc, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
2737             erg->angle_v  += alpha * weight;
2738             erg->weight_v += weight;
2739         }
2740         /* If you want enforced rotation to contribute to the virial,
2741          * activate the following lines:
2742             if (MASTER(cr))
2743             {
2744                Add the rotation contribution to the virial
2745               for(j=0; j<DIM; j++)
2746                 for(m=0;m<DIM;m++)
2747                   vir[j][m] += 0.5*f[ii][j]*dr[m];
2748             }
2749          */
2750
2751         PRINT_FORCE_J
2752
2753     } /* end of loop over local rotation group atoms */
2754 }
2755
2756
2757 /* Calculate the radial motion potential and forces */
2758 static void do_radial_motion(
2759         gmx_enfrotgrp  *erg,
2760         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
2761         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
2762 {
2763     rvec            tmp_f;            /* Force */
2764     real            alpha;            /* a single angle between an actual and a reference position */
2765     real            weight;           /* single weight for a single angle */
2766     rvec            xj_u;             /* xj - u */
2767     rvec            tmpvec, fit_tmpvec;
2768     real            fac, fac2, sum = 0.0;
2769     rvec            pj;
2770     gmx_bool        bCalcPotFit;
2771
2772     /* For mass weighting: */
2773     real      wj;              /* Mass-weighting of the positions */
2774     real      N_M;             /* N/M */
2775
2776     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
2777
2778     N_M = erg->rotg->nat * erg->invmass;
2779     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
2780     /* Each process calculates the forces on its local atoms */
2781     for (size_t j = 0; j < erg->atomSet->numAtomsLocal(); j++)
2782     {
2783         /* Calculate (xj-u) */
2784         rvec_sub(erg->x_loc_pbc[j], erg->xc_center, xj_u);  /* xj_u = xj-u */
2785
2786         /* Calculate Omega.(yj0-u) */
2787         cprod(erg->vec, erg->xr_loc[j], tmpvec);  /* tmpvec = v x Omega.(yj0-u) */
2788
2789         /*                       *         v x Omega.(yj0-u)     */
2790         unitv(tmpvec, pj);      /*  pj = ---------------------   */
2791                                 /*       | v x Omega.(yj0-u) |   */
2792
2793         fac  = iprod(pj, xj_u); /* fac = pj.(xj-u) */
2794         fac2 = fac*fac;
2795
2796         /* Mass-weighting */
2797         wj = N_M*erg->m_loc[j];
2798
2799         /* Store the additional force so that it can be added to the force
2800          * array after the normal forces have been evaluated */
2801         svmul(-erg->rotg->k*wj*fac, pj, tmp_f);
2802         copy_rvec(tmp_f, erg->f_rot_loc[j]);
2803         sum += wj*fac2;
2804
2805         /* If requested, also calculate the potential for a set of angles
2806          * near the current reference angle */
2807         if (bCalcPotFit)
2808         {
2809             for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2810             {
2811                 /* Index of this rotation group atom with respect to the whole rotation group */
2812                 int jj = collectiveRotationGroupIndex[j];
2813
2814                 /* Rotate with the alternative angle. Like rotate_local_reference(),
2815                  * just for a single local atom */
2816                 mvmul(erg->PotAngleFit->rotmat[ifit], erg->rotg->x_ref[jj], fit_tmpvec); /* fit_tmpvec = Omega*(yj0-u) */
2817
2818                 /* Calculate Omega.(yj0-u) */
2819                 cprod(erg->vec, fit_tmpvec, tmpvec); /* tmpvec = v x Omega.(yj0-u) */
2820                 /*                                     *         v x Omega.(yj0-u)     */
2821                 unitv(tmpvec, pj);                   /*  pj = ---------------------   */
2822                                                      /*       | v x Omega.(yj0-u) |   */
2823
2824                 fac  = iprod(pj, xj_u);              /* fac = pj.(xj-u) */
2825                 fac2 = fac*fac;
2826
2827                 /* Add to the rotation potential for this angle: */
2828                 erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*fac2;
2829             }
2830         }
2831
2832         if (bOutstepRot)
2833         {
2834             /* Add to the torque of this rotation group */
2835             erg->torque_v += torque(erg->vec, tmp_f, erg->x_loc_pbc[j], erg->xc_center);
2836
2837             /* Calculate the angle between reference and actual rotation group atom. */
2838             angle(erg, xj_u, erg->xr_loc[j], &alpha, &weight);  /* angle in rad, weighted */
2839             erg->angle_v  += alpha * weight;
2840             erg->weight_v += weight;
2841         }
2842
2843         PRINT_FORCE_J
2844
2845     } /* end of loop over local rotation group atoms */
2846     erg->V = 0.5*erg->rotg->k*sum;
2847 }
2848
2849
2850 /* Calculate the radial motion pivot-free potential and forces */
2851 static void do_radial_motion_pf(
2852         gmx_enfrotgrp  *erg,
2853         rvec            x[],          /* The positions                              */
2854         matrix          box,          /* The simulation box                         */
2855         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
2856         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
2857 {
2858     rvec            xj;               /* Current position */
2859     rvec            xj_xc;            /* xj  - xc  */
2860     rvec            yj0_yc0;          /* yj0 - yc0 */
2861     rvec            tmp_f;            /* Force */
2862     real            alpha;            /* a single angle between an actual and a reference position */
2863     real            weight;           /* single weight for a single angle */
2864     rvec            tmpvec, tmpvec2;
2865     rvec            innersumvec;      /* Precalculation of the inner sum */
2866     rvec            innersumveckM;
2867     real            fac, fac2, V = 0.0;
2868     rvec            qi, qj;
2869     gmx_bool        bCalcPotFit;
2870
2871     /* For mass weighting: */
2872     real      mj, wi, wj;      /* Mass-weighting of the positions */
2873     real      N_M;             /* N/M */
2874
2875     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
2876
2877     N_M = erg->rotg->nat * erg->invmass;
2878
2879     /* Get the current center of the rotation group: */
2880     get_center(erg->xc, erg->mc, erg->rotg->nat, erg->xc_center);
2881
2882     /* Precalculate Sum_i [ wi qi.(xi-xc) qi ] which is needed for every single j */
2883     clear_rvec(innersumvec);
2884     for (int i = 0; i < erg->rotg->nat; i++)
2885     {
2886         /* Mass-weighting */
2887         wi = N_M*erg->mc[i];
2888
2889         /* Calculate qi. Note that xc_ref_center has already been subtracted from
2890          * x_ref in init_rot_group.*/
2891         mvmul(erg->rotmat, erg->rotg->x_ref[i], tmpvec); /* tmpvec  = Omega.(yi0-yc0) */
2892
2893         cprod(erg->vec, tmpvec, tmpvec2);                /* tmpvec2 = v x Omega.(yi0-yc0) */
2894
2895         /*                                             *         v x Omega.(yi0-yc0)     */
2896         unitv(tmpvec2, qi);                           /*  qi = -----------------------   */
2897                                                       /*       | v x Omega.(yi0-yc0) |   */
2898
2899         rvec_sub(erg->xc[i], erg->xc_center, tmpvec); /* tmpvec = xi-xc */
2900
2901         svmul(wi*iprod(qi, tmpvec), qi, tmpvec2);
2902
2903         rvec_inc(innersumvec, tmpvec2);
2904     }
2905     svmul(erg->rotg->k*erg->invmass, innersumvec, innersumveckM);
2906
2907     /* Each process calculates the forces on its local atoms */
2908     const auto &localRotationGroupIndex      = erg->atomSet->localIndex();
2909     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
2910     for (gmx::index j = 0; j < localRotationGroupIndex.ssize(); j++)
2911     {
2912         /* Local index of a rotation group atom  */
2913         int ii = localRotationGroupIndex[j];
2914         /* Position of this atom in the collective array */
2915         int iigrp = collectiveRotationGroupIndex[j];
2916         /* Mass-weighting */
2917         mj = erg->mc[iigrp];  /* need the unsorted mass here */
2918         wj = N_M*mj;
2919
2920         /* Current position of this atom: x[ii][XX/YY/ZZ] */
2921         copy_rvec(x[ii], xj);
2922
2923         /* Shift this atom such that it is near its reference */
2924         shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
2925
2926         /* The (unrotated) reference position is yj0. yc0 has already
2927          * been subtracted in init_rot_group */
2928         copy_rvec(erg->rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0      */
2929
2930         /* Calculate Omega.(yj0-yc0) */
2931         mvmul(erg->rotmat, yj0_yc0, tmpvec2); /* tmpvec2 = Omega.(yj0 - yc0)  */
2932
2933         cprod(erg->vec, tmpvec2, tmpvec);     /* tmpvec = v x Omega.(yj0-yc0) */
2934
2935         /*                     *         v x Omega.(yj0-yc0)     */
2936         unitv(tmpvec, qj);    /*  qj = -----------------------   */
2937                               /*       | v x Omega.(yj0-yc0) |   */
2938
2939         /* Calculate (xj-xc) */
2940         rvec_sub(xj, erg->xc_center, xj_xc); /* xj_xc = xj-xc */
2941
2942         fac  = iprod(qj, xj_xc);             /* fac = qj.(xj-xc) */
2943         fac2 = fac*fac;
2944
2945         /* Store the additional force so that it can be added to the force
2946          * array after the normal forces have been evaluated */
2947         svmul(-erg->rotg->k*wj*fac, qj, tmp_f); /* part 1 of force */
2948         svmul(mj, innersumveckM, tmpvec);       /* part 2 of force */
2949         rvec_inc(tmp_f, tmpvec);
2950         copy_rvec(tmp_f, erg->f_rot_loc[j]);
2951         V += wj*fac2;
2952
2953         /* If requested, also calculate the potential for a set of angles
2954          * near the current reference angle */
2955         if (bCalcPotFit)
2956         {
2957             for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
2958             {
2959                 /* Rotate with the alternative angle. Like rotate_local_reference(),
2960                  * just for a single local atom */
2961                 mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, tmpvec2); /* tmpvec2 = Omega*(yj0-yc0) */
2962
2963                 /* Calculate Omega.(yj0-u) */
2964                 cprod(erg->vec, tmpvec2, tmpvec); /* tmpvec = v x Omega.(yj0-yc0) */
2965                 /*                                  *         v x Omega.(yj0-yc0)     */
2966                 unitv(tmpvec, qj);                /*  qj = -----------------------   */
2967                                                   /*       | v x Omega.(yj0-yc0) |   */
2968
2969                 fac  = iprod(qj, xj_xc);          /* fac = qj.(xj-xc) */
2970                 fac2 = fac*fac;
2971
2972                 /* Add to the rotation potential for this angle: */
2973                 erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*fac2;
2974             }
2975         }
2976
2977         if (bOutstepRot)
2978         {
2979             /* Add to the torque of this rotation group */
2980             erg->torque_v += torque(erg->vec, tmp_f, xj, erg->xc_center);
2981
2982             /* Calculate the angle between reference and actual rotation group atom. */
2983             angle(erg, xj_xc, yj0_yc0, &alpha, &weight);  /* angle in rad, weighted */
2984             erg->angle_v  += alpha * weight;
2985             erg->weight_v += weight;
2986         }
2987
2988         PRINT_FORCE_J
2989
2990     } /* end of loop over local rotation group atoms */
2991     erg->V = 0.5*erg->rotg->k*V;
2992 }
2993
2994
2995 /* Precalculate the inner sum for the radial motion 2 forces */
2996 static void radial_motion2_precalc_inner_sum(const gmx_enfrotgrp *erg,
2997                                              rvec                 innersumvec)
2998 {
2999     int             i;
3000     rvec            xi_xc;     /* xj - xc */
3001     rvec            tmpvec, tmpvec2;
3002     real            fac;
3003     rvec            ri, si;
3004     real            siri;
3005     rvec            v_xi_xc;   /* v x (xj - u) */
3006     real            psii, psiistar;
3007     real            wi;        /* Mass-weighting of the positions */
3008     real            N_M;       /* N/M */
3009     rvec            sumvec;
3010
3011     N_M = erg->rotg->nat * erg->invmass;
3012
3013     /* Loop over the collective set of positions */
3014     clear_rvec(sumvec);
3015     for (i = 0; i < erg->rotg->nat; i++)
3016     {
3017         /* Mass-weighting */
3018         wi = N_M*erg->mc[i];
3019
3020         rvec_sub(erg->xc[i], erg->xc_center, xi_xc); /* xi_xc = xi-xc         */
3021
3022         /* Calculate ri. Note that xc_ref_center has already been subtracted from
3023          * x_ref in init_rot_group.*/
3024         mvmul(erg->rotmat, erg->rotg->x_ref[i], ri); /* ri  = Omega.(yi0-yc0) */
3025
3026         cprod(erg->vec, xi_xc, v_xi_xc);             /* v_xi_xc = v x (xi-u)  */
3027
3028         fac = norm2(v_xi_xc);
3029         /*                                 *                      1           */
3030         psiistar = 1.0/(fac + erg->rotg->eps); /* psiistar = --------------------- */
3031         /*            |v x (xi-xc)|^2 + eps */
3032
3033         psii = gmx::invsqrt(fac);         /*                 1                */
3034                                           /*  psii    = -------------         */
3035                                           /*            |v x (xi-xc)|         */
3036
3037         svmul(psii, v_xi_xc, si);         /*  si = psii * (v x (xi-xc) )     */
3038
3039         siri = iprod(si, ri);             /* siri = si.ri           */
3040
3041         svmul(psiistar/psii, ri, tmpvec);
3042         svmul(psiistar*psiistar/(psii*psii*psii) * siri, si, tmpvec2);
3043         rvec_dec(tmpvec, tmpvec2);
3044         cprod(tmpvec, erg->vec, tmpvec2);
3045
3046         svmul(wi*siri, tmpvec2, tmpvec);
3047
3048         rvec_inc(sumvec, tmpvec);
3049     }
3050     svmul(erg->rotg->k*erg->invmass, sumvec, innersumvec);
3051 }
3052
3053
3054 /* Calculate the radial motion 2 potential and forces */
3055 static void do_radial_motion2(
3056         gmx_enfrotgrp  *erg,
3057         rvec            x[],          /* The positions                              */
3058         matrix          box,          /* The simulation box                         */
3059         gmx_bool        bOutstepRot,  /* Output to main rotation output file        */
3060         gmx_bool        bOutstepSlab) /* Output per-slab data                       */
3061 {
3062     rvec            xj;               /* Position */
3063     real            alpha;            /* a single angle between an actual and a reference position */
3064     real            weight;           /* single weight for a single angle */
3065     rvec            xj_u;             /* xj - u */
3066     rvec            yj0_yc0;          /* yj0 -yc0 */
3067     rvec            tmpvec, tmpvec2;
3068     real            fac, fit_fac, fac2, Vpart = 0.0;
3069     rvec            rj, fit_rj, sj;
3070     real            sjrj;
3071     rvec            v_xj_u;    /* v x (xj - u) */
3072     real            psij, psijstar;
3073     real            mj, wj;    /* For mass-weighting of the positions */
3074     real            N_M;       /* N/M */
3075     gmx_bool        bPF;
3076     rvec            innersumvec;
3077     gmx_bool        bCalcPotFit;
3078
3079     bPF         = erg->rotg->eType == erotgRM2PF;
3080     bCalcPotFit = (bOutstepRot || bOutstepSlab) && (erotgFitPOT == erg->rotg->eFittype);
3081
3082     clear_rvec(yj0_yc0); /* Make the compiler happy */
3083
3084     clear_rvec(innersumvec);
3085     if (bPF)
3086     {
3087         /* For the pivot-free variant we have to use the current center of
3088          * mass of the rotation group instead of the pivot u */
3089         get_center(erg->xc, erg->mc, erg->rotg->nat, erg->xc_center);
3090
3091         /* Also, we precalculate the second term of the forces that is identical
3092          * (up to the weight factor mj) for all forces */
3093         radial_motion2_precalc_inner_sum(erg, innersumvec);
3094     }
3095
3096     N_M = erg->rotg->nat * erg->invmass;
3097
3098     /* Each process calculates the forces on its local atoms */
3099     const auto &localRotationGroupIndex      = erg->atomSet->localIndex();
3100     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
3101     for (gmx::index j = 0; j < localRotationGroupIndex.ssize(); j++)
3102     {
3103         if (bPF)
3104         {
3105             /* Local index of a rotation group atom  */
3106             int ii = localRotationGroupIndex[j];
3107             /* Position of this atom in the collective array */
3108             int iigrp = collectiveRotationGroupIndex[j];
3109             /* Mass-weighting */
3110             mj = erg->mc[iigrp];
3111
3112             /* Current position of this atom: x[ii] */
3113             copy_rvec(x[ii], xj);
3114
3115             /* Shift this atom such that it is near its reference */
3116             shift_single_coord(box, xj, erg->xc_shifts[iigrp]);
3117
3118             /* The (unrotated) reference position is yj0. yc0 has already
3119              * been subtracted in init_rot_group */
3120             copy_rvec(erg->rotg->x_ref[iigrp], yj0_yc0);   /* yj0_yc0 = yj0 - yc0  */
3121
3122             /* Calculate Omega.(yj0-yc0) */
3123             mvmul(erg->rotmat, yj0_yc0, rj);         /* rj = Omega.(yj0-yc0)  */
3124         }
3125         else
3126         {
3127             mj = erg->m_loc[j];
3128             copy_rvec(erg->x_loc_pbc[j], xj);
3129             copy_rvec(erg->xr_loc[j], rj);           /* rj = Omega.(yj0-u)    */
3130         }
3131         /* Mass-weighting */
3132         wj = N_M*mj;
3133
3134         /* Calculate (xj-u) resp. (xj-xc) */
3135         rvec_sub(xj, erg->xc_center, xj_u);         /* xj_u = xj-u           */
3136
3137         cprod(erg->vec, xj_u, v_xj_u);              /* v_xj_u = v x (xj-u)   */
3138
3139         fac = norm2(v_xj_u);
3140         /*                                      *                      1           */
3141         psijstar = 1.0/(fac + erg->rotg->eps); /*  psistar = --------------------  */
3142         /*                                      *            |v x (xj-u)|^2 + eps  */
3143
3144         psij = gmx::invsqrt(fac);         /*                 1                */
3145                                           /*  psij    = ------------          */
3146                                           /*            |v x (xj-u)|          */
3147
3148         svmul(psij, v_xj_u, sj);          /*  sj = psij * (v x (xj-u) )       */
3149
3150         fac  = iprod(v_xj_u, rj);         /* fac = (v x (xj-u)).rj */
3151         fac2 = fac*fac;
3152
3153         sjrj = iprod(sj, rj);                        /* sjrj = sj.rj          */
3154
3155         svmul(psijstar/psij, rj, tmpvec);
3156         svmul(psijstar*psijstar/(psij*psij*psij) * sjrj, sj, tmpvec2);
3157         rvec_dec(tmpvec, tmpvec2);
3158         cprod(tmpvec, erg->vec, tmpvec2);
3159
3160         /* Store the additional force so that it can be added to the force
3161          * array after the normal forces have been evaluated */
3162         svmul(-erg->rotg->k*wj*sjrj, tmpvec2, tmpvec);
3163         svmul(mj, innersumvec, tmpvec2);  /* This is != 0 only for the pivot-free variant */
3164
3165         rvec_add(tmpvec2, tmpvec, erg->f_rot_loc[j]);
3166         Vpart += wj*psijstar*fac2;
3167
3168         /* If requested, also calculate the potential for a set of angles
3169          * near the current reference angle */
3170         if (bCalcPotFit)
3171         {
3172             for (int ifit = 0; ifit < erg->rotg->PotAngle_nstep; ifit++)
3173             {
3174                 if (bPF)
3175                 {
3176                     mvmul(erg->PotAngleFit->rotmat[ifit], yj0_yc0, fit_rj); /* fit_rj = Omega.(yj0-yc0) */
3177                 }
3178                 else
3179                 {
3180                     /* Position of this atom in the collective array */
3181                     int iigrp = collectiveRotationGroupIndex[j];
3182                     /* Rotate with the alternative angle. Like rotate_local_reference(),
3183                      * just for a single local atom */
3184                     mvmul(erg->PotAngleFit->rotmat[ifit], erg->rotg->x_ref[iigrp], fit_rj); /* fit_rj = Omega*(yj0-u) */
3185                 }
3186                 fit_fac = iprod(v_xj_u, fit_rj);                                            /* fac = (v x (xj-u)).fit_rj */
3187                 /* Add to the rotation potential for this angle: */
3188                 erg->PotAngleFit->V[ifit] += 0.5*erg->rotg->k*wj*psijstar*fit_fac*fit_fac;
3189             }
3190         }
3191
3192         if (bOutstepRot)
3193         {
3194             /* Add to the torque of this rotation group */
3195             erg->torque_v += torque(erg->vec, erg->f_rot_loc[j], xj, erg->xc_center);
3196
3197             /* Calculate the angle between reference and actual rotation group atom. */
3198             angle(erg, xj_u, rj, &alpha, &weight);  /* angle in rad, weighted */
3199             erg->angle_v  += alpha * weight;
3200             erg->weight_v += weight;
3201         }
3202
3203         PRINT_FORCE_J
3204
3205     } /* end of loop over local rotation group atoms */
3206     erg->V = 0.5*erg->rotg->k*Vpart;
3207 }
3208
3209
3210 /* Determine the smallest and largest position vector (with respect to the
3211  * rotation vector) for the reference group */
3212 static void get_firstlast_atom_ref(
3213         const gmx_enfrotgrp *erg,
3214         int                 *firstindex,
3215         int                 *lastindex)
3216 {
3217     int             i;
3218     real            xcproj;           /* The projection of a reference position on the
3219                                          rotation vector */
3220     real            minproj, maxproj; /* Smallest and largest projection on v */
3221
3222     /* Start with some value */
3223     minproj = iprod(erg->rotg->x_ref[0], erg->vec);
3224     maxproj = minproj;
3225
3226     /* This is just to ensure that it still works if all the atoms of the
3227      * reference structure are situated in a plane perpendicular to the rotation
3228      * vector */
3229     *firstindex = 0;
3230     *lastindex  = erg->rotg->nat-1;
3231
3232     /* Loop over all atoms of the reference group,
3233      * project them on the rotation vector to find the extremes */
3234     for (i = 0; i < erg->rotg->nat; i++)
3235     {
3236         xcproj = iprod(erg->rotg->x_ref[i], erg->vec);
3237         if (xcproj < minproj)
3238         {
3239             minproj     = xcproj;
3240             *firstindex = i;
3241         }
3242         if (xcproj > maxproj)
3243         {
3244             maxproj    = xcproj;
3245             *lastindex = i;
3246         }
3247     }
3248 }
3249
3250
3251 /* Allocate memory for the slabs */
3252 static void allocate_slabs(
3253         gmx_enfrotgrp *erg,
3254         FILE          *fplog,
3255         gmx_bool       bVerbose)
3256 {
3257     /* More slabs than are defined for the reference are never needed */
3258     int nslabs = erg->slab_last_ref - erg->slab_first_ref + 1;
3259
3260     /* Remember how many we allocated */
3261     erg->nslabs_alloc = nslabs;
3262
3263     if ( (nullptr != fplog) && bVerbose)
3264     {
3265         fprintf(fplog, "%s allocating memory to store data for %d slabs (rotation group %d).\n",
3266                 RotStr, nslabs, erg->groupIndex);
3267     }
3268     snew(erg->slab_center, nslabs);
3269     snew(erg->slab_center_ref, nslabs);
3270     snew(erg->slab_weights, nslabs);
3271     snew(erg->slab_torque_v, nslabs);
3272     snew(erg->slab_data, nslabs);
3273     snew(erg->gn_atom, nslabs);
3274     snew(erg->gn_slabind, nslabs);
3275     snew(erg->slab_innersumvec, nslabs);
3276     for (int i = 0; i < nslabs; i++)
3277     {
3278         snew(erg->slab_data[i].x, erg->rotg->nat);
3279         snew(erg->slab_data[i].ref, erg->rotg->nat);
3280         snew(erg->slab_data[i].weight, erg->rotg->nat);
3281     }
3282     snew(erg->xc_ref_sorted, erg->rotg->nat);
3283     snew(erg->xc_sortind, erg->rotg->nat);
3284     snew(erg->firstatom, nslabs);
3285     snew(erg->lastatom, nslabs);
3286 }
3287
3288
3289 /* From the extreme positions of the reference group, determine the first
3290  * and last slab of the reference. We can never have more slabs in the real
3291  * simulation than calculated here for the reference.
3292  */
3293 static void get_firstlast_slab_ref(gmx_enfrotgrp *erg,
3294                                    real mc[], int ref_firstindex, int ref_lastindex)
3295 {
3296     rvec dummy;
3297
3298     int  first      = get_first_slab(erg, erg->rotg->x_ref[ref_firstindex]);
3299     int  last       = get_last_slab(erg, erg->rotg->x_ref[ref_lastindex ]);
3300
3301     while (get_slab_weight(first, erg, erg->rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
3302     {
3303         first--;
3304     }
3305     erg->slab_first_ref = first+1;
3306     while (get_slab_weight(last, erg, erg->rotg->x_ref, mc, &dummy) > WEIGHT_MIN)
3307     {
3308         last++;
3309     }
3310     erg->slab_last_ref  = last-1;
3311 }
3312
3313
3314 /* Special version of copy_rvec:
3315  * During the copy procedure of xcurr to b, the correct PBC image is chosen
3316  * such that the copied vector ends up near its reference position xref */
3317 static inline void copy_correct_pbc_image(
3318         const rvec   xcurr,  /* copy vector xcurr ...                */
3319         rvec         b,      /* ... to b ...                         */
3320         const rvec   xref,   /* choosing the PBC image such that b ends up near xref */
3321         const matrix box,
3322         int          npbcdim)
3323 {
3324     rvec  dx;
3325     int   d, m;
3326     ivec  shift;
3327
3328
3329     /* Shortest PBC distance between the atom and its reference */
3330     rvec_sub(xcurr, xref, dx);
3331
3332     /* Determine the shift for this atom */
3333     clear_ivec(shift);
3334     for (m = npbcdim-1; m >= 0; m--)
3335     {
3336         while (dx[m] < -0.5*box[m][m])
3337         {
3338             for (d = 0; d < DIM; d++)
3339             {
3340                 dx[d] += box[m][d];
3341             }
3342             shift[m]++;
3343         }
3344         while (dx[m] >= 0.5*box[m][m])
3345         {
3346             for (d = 0; d < DIM; d++)
3347             {
3348                 dx[d] -= box[m][d];
3349             }
3350             shift[m]--;
3351         }
3352     }
3353
3354     /* Apply the shift to the position */
3355     copy_rvec(xcurr, b);
3356     shift_single_coord(box, b, shift);
3357 }
3358
3359
3360 static void init_rot_group(FILE *fplog, const t_commrec *cr,
3361                            gmx_enfrotgrp *erg,
3362                            rvec *x, gmx_mtop_t *mtop, gmx_bool bVerbose, FILE *out_slabs, const matrix box,
3363                            t_inputrec *ir, gmx_bool bOutputCenters)
3364 {
3365     rvec            coord, xref, *xdum;
3366     gmx_bool        bFlex, bColl;
3367     int             ref_firstindex, ref_lastindex;
3368     real            mass, totalmass;
3369     real            start = 0.0;
3370     double          t_start;
3371     const t_rotgrp *rotg = erg->rotg;
3372
3373
3374     /* Do we have a flexible axis? */
3375     bFlex = ISFLEX(rotg);
3376     /* Do we use a global set of coordinates? */
3377     bColl = ISCOLL(rotg);
3378
3379     /* Allocate space for collective coordinates if needed */
3380     if (bColl)
3381     {
3382         snew(erg->xc, erg->rotg->nat);
3383         snew(erg->xc_shifts, erg->rotg->nat);
3384         snew(erg->xc_eshifts, erg->rotg->nat);
3385         snew(erg->xc_old, erg->rotg->nat);
3386
3387         if (erg->rotg->eFittype == erotgFitNORM)
3388         {
3389             snew(erg->xc_ref_length, erg->rotg->nat); /* in case fit type NORM is chosen */
3390             snew(erg->xc_norm, erg->rotg->nat);
3391         }
3392     }
3393     else
3394     {
3395         snew(erg->xr_loc, erg->rotg->nat);
3396         snew(erg->x_loc_pbc, erg->rotg->nat);
3397     }
3398
3399     copy_rvec(erg->rotg->inputVec, erg->vec);
3400     snew(erg->f_rot_loc, erg->rotg->nat);
3401
3402     /* Make space for the calculation of the potential at other angles (used
3403      * for fitting only) */
3404     if (erotgFitPOT == erg->rotg->eFittype)
3405     {
3406         snew(erg->PotAngleFit, 1);
3407         snew(erg->PotAngleFit->degangle, erg->rotg->PotAngle_nstep);
3408         snew(erg->PotAngleFit->V, erg->rotg->PotAngle_nstep);
3409         snew(erg->PotAngleFit->rotmat, erg->rotg->PotAngle_nstep);
3410
3411         /* Get the set of angles around the reference angle */
3412         start = -0.5 * (erg->rotg->PotAngle_nstep - 1)*erg->rotg->PotAngle_step;
3413         for (int i = 0; i < erg->rotg->PotAngle_nstep; i++)
3414         {
3415             erg->PotAngleFit->degangle[i] = start + i*erg->rotg->PotAngle_step;
3416         }
3417     }
3418     else
3419     {
3420         erg->PotAngleFit = nullptr;
3421     }
3422
3423     /* Copy the masses so that the center can be determined. For all types of
3424      * enforced rotation, we store the masses in the erg->mc array. */
3425     snew(erg->mc, erg->rotg->nat);
3426     if (bFlex)
3427     {
3428         snew(erg->mc_sorted, erg->rotg->nat);
3429     }
3430     if (!bColl)
3431     {
3432         snew(erg->m_loc, erg->rotg->nat);
3433     }
3434     totalmass = 0.0;
3435     int molb  = 0;
3436     for (int i = 0; i < erg->rotg->nat; i++)
3437     {
3438         if (erg->rotg->bMassW)
3439         {
3440             mass = mtopGetAtomMass(mtop, erg->rotg->ind[i], &molb);
3441         }
3442         else
3443         {
3444             mass = 1.0;
3445         }
3446         erg->mc[i] = mass;
3447         totalmass += mass;
3448     }
3449     erg->invmass = 1.0/totalmass;
3450
3451     /* Set xc_ref_center for any rotation potential */
3452     if ((erg->rotg->eType == erotgISO) || (erg->rotg->eType == erotgPM) || (erg->rotg->eType == erotgRM) || (erg->rotg->eType == erotgRM2))
3453     {
3454         /* Set the pivot point for the fixed, stationary-axis potentials. This
3455          * won't change during the simulation */
3456         copy_rvec(erg->rotg->pivot, erg->xc_ref_center);
3457         copy_rvec(erg->rotg->pivot, erg->xc_center    );
3458     }
3459     else
3460     {
3461         /* Center of the reference positions */
3462         get_center(erg->rotg->x_ref, erg->mc, erg->rotg->nat, erg->xc_ref_center);
3463
3464         /* Center of the actual positions */
3465         if (MASTER(cr))
3466         {
3467             snew(xdum, erg->rotg->nat);
3468             for (int i = 0; i < erg->rotg->nat; i++)
3469             {
3470                 int ii = erg->rotg->ind[i];
3471                 copy_rvec(x[ii], xdum[i]);
3472             }
3473             get_center(xdum, erg->mc, erg->rotg->nat, erg->xc_center);
3474             sfree(xdum);
3475         }
3476 #if GMX_MPI
3477         if (PAR(cr))
3478         {
3479             gmx_bcast(sizeof(erg->xc_center), erg->xc_center, cr);
3480         }
3481 #endif
3482     }
3483
3484     if (bColl)
3485     {
3486         /* Save the original (whole) set of positions in xc_old such that at later
3487          * steps the rotation group can always be made whole again. If the simulation is
3488          * restarted, we compute the starting reference positions (given the time)
3489          * and assume that the correct PBC image of each position is the one nearest
3490          * to the current reference */
3491         if (MASTER(cr))
3492         {
3493             /* Calculate the rotation matrix for this angle: */
3494             t_start       = ir->init_t + ir->init_step*ir->delta_t;
3495             erg->degangle = erg->rotg->rate * t_start;
3496             calc_rotmat(erg->vec, erg->degangle, erg->rotmat);
3497
3498             for (int i = 0; i < erg->rotg->nat; i++)
3499             {
3500                 int ii = erg->rotg->ind[i];
3501
3502                 /* Subtract pivot, rotate, and add pivot again. This will yield the
3503                  * reference position for time t */
3504                 rvec_sub(erg->rotg->x_ref[i], erg->xc_ref_center, coord);
3505                 mvmul(erg->rotmat, coord, xref);
3506                 rvec_inc(xref, erg->xc_ref_center);
3507
3508                 copy_correct_pbc_image(x[ii], erg->xc_old[i], xref, box, 3);
3509             }
3510         }
3511 #if GMX_MPI
3512         if (PAR(cr))
3513         {
3514             gmx_bcast(erg->rotg->nat*sizeof(erg->xc_old[0]), erg->xc_old, cr);
3515         }
3516 #endif
3517     }
3518
3519     if ( (erg->rotg->eType != erotgFLEX) && (erg->rotg->eType != erotgFLEX2) )
3520     {
3521         /* Put the reference positions into origin: */
3522         for (int i = 0; i < erg->rotg->nat; i++)
3523         {
3524             rvec_dec(erg->rotg->x_ref[i], erg->xc_ref_center);
3525         }
3526     }
3527
3528     /* Enforced rotation with flexible axis */
3529     if (bFlex)
3530     {
3531         /* Calculate maximum beta value from minimum gaussian (performance opt.) */
3532         erg->max_beta = calc_beta_max(erg->rotg->min_gaussian, erg->rotg->slab_dist);
3533
3534         /* Determine the smallest and largest coordinate with respect to the rotation vector */
3535         get_firstlast_atom_ref(erg, &ref_firstindex, &ref_lastindex);
3536
3537         /* From the extreme positions of the reference group, determine the first
3538          * and last slab of the reference. */
3539         get_firstlast_slab_ref(erg, erg->mc, ref_firstindex, ref_lastindex);
3540
3541         /* Allocate memory for the slabs */
3542         allocate_slabs(erg, fplog, bVerbose);
3543
3544         /* Flexible rotation: determine the reference centers for the rest of the simulation */
3545         erg->slab_first = erg->slab_first_ref;
3546         erg->slab_last  = erg->slab_last_ref;
3547         get_slab_centers(erg, erg->rotg->x_ref, erg->mc, -1, out_slabs, bOutputCenters, TRUE);
3548
3549         /* Length of each x_rotref vector from center (needed if fit routine NORM is chosen): */
3550         if (erg->rotg->eFittype == erotgFitNORM)
3551         {
3552             for (int i = 0; i < erg->rotg->nat; i++)
3553             {
3554                 rvec_sub(erg->rotg->x_ref[i], erg->xc_ref_center, coord);
3555                 erg->xc_ref_length[i] = norm(coord);
3556             }
3557         }
3558     }
3559 }
3560
3561 /* Calculate the size of the MPI buffer needed in reduce_output() */
3562 static int calc_mpi_bufsize(const gmx_enfrot *er)
3563
3564 {
3565     int count_total = 0;
3566     for (int g = 0; g < er->rot->ngrp; g++)
3567     {
3568         const t_rotgrp      *rotg = &er->rot->grp[g];
3569         const gmx_enfrotgrp *erg  = &er->enfrotgrp[g];
3570
3571         /* Count the items that are transferred for this group: */
3572         int count_group = 4; /* V, torque, angle, weight */
3573
3574         /* Add the maximum number of slabs for flexible groups */
3575         if (ISFLEX(rotg))
3576         {
3577             count_group += erg->slab_last_ref - erg->slab_first_ref + 1;
3578         }
3579
3580         /* Add space for the potentials at different angles: */
3581         if (erotgFitPOT == erg->rotg->eFittype)
3582         {
3583             count_group += erg->rotg->PotAngle_nstep;
3584         }
3585
3586         /* Add to the total number: */
3587         count_total += count_group;
3588     }
3589
3590     return count_total;
3591 }
3592
3593
3594 std::unique_ptr<gmx::EnforcedRotation>
3595 init_rot(FILE *fplog, t_inputrec *ir, int nfile, const t_filenm fnm[],
3596          const t_commrec *cr, gmx::LocalAtomSetManager * atomSets, const t_state *globalState, gmx_mtop_t *mtop, const gmx_output_env_t *oenv,
3597          const gmx::MdrunOptions &mdrunOptions)
3598 {
3599     int             nat_max = 0;       /* Size of biggest rotation group */
3600     rvec           *x_pbc   = nullptr; /* Space for the pbc-correct atom positions */
3601
3602     if (MASTER(cr) && mdrunOptions.verbose)
3603     {
3604         fprintf(stdout, "%s Initializing ...\n", RotStr);
3605     }
3606
3607     auto        enforcedRotation = std::make_unique<gmx::EnforcedRotation>();
3608     gmx_enfrot *er               = enforcedRotation->getLegacyEnfrot();
3609     // TODO When this module implements IMdpOptions, the ownership will become more clear.
3610     er->rot         = ir->rot;
3611     er->appendFiles = mdrunOptions.continuationOptions.appendFiles;
3612
3613     /* When appending, skip first output to avoid duplicate entries in the data files */
3614     if (er->appendFiles)
3615     {
3616         er->bOut = FALSE;
3617     }
3618     else
3619     {
3620         er->bOut = TRUE;
3621     }
3622
3623     if (MASTER(cr) && er->bOut)
3624     {
3625         please_cite(fplog, "Kutzner2011");
3626     }
3627
3628     /* Output every step for reruns */
3629     if (mdrunOptions.rerun)
3630     {
3631         if (nullptr != fplog)
3632         {
3633             fprintf(fplog, "%s rerun - will write rotation output every available step.\n", RotStr);
3634         }
3635         er->nstrout = 1;
3636         er->nstsout = 1;
3637     }
3638     else
3639     {
3640         er->nstrout = er->rot->nstrout;
3641         er->nstsout = er->rot->nstsout;
3642     }
3643
3644     er->out_slabs = nullptr;
3645     if (MASTER(cr) && HaveFlexibleGroups(er->rot) )
3646     {
3647         er->out_slabs = open_slab_out(opt2fn("-rs", nfile, fnm), er);
3648     }
3649
3650     if (MASTER(cr))
3651     {
3652         /* Remove pbc, make molecule whole.
3653          * When ir->bContinuation=TRUE this has already been done, but ok. */
3654         snew(x_pbc, mtop->natoms);
3655         copy_rvecn(globalState->x.rvec_array(), x_pbc, 0, mtop->natoms);
3656         do_pbc_first_mtop(nullptr, ir->ePBC, globalState->box, mtop, x_pbc);
3657         /* All molecules will be whole now, but not necessarily in the home box.
3658          * Additionally, if a rotation group consists of more than one molecule
3659          * (e.g. two strands of DNA), each one of them can end up in a different
3660          * periodic box. This is taken care of in init_rot_group.  */
3661     }
3662
3663     /* Allocate space for the per-rotation-group data: */
3664     er->enfrotgrp.resize(er->rot->ngrp);
3665     int groupIndex = 0;
3666     for (auto &ergRef : er->enfrotgrp)
3667     {
3668         gmx_enfrotgrp *erg = &ergRef;
3669         erg->rotg       = &er->rot->grp[groupIndex];
3670         erg->atomSet    = std::make_unique<gmx::LocalAtomSet>(atomSets->add({erg->rotg->ind, erg->rotg->ind + erg->rotg->nat}));
3671         erg->groupIndex = groupIndex;
3672
3673         if (nullptr != fplog)
3674         {
3675             fprintf(fplog, "%s group %d type '%s'\n", RotStr, groupIndex, erotg_names[erg->rotg->eType]);
3676         }
3677
3678         if (erg->rotg->nat > 0)
3679         {
3680             nat_max = std::max(nat_max, erg->rotg->nat);
3681
3682             init_rot_group(fplog, cr, erg, x_pbc, mtop, mdrunOptions.verbose, er->out_slabs, MASTER(cr) ? globalState->box : nullptr, ir,
3683                            !er->appendFiles); /* Do not output the reference centers
3684                                                * again if we are appending */
3685         }
3686         ++groupIndex;
3687     }
3688
3689     /* Allocate space for enforced rotation buffer variables */
3690     er->bufsize = nat_max;
3691     snew(er->data, nat_max);
3692     snew(er->xbuf, nat_max);
3693     snew(er->mbuf, nat_max);
3694
3695     /* Buffers for MPI reducing torques, angles, weights (for each group), and V */
3696     if (PAR(cr))
3697     {
3698         er->mpi_bufsize = calc_mpi_bufsize(er) + 100; /* larger to catch errors */
3699         snew(er->mpi_inbuf, er->mpi_bufsize);
3700         snew(er->mpi_outbuf, er->mpi_bufsize);
3701     }
3702     else
3703     {
3704         er->mpi_bufsize = 0;
3705         er->mpi_inbuf   = nullptr;
3706         er->mpi_outbuf  = nullptr;
3707     }
3708
3709     /* Only do I/O on the MASTER */
3710     er->out_angles  = nullptr;
3711     er->out_rot     = nullptr;
3712     er->out_torque  = nullptr;
3713     if (MASTER(cr))
3714     {
3715         er->out_rot = open_rot_out(opt2fn("-ro", nfile, fnm), oenv, er);
3716
3717         if (er->nstsout > 0)
3718         {
3719             if (HaveFlexibleGroups(er->rot) || HavePotFitGroups(er->rot) )
3720             {
3721                 er->out_angles  = open_angles_out(opt2fn("-ra", nfile, fnm), er);
3722             }
3723             if (HaveFlexibleGroups(er->rot) )
3724             {
3725                 er->out_torque  = open_torque_out(opt2fn("-rt", nfile, fnm), er);
3726             }
3727         }
3728
3729         sfree(x_pbc);
3730     }
3731     return enforcedRotation;
3732 }
3733
3734 /* Rotate the local reference positions and store them in
3735  * erg->xr_loc[0...(nat_loc-1)]
3736  *
3737  * Note that we already subtracted u or y_c from the reference positions
3738  * in init_rot_group().
3739  */
3740 static void rotate_local_reference(gmx_enfrotgrp *erg)
3741 {
3742     const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
3743     for (size_t i = 0; i < erg->atomSet->numAtomsLocal(); i++)
3744     {
3745         /* Index of this rotation group atom with respect to the whole rotation group */
3746         int ii = collectiveRotationGroupIndex[i];
3747         /* Rotate */
3748         mvmul(erg->rotmat, erg->rotg->x_ref[ii], erg->xr_loc[i]);
3749     }
3750 }
3751
3752
3753 /* Select the PBC representation for each local x position and store that
3754  * for later usage. We assume the right PBC image of an x is the one nearest to
3755  * its rotated reference */
3756 static void choose_pbc_image(rvec x[],
3757                              gmx_enfrotgrp *erg,
3758                              matrix box, int npbcdim)
3759 {
3760     const auto &localRotationGroupIndex = erg->atomSet->localIndex();
3761     for (gmx::index i = 0; i < localRotationGroupIndex.ssize(); i++)
3762     {
3763         /* Index of a rotation group atom  */
3764         int ii = localRotationGroupIndex[i];
3765
3766         /* Get the correctly rotated reference position. The pivot was already
3767          * subtracted in init_rot_group() from the reference positions. Also,
3768          * the reference positions have already been rotated in
3769          * rotate_local_reference(). For the current reference position we thus
3770          * only need to add the pivot again. */
3771         rvec xref;
3772         copy_rvec(erg->xr_loc[i], xref);
3773         rvec_inc(xref, erg->xc_ref_center);
3774
3775         copy_correct_pbc_image(x[ii], erg->x_loc_pbc[i], xref, box, npbcdim);
3776     }
3777 }
3778
3779
3780 void do_rotation(const t_commrec       *cr,
3781                  gmx_enfrot            *er,
3782                  matrix                 box,
3783                  rvec                   x[],
3784                  real                   t,
3785                  int64_t                step,
3786                  gmx_bool               bNS)
3787 {
3788     gmx_bool        outstep_slab, outstep_rot;
3789     gmx_bool        bColl;
3790     rvec            transvec;
3791     gmx_potfit     *fit = nullptr; /* For fit type 'potential' determine the fit
3792                                         angle via the potential minimum            */
3793
3794 #ifdef TAKETIME
3795     double t0;
3796 #endif
3797
3798     /* When to output in main rotation output file */
3799     outstep_rot  = do_per_step(step, er->nstrout) && er->bOut;
3800     /* When to output per-slab data */
3801     outstep_slab = do_per_step(step, er->nstsout) && er->bOut;
3802
3803     /* Output time into rotation output file */
3804     if (outstep_rot && MASTER(cr))
3805     {
3806         fprintf(er->out_rot, "%12.3e", t);
3807     }
3808
3809     /**************************************************************************/
3810     /* First do ALL the communication! */
3811     for (auto &ergRef : er->enfrotgrp)
3812     {
3813         gmx_enfrotgrp  *erg  = &ergRef;
3814         const t_rotgrp *rotg = erg->rotg;
3815
3816         /* Do we use a collective (global) set of coordinates? */
3817         bColl = ISCOLL(rotg);
3818
3819         /* Calculate the rotation matrix for this angle: */
3820         erg->degangle = rotg->rate * t;
3821         calc_rotmat(erg->vec, erg->degangle, erg->rotmat);
3822
3823         if (bColl)
3824         {
3825             /* Transfer the rotation group's positions such that every node has
3826              * all of them. Every node contributes its local positions x and stores
3827              * it in the collective erg->xc array. */
3828             communicate_group_positions(cr, erg->xc, erg->xc_shifts, erg->xc_eshifts, bNS,
3829                                         x, rotg->nat, erg->atomSet->numAtomsLocal(), erg->atomSet->localIndex().data(), erg->atomSet->collectiveIndex().data(), erg->xc_old, box);
3830         }
3831         else
3832         {
3833             /* Fill the local masses array;
3834              * this array changes in DD/neighborsearching steps */
3835             if (bNS)
3836             {
3837                 const auto &collectiveRotationGroupIndex = erg->atomSet->collectiveIndex();
3838                 for (gmx::index i = 0; i < collectiveRotationGroupIndex.ssize(); i++)
3839                 {
3840                     /* Index of local atom w.r.t. the collective rotation group */
3841                     int ii        = collectiveRotationGroupIndex[i];
3842                     erg->m_loc[i] = erg->mc[ii];
3843                 }
3844             }
3845
3846             /* Calculate Omega*(y_i-y_c) for the local positions */
3847             rotate_local_reference(erg);
3848
3849             /* Choose the nearest PBC images of the group atoms with respect
3850              * to the rotated reference positions */
3851             choose_pbc_image(x, erg, box, 3);
3852
3853             /* Get the center of the rotation group */
3854             if ( (rotg->eType == erotgISOPF) || (rotg->eType == erotgPMPF) )
3855             {
3856                 get_center_comm(cr, erg->x_loc_pbc, erg->m_loc, erg->atomSet->numAtomsLocal(), rotg->nat, erg->xc_center);
3857             }
3858         }
3859
3860     } /* End of loop over rotation groups */
3861
3862     /**************************************************************************/
3863     /* Done communicating, we can start to count cycles for the load balancing now ... */
3864     if (DOMAINDECOMP(cr))
3865     {
3866         ddReopenBalanceRegionCpu(cr->dd);
3867     }
3868
3869 #ifdef TAKETIME
3870     t0 = MPI_Wtime();
3871 #endif
3872
3873     for (auto &ergRef : er->enfrotgrp)
3874     {
3875         gmx_enfrotgrp  *erg  = &ergRef;
3876         const t_rotgrp *rotg = erg->rotg;
3877
3878         if (outstep_rot && MASTER(cr))
3879         {
3880             fprintf(er->out_rot, "%12.4f", erg->degangle);
3881         }
3882
3883         /* Calculate angles and rotation matrices for potential fitting: */
3884         if ( (outstep_rot || outstep_slab) && (erotgFitPOT == rotg->eFittype) )
3885         {
3886             fit = erg->PotAngleFit;
3887             for (int i = 0; i < rotg->PotAngle_nstep; i++)
3888             {
3889                 calc_rotmat(erg->vec, erg->degangle + fit->degangle[i], fit->rotmat[i]);
3890
3891                 /* Clear value from last step */
3892                 erg->PotAngleFit->V[i] = 0.0;
3893             }
3894         }
3895
3896         /* Clear values from last time step */
3897         erg->V        = 0.0;
3898         erg->torque_v = 0.0;
3899         erg->angle_v  = 0.0;
3900         erg->weight_v = 0.0;
3901
3902         switch (rotg->eType)
3903         {
3904             case erotgISO:
3905             case erotgISOPF:
3906             case erotgPM:
3907             case erotgPMPF:
3908                 do_fixed(erg, outstep_rot, outstep_slab);
3909                 break;
3910             case erotgRM:
3911                 do_radial_motion(erg, outstep_rot, outstep_slab);
3912                 break;
3913             case erotgRMPF:
3914                 do_radial_motion_pf(erg, x, box, outstep_rot, outstep_slab);
3915                 break;
3916             case erotgRM2:
3917             case erotgRM2PF:
3918                 do_radial_motion2(erg, x, box, outstep_rot, outstep_slab);
3919                 break;
3920             case erotgFLEXT:
3921             case erotgFLEX2T:
3922                 /* Subtract the center of the rotation group from the collective positions array
3923                  * Also store the center in erg->xc_center since it needs to be subtracted
3924                  * in the low level routines from the local coordinates as well */
3925                 get_center(erg->xc, erg->mc, rotg->nat, erg->xc_center);
3926                 svmul(-1.0, erg->xc_center, transvec);
3927                 translate_x(erg->xc, rotg->nat, transvec);
3928                 do_flexible(MASTER(cr), er, erg, x, box, t, outstep_rot, outstep_slab);
3929                 break;
3930             case erotgFLEX:
3931             case erotgFLEX2:
3932                 /* Do NOT subtract the center of mass in the low level routines! */
3933                 clear_rvec(erg->xc_center);
3934                 do_flexible(MASTER(cr), er, erg, x, box, t, outstep_rot, outstep_slab);
3935                 break;
3936             default:
3937                 gmx_fatal(FARGS, "No such rotation potential.");
3938         }
3939     }
3940
3941 #ifdef TAKETIME
3942     if (MASTER(cr))
3943     {
3944         fprintf(stderr, "%s calculation (step %d) took %g seconds.\n", RotStr, step, MPI_Wtime()-t0);
3945     }
3946 #endif
3947 }