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