LCOV - code coverage report
Current view: top level - membranefusion - FusionPoreNucleationP.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 184 208 88.5 %
Date: 2024-10-18 14:00:25 Functions: 3 4 75.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             : Copyright (c) 2022.
       3             : 
       4             : CVs originally developed by the Jochen Hub group from the University of Saarland (Germany)
       5             : and adapted and implemented in PLUMED by Ary Lautaro Di Bartolo and Diego Masone from the
       6             : National University of Cuyo (Argentina).
       7             : 
       8             : The membranefusion module is free software: you can redistribute it and/or modify
       9             : it under the terms of the GNU Lesser General Public License as published by
      10             : the Free Software Foundation, either version 3 of the License, or
      11             : (at your option) any later version.
      12             : 
      13             : The membranefusion module is distributed in the hope that it will be useful,
      14             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             : GNU Lesser General Public License for more details.
      17             : 
      18             : You should have received a copy of the GNU Lesser General Public License
      19             : along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      20             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      21             : #include "colvar/Colvar.h"
      22             : #include "core/ActionRegister.h"
      23             : #include <cmath>
      24             : #ifdef _OPENMP
      25             : #if _OPENMP >= 201307
      26             : #include <omp.h>
      27             : #endif
      28             : #endif
      29             : 
      30             : namespace PLMD
      31             : {
      32             : namespace membranefusion
      33             : {
      34             : //+PLUMEDOC MEMBRANEFUSIONMOD_COLVAR FUSIONPORENUCLEATIONP
      35             : /*
      36             : A CV for inducing the nucleation of the fusion pore from a hemifusion stalk.
      37             : 
      38             : Calculate the collective variable designed by Hub and collaborators \cite Hub2017 and
      39             : implemented into PLUMED by Masone and collaborators.
      40             : This CV is capable of inducing the nucleation of the fusion pore from a hemifusion stalk.
      41             : 
      42             : \f[
      43             : \xi_n = \frac{1}{N_{sn}} \sum_{s=0}^{N_{sn}-1} \delta_{sn} (N_{sn}^{(p)})
      44             : \f]
      45             : 
      46             : Where \f$\xi_n\f$ is the CV, \f$N_{sn}\f$ is the number of slices of the cylinder that make up the CV,
      47             : \f$\delta_{sn}\f$ is a continuos function in the interval [0 1] (\f$\delta_{sf} = 0\f$ for no beads in the slice s, and
      48             : \f$\delta_{sf} = 1\f$ for 1 or more beads in the slice s) and \f$N_{sf}^{(p)}\f$ accounts for the number of water and
      49             : phosphateoxygens beads within the slice s.
      50             : 
      51             : \par Examples
      52             : 
      53             : This example induces the nucleation of the fusion pore (\f$\xi_n = 1.0\f$) from a hemifusion stalk (\f$\xi_n = 0.2\f$).
      54             : 
      55             : \plumedfile
      56             : 
      57             : lMem: GROUP ATOMS=1-10752,21505-22728,23953-24420 #All the lower membrane beads.
      58             : uMem: GROUP ATOMS=10753-21504,22729-23952,24421-24888 #All the upper membrane beads.
      59             : tails: GROUP ATOMS=8-23948:12,12-23952:12,23966-24884:18,23970-24888:18 #All the lipid tails beads (from the lower and upper membrane).
      60             : waters: GROUP ATOMS=24889-56490 #All the water beads.
      61             : po4: GROUP ATOMS=2-23942:12,23957-24875:18 #All the lipid phosphateoxygens beads.
      62             : 
      63             : fusionPoreNucleation: FUSIONPORENUCLEATIONP UMEMBRANE=uMem LMEMBRANE=lMem TAILS=tails WATERS=waters PHOSPHATEOXYGENS=po4 NSMEM=85 NS=45
      64             : 
      65             : MOVINGRESTRAINT ...
      66             :     ARG=fusionPoreNucleation
      67             :     STEP0=0 AT0=0.2 KAPPA0=10000.0
      68             :     STEP1=500000 AT1=1.0 KAPPA1=10000.0
      69             : ...
      70             : 
      71             : PRINT ARG=fusionPoreNucleation FILE=COLVAR STRIDE=1
      72             : 
      73             : \endplumedfile
      74             : 
      75             : */
      76             : //+ENDPLUMEDOC
      77             : class fusionPoreNucleationP : public Colvar
      78             : {
      79             :   std::vector<AtomNumber> UMEM, LMEM, TAILS, WATERS, POXYGENS;
      80             :   std::vector<double> NSMEM, DSMEM, HMEM, NS, DS, HCH, RCYL, ZETA, ONEOVERS2C2CUTOFF, XCYL, YCYL;
      81             : 
      82             : public:
      83             :   explicit fusionPoreNucleationP(const ActionOptions &);
      84             :   void calculate() override;
      85             :   static void registerKeywords(Keywords &keys);
      86             : };
      87             : 
      88             : PLUMED_REGISTER_ACTION(fusionPoreNucleationP, "FUSIONPORENUCLEATIONP")
      89             : 
      90           3 : void fusionPoreNucleationP::registerKeywords(Keywords &keys)
      91             : {
      92           3 :   Colvar::registerKeywords(keys);
      93           6 :   keys.add("atoms", "UMEMBRANE", "all the beads of the upper membrane.");
      94           6 :   keys.add("atoms", "LMEMBRANE", "all the beads of the lower membrane.");
      95           6 :   keys.add("atoms", "TAILS", "all the tail beads of the system.");
      96           6 :   keys.add("atoms", "WATERS", "all the water beads of the system.");
      97           6 :   keys.add("atoms", "PHOSPHATEOXYGENS", "all the lipid phosphateoxygens beads of the system.");
      98           6 :   keys.add("compulsory", "NSMEM", "the number of slices of the membrane fusion cylinder.");
      99           6 :   keys.add("optional", "DSMEM", "( default=0.1 ) thickness of the slices of the membrane fusion cylinder.");
     100           6 :   keys.add("optional", "HMEM", "( default=0.25 ) parameter of the step function θ(x,h) for the membrane fusion.");
     101           6 :   keys.add("compulsory", "NS", "the number of slices of the membrane-spanning cylinder in such a way that when the bilayers are flat and parallel the CV is equal to 0.2.");
     102           6 :   keys.add("optional", "DS", "( default=0.25 ) thickness of the slices of the membrane-spanning cylinder.");
     103           6 :   keys.add("optional", "HCH", "( default=0.25 ) parameter of the step function θ(x,h) for the CV.");
     104           6 :   keys.add("optional", "RCYL", "( default=0.8 ) the radius of the membrane-spanning cylinder.");
     105           6 :   keys.add("optional", "ZETA", "( default=0.75 ) parameter of the switch function ψ(x,ζ).");
     106           6 :   keys.add("optional", "ONEOVERS2C2CUTOFF", "( default=500 ) cut off large values for the derivative of the atan2 function to avoid violate energy.");
     107           6 :   keys.add("optional", "XCYL", "X coordinate of the fixed cylinder, if not present this will be calculated.");
     108           6 :   keys.add("optional", "YCYL", "X coordinate of the fixed cylinder, if not present this will be calculated.");
     109           3 :   keys.setValueDescription("the value of the CV");
     110           3 : }
     111             : 
     112           1 : fusionPoreNucleationP::fusionPoreNucleationP(const ActionOptions &ao) : PLUMED_COLVAR_INIT(ao)
     113             : {
     114           2 :   parseAtomList("UMEMBRANE", UMEM);
     115           1 :   if (UMEM.size() == 0)
     116           0 :     error("UMEMBRANE has not any atom specified.");
     117             : 
     118           2 :   parseAtomList("LMEMBRANE", LMEM);
     119           1 :   if (LMEM.size() == 0)
     120           0 :     error("LMEMBRANE has not any atom specified.");
     121             : 
     122           2 :   parseAtomList("TAILS", TAILS);
     123           1 :   if (TAILS.size() == 0)
     124           0 :     error("TAILS has not any atom specified.");
     125             : 
     126           2 :   parseAtomList("WATERS", WATERS);
     127           1 :   if (WATERS.size() == 0)
     128           0 :     error("WATERS has not any atom specified.");
     129             : 
     130           2 :   parseAtomList("PHOSPHATEOXYGENS", POXYGENS);
     131           1 :   if (POXYGENS.size() == 0)
     132           0 :     error("PHOSPHATEOXYGENS has not any atom specified.");
     133             : 
     134           2 :   parseVector("NSMEM", NSMEM);
     135           1 :   if (NSMEM.size() > 1)
     136           0 :     error("NSMEM cannot take more than one value.");
     137             : 
     138           2 :   parseVector("DSMEM", DSMEM);
     139           1 :   if (DSMEM.size() > 1)
     140           0 :     error("DSMEM cannot take more than one value.");
     141           1 :   if (DSMEM.size() == 0)
     142           0 :     DSMEM.push_back(0.1);
     143             : 
     144           2 :   parseVector("HMEM", HMEM);
     145           1 :   if (HMEM.size() > 1)
     146           0 :     error("HMEM cannot take more than one value.");
     147           1 :   if (HMEM.size() == 0)
     148           0 :     HMEM.push_back(0.25);
     149             : 
     150           2 :   parseVector("NS", NS);
     151           1 :   if (NS.size() > 1)
     152           0 :     error("NS cannot take more than one value.");
     153             : 
     154           2 :   parseVector("DS", DS);
     155           1 :   if (DS.size() > 1)
     156           0 :     error("DS cannot take more than one value.");
     157           1 :   if (DS.size() == 0)
     158           0 :     DS.push_back(0.25);
     159             : 
     160           2 :   parseVector("HCH", HCH);
     161           1 :   if (HCH.size() > 1)
     162           0 :     error("H cannot take more than one value.");
     163           1 :   if (HCH.size() == 0)
     164           0 :     HCH.push_back(0.25);
     165             : 
     166           2 :   parseVector("RCYL", RCYL);
     167           1 :   if (RCYL.size() > 1)
     168           0 :     error("RCYL cannot take more than one value.");
     169           1 :   if (RCYL.size() == 0)
     170           0 :     RCYL.push_back(0.8);
     171             : 
     172           2 :   parseVector("ZETA", ZETA);
     173           1 :   if (ZETA.size() > 1)
     174           0 :     error("ZETA cannot take more than one value.");
     175           1 :   if (ZETA.size() == 0)
     176           0 :     ZETA.push_back(0.75);
     177             : 
     178           2 :   parseVector("ONEOVERS2C2CUTOFF", ONEOVERS2C2CUTOFF);
     179           1 :   if (ONEOVERS2C2CUTOFF.size() > 1)
     180           0 :     error("ONEOVERS2C2CUTOFF cannot take more than one value.");
     181           1 :   if (ONEOVERS2C2CUTOFF.size() == 0)
     182           1 :     ONEOVERS2C2CUTOFF.push_back(500);
     183             : 
     184           2 :   parseVector("XCYL", XCYL);
     185           1 :   if (XCYL.size() > 1)
     186           0 :     error("XCYL cannot take more than one value.");
     187           1 :   if (XCYL.size() == 0)
     188           1 :     XCYL.push_back(-1.0);
     189             : 
     190           2 :   parseVector("YCYL", YCYL);
     191           1 :   if (YCYL.size() > 1)
     192           0 :     error("YCYL cannot take more than one value.");
     193           1 :   if (YCYL.size() == 0)
     194           1 :     YCYL.push_back(-1.0);
     195             : 
     196           1 :   checkRead();
     197             : 
     198             :   std::vector<AtomNumber> atoms;
     199       12445 :   for (unsigned i = 0; i < UMEM.size(); i++)
     200             :   {
     201       12444 :     atoms.push_back(UMEM[i]);
     202             :   }
     203       12445 :   for (unsigned i = 0; i < LMEM.size(); i++)
     204             :   {
     205       12444 :     atoms.push_back(LMEM[i]);
     206             :   }
     207        4097 :   for (unsigned i = 0; i < TAILS.size(); i++)
     208             :   {
     209        4096 :     atoms.push_back(TAILS[i]);
     210             :   }
     211       31603 :   for (unsigned i = 0; i < WATERS.size(); i++)
     212             :   {
     213       31602 :     atoms.push_back(WATERS[i]);
     214             :   }
     215        2049 :   for (unsigned i = 0; i < POXYGENS.size(); i++)
     216             :   {
     217        2048 :     atoms.push_back(POXYGENS[i]);
     218             :   }
     219             : 
     220           1 :   addValueWithDerivatives();
     221           1 :   setNotPeriodic();
     222           1 :   requestAtoms(atoms);
     223           1 : }
     224             : 
     225           4 : void fusionPoreNucleationP::calculate()
     226             : {
     227             :   /*************************
     228             :   *                        *
     229             :   *         System         *
     230             :   *                        *
     231             :   **************************/
     232             : 
     233             :   // Box dimensions.
     234           4 :   double Lx = getBox()[0][0], Ly = getBox()[1][1], Lz = getBox()[2][2];
     235             : 
     236             :   // Z center of the upper membrane (uMem) and lower membrane (lMem) for systems with PBC: https://en.wikipedia.org/wiki/Center_of_mass#Systems_with_periodic_boundary_conditions .
     237             :   double ZuMem, ZuMemcos = 0.0, ZuMemsin = 0.0, uMemAngle, ZlMem, ZlMemcos = 0.0, ZlMemsin = 0.0, lMemAngle;
     238             : 
     239             : #ifdef _OPENMP
     240             : #if _OPENMP >= 201307
     241           4 :   #pragma omp parallel for private(uMemAngle, lMemAngle) reduction(+:ZuMemcos, ZuMemsin, ZlMemcos, ZlMemsin)
     242             : #endif
     243             : #endif
     244             :   for (unsigned i = 0; i < UMEM.size(); i++)
     245             :   {
     246             :     uMemAngle = 2.0 * M_PI * getPbc().realToScaled(pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i)))[2];
     247             :     lMemAngle = 2.0 * M_PI * getPbc().realToScaled(pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + UMEM.size())))[2];
     248             :     ZuMemcos += cos(uMemAngle);
     249             :     ZuMemsin += sin(uMemAngle);
     250             :     ZlMemcos += cos(lMemAngle);
     251             :     ZlMemsin += sin(lMemAngle);
     252             :   }
     253           4 :   ZuMemcos = ZuMemcos / UMEM.size();
     254           4 :   ZuMemsin = ZuMemsin / UMEM.size();
     255           4 :   ZuMem = Lz * (atan2(-ZuMemsin, -ZuMemcos) + M_PI) / (2.0 * M_PI);
     256           4 :   ZlMemcos = ZlMemcos / UMEM.size();
     257           4 :   ZlMemsin = ZlMemsin / UMEM.size();
     258           4 :   ZlMem = Lz * (atan2(-ZlMemsin, -ZlMemcos) + M_PI) / (2.0 * M_PI);
     259             : 
     260             :   // Z center of the boths membranes (upper and lower).
     261           4 :   double ZMems = (ZuMem + ZlMem) / 2.0;
     262             : 
     263             :   /**************************
     264             :   *                         *
     265             :   *   Xcyl_Mem & Ycyl_Mem   *
     266             :   *                         *
     267             :   ***************************/
     268             : 
     269             :   // Quantity of beads of the membranes.
     270           4 :   unsigned membraneBeads = UMEM.size() + LMEM.size();
     271             : 
     272             :   // Z distance from the lipid tail to the geometric center of both membranes.
     273             :   double ZTailDistance;
     274             : 
     275             :   // Z position of the first slice.
     276           4 :   double firstSliceZ_Mem = ZMems + (0.0 + 0.5 - NSMEM[0] / 2.0) * DSMEM[0];
     277             : 
     278             :   // Z distance between the first slice and the Z center of the membrane.
     279           8 :   double firstSliceZDist_Mem = pbcDistance(Vector(0.0, 0.0, firstSliceZ_Mem), Vector(0.0, 0.0, ZMems))[2];
     280             : 
     281             :   // Position in the cylinder.
     282             :   double PositionS_Mem;
     283             : 
     284             :   // Slices to analyze per particle.
     285             :   unsigned s1_Mem, s2_Mem;
     286             : 
     287             :   // Eq. 7 Hub & Awasthi JCTC 2017.
     288           4 :   std::vector<double> faxial_Mem(TAILS.size() * NSMEM[0]);
     289             : 
     290             :   // Eq. 10 Hub & Awasthi JCTC 2017.
     291           4 :   std::vector<double> Fs_Mem(NSMEM[0]);
     292             : 
     293             :   // Eq. 11 Hub & Awasthi JCTC 2017.
     294           4 :   std::vector<double> ws_Mem(NSMEM[0]);
     295             : 
     296             :   // Eq. 10 Hub & Awasthi JCTC 2017.
     297             :   double W_Mem = 0.0;
     298             : 
     299             :   // Eq. 21 and 22 Hub & Awasthi JCTC 2017.
     300           4 :   std::vector<double> sx_Mem(NSMEM[0]), sy_Mem(NSMEM[0]), cx_Mem(NSMEM[0]), cy_Mem(NSMEM[0]);
     301             : 
     302             :   // Eq. 10 Hub & Awasthi JCTC 2017.
     303             :   double Xsc_Mem = 0.0, Xcc_Mem = 0.0, Ysc_Mem = 0.0, Ycc_Mem = 0.0;
     304             : 
     305             :   // Aux.
     306             :   double x, aux;
     307             : 
     308             :   // Scaled position of the lipid tail respect the origin of coordinates.
     309           4 :   Vector TailPosition;
     310             : 
     311             : #ifdef _OPENMP
     312             : #if _OPENMP >= 201307
     313             :   #pragma omp declare reduction(vec_double_plus : std::vector<double> : \
     314             :   std::transform(omp_out.begin(), omp_out.end(), omp_in.begin(), omp_out.begin(), std::plus<double>())) \
     315             :   initializer(omp_priv = decltype(omp_orig)(omp_orig.size()))
     316             : #endif
     317             : #endif
     318             : 
     319             : #ifdef _OPENMP
     320             : #if _OPENMP >= 201307
     321           4 :   #pragma omp parallel for private(ZTailDistance, PositionS_Mem, s1_Mem, s2_Mem, TailPosition, x, aux) reduction(vec_double_plus:Fs_Mem, sx_Mem, sy_Mem, cx_Mem, cy_Mem)
     322             : #endif
     323             : #endif
     324             :   for (unsigned i = 0; i < TAILS.size(); i++)
     325             :   {
     326             :     ZTailDistance = pbcDistance(Vector(0.0, 0.0, ZMems), getPosition(i + membraneBeads))[2];
     327             :     PositionS_Mem = (ZTailDistance + firstSliceZDist_Mem) / DSMEM[0];
     328             :     // If the following condition is met the particle is in the Z space of the cylinder.
     329             :     if ((PositionS_Mem >= (-0.5 - HMEM[0])) && (PositionS_Mem <= (NSMEM[0] + 0.5 - 1.0 + HMEM[0])))
     330             :     {
     331             :       //Defining the slices to analyze each particle.
     332             :       if (PositionS_Mem < 1)
     333             :       {
     334             :         s1_Mem = 0;
     335             :         s2_Mem = 2;
     336             :       }
     337             :       else if (PositionS_Mem <= (NSMEM[0] - 2.0))
     338             :       {
     339             :         s1_Mem = floor(PositionS_Mem) - 1;
     340             :         s2_Mem = floor(PositionS_Mem) + 1;
     341             :       }
     342             :       else
     343             :       {
     344             :         s1_Mem = NSMEM[0] - 3;
     345             :         s2_Mem = NSMEM[0] - 1;
     346             :       }
     347             : 
     348             :       TailPosition = getPbc().realToScaled(pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + membraneBeads)));
     349             : 
     350             :       for (unsigned s = s1_Mem; s <= s2_Mem; s++)
     351             :       {
     352             :         x = (ZTailDistance - (s + 0.5 - NSMEM[0] / 2.0) * DSMEM[0]) * 2.0 / DSMEM[0];
     353             :         if (!((x <= -1.0 - HMEM[0]) || (x >= 1.0 + HMEM[0])))
     354             :         {
     355             :           if (((-1.0 + HMEM[0]) <= x) && (x <= (1.0 - HMEM[0])))
     356             :           {
     357             :             faxial_Mem[i + TAILS.size() * s] = 1.0;
     358             :             Fs_Mem[s] += 1.0;
     359             :             sx_Mem[s] += sin(2.0 * M_PI * TailPosition[0]);
     360             :             sy_Mem[s] += sin(2.0 * M_PI * TailPosition[1]);
     361             :             cx_Mem[s] += cos(2.0 * M_PI * TailPosition[0]);
     362             :             cy_Mem[s] += cos(2.0 * M_PI * TailPosition[1]);
     363             :           }
     364             :           else if (((1.0 - HMEM[0]) < x) && (x < (1.0 + HMEM[0])))
     365             :           {
     366             :             aux = 0.5 - ((3.0 * x - 3.0) / (4.0 * HMEM[0])) + (pow((x - 1.0), 3) / (4.0 * pow(HMEM[0], 3)));
     367             :             faxial_Mem[i + TAILS.size() * s] = aux;
     368             :             Fs_Mem[s] += aux;
     369             :             sx_Mem[s] += aux * sin(2.0 * M_PI * TailPosition[0]);
     370             :             sy_Mem[s] += aux * sin(2.0 * M_PI * TailPosition[1]);
     371             :             cx_Mem[s] += aux * cos(2.0 * M_PI * TailPosition[0]);
     372             :             cy_Mem[s] += aux * cos(2.0 * M_PI * TailPosition[1]);
     373             :           }
     374             :           else if (((-1.0 - HMEM[0]) < x) && (x < (-1.0 + HMEM[0])))
     375             :           {
     376             :             aux = 0.5 + ((3.0 * x + 3.0) / (4.0 * HMEM[0])) - (pow((x + 1.0), 3) / (4.0 * pow(HMEM[0], 3)));
     377             :             faxial_Mem[i + TAILS.size() * s] = aux;
     378             :             Fs_Mem[s] += aux;
     379             :             sx_Mem[s] += (aux * sin(2.0 * M_PI * TailPosition[0]));
     380             :             sy_Mem[s] += (aux * sin(2.0 * M_PI * TailPosition[1]));
     381             :             cx_Mem[s] += (aux * cos(2.0 * M_PI * TailPosition[0]));
     382             :             cy_Mem[s] += (aux * cos(2.0 * M_PI * TailPosition[1]));
     383             :           }
     384             :         }
     385             :       }
     386             :     }
     387             :   }
     388             : 
     389         344 :   for (unsigned s = 0; s < NSMEM[0]; s++)
     390             :   {
     391         340 :     if (Fs_Mem[s] != 0.0)
     392             :     {
     393         339 :       ws_Mem[s] = tanh(Fs_Mem[s]);
     394         339 :       W_Mem += ws_Mem[s];
     395         339 :       sx_Mem[s] = sx_Mem[s] / Fs_Mem[s];
     396         339 :       sy_Mem[s] = sy_Mem[s] / Fs_Mem[s];
     397         339 :       cx_Mem[s] = cx_Mem[s] / Fs_Mem[s];
     398         339 :       cy_Mem[s] = cy_Mem[s] / Fs_Mem[s];
     399         339 :       Xsc_Mem += sx_Mem[s] * ws_Mem[s];
     400         339 :       Ysc_Mem += sy_Mem[s] * ws_Mem[s];
     401         339 :       Xcc_Mem += cx_Mem[s] * ws_Mem[s];
     402         339 :       Ycc_Mem += cy_Mem[s] * ws_Mem[s];
     403             :     }
     404             :   }
     405             : 
     406           4 :   Xsc_Mem = Xsc_Mem / W_Mem;
     407           4 :   Ysc_Mem = Ysc_Mem / W_Mem;
     408           4 :   Xcc_Mem = Xcc_Mem / W_Mem;
     409           4 :   Ycc_Mem = Ycc_Mem / W_Mem;
     410             : 
     411             :   // Eq. 12 Hub & Awasthi JCTC 2017.
     412             :   double Xcyl_Mem, Ycyl_Mem;
     413             : 
     414           4 :   if ((XCYL[0] > 0.0) && (YCYL[0] > 0.0))
     415             :   {
     416             :     Xcyl_Mem = XCYL[0];
     417             :     Ycyl_Mem = YCYL[0];
     418             :   }
     419             :   else
     420             :   {
     421           4 :     Xcyl_Mem = (atan2(-Xsc_Mem, -Xcc_Mem) + M_PI) * Lx / (2 * M_PI);
     422           4 :     Ycyl_Mem = (atan2(-Ysc_Mem, -Ycc_Mem) + M_PI) * Ly / (2 * M_PI);
     423             :   }
     424             : 
     425             :   /*************************
     426             :   *                        *
     427             :   *        Xi_n            *
     428             :   *                        *
     429             :   **************************/
     430             : 
     431             :   // Eq. 1 Hub & Awasthi JCTC 2017. This is the CV that describes de Pore Nucleation.
     432           4 :   double Xi_n = 0.0;
     433             : 
     434             :   // Quantity of beads that could participate in the calculation of the Xi_Chain
     435           4 :   unsigned chainBeads = WATERS.size() + POXYGENS.size();
     436             : 
     437             :   // Quantity of beads that don't participate in the calculation of the Xi_Chain
     438           4 :   unsigned noChainBeads = (UMEM.size() * 2) + TAILS.size();
     439             : 
     440             :   // Z Distances from the oxygens to the geometric center of the membranes.
     441             :   double ZMemDistance;
     442             : 
     443             :   // Scaled positions of the oxygens to respect of the origin of coordinates.
     444           4 :   Vector Position;
     445             : 
     446             :   // Distance from the water/phosphate group to the defect cylinder.
     447           4 :   Vector distCylinder;
     448             : 
     449             :   // Center of the cylinder. XY components are calculated (or defined), Z is the Z geometric center of the membranes of the system.
     450           8 :   Vector xyzCyl_Mem = pbcDistance(Vector(0.0, 0.0, 0.0), Vector(Xcyl_Mem, Ycyl_Mem, ZMems));
     451             : 
     452             :   // Average of the radius of the water and lipid cylinder.
     453           4 :   double RCYLAVERAGE = RCYL[0] * (1 + HCH[0]);
     454             : 
     455             :   // Conditions.
     456             :   bool condition1, condition2, condition3;
     457             : 
     458             :   // Z position of the first slice.
     459           4 :   double firstSliceZ = ZMems + (0.0 + 0.5 - NS[0] / 2.0) * DS[0];
     460             : 
     461             :   // Z distance between the first slice and the Z center of the membrane.
     462           4 :   double firstSliceZDist = pbcDistance(Vector(0.0, 0.0, firstSliceZ), Vector(0.0, 0.0, ZMems))[2];
     463             : 
     464             :   // Position in the cylinder.
     465             :   double PositionS;
     466             : 
     467             :   // Mark the particles to analyze.
     468           4 :   std::vector<double> analyzeThisParticle(chainBeads);
     469             : 
     470             :   // Slices to analyze per particle.
     471           4 :   std::vector<unsigned> s1(chainBeads), s2(chainBeads);
     472             : 
     473             :   // Eq. 7 Hub & Awasthi JCTC 2017.
     474           4 :   std::vector<double> faxial(chainBeads * NS[0]);
     475             : 
     476             :   // Eq. 16 Hub & Awasthi JCTC 2017.
     477           4 :   std::vector<double> d_faxial_dz(chainBeads * NS[0]);
     478             : 
     479             :   // Eq. 10 Hub & Awasthi JCTC 2017.
     480           4 :   std::vector<double> Fs(NS[0]);
     481             : 
     482             :   // Eq. 11 Hub & Awasthi JCTC 2017.
     483           4 :   std::vector<double> ws(NS[0]);
     484             : 
     485             :   // Eq. 10 Hub & Awasthi JCTC 2017.
     486             :   double W = 0.0;
     487             : 
     488             :   // Eq. 21 and 22 Hub & Awasthi JCTC 2017.
     489           4 :   std::vector<double> sx(NS[0]), sy(NS[0]), cx(NS[0]), cy(NS[0]);
     490             : 
     491             :   // Eq. 10 Hub & Awasthi JCTC 2017.
     492             :   double Xsc = 0.0, Xcc = 0.0, Ysc = 0.0, Ycc = 0.0;
     493             : 
     494             : #ifdef _OPENMP
     495             : #if _OPENMP >= 201307
     496           4 :   #pragma omp parallel for private(distCylinder, aux, condition1, condition2, condition3, ZMemDistance, PositionS, Position, x) reduction(vec_double_plus:Fs, sx, sy, cx, cy)
     497             : #endif
     498             : #endif
     499             :   for (unsigned i = 0; i < chainBeads; i++)
     500             : {
     501             :   distCylinder = pbcDistance(xyzCyl_Mem, pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + noChainBeads)));
     502             :     aux = sqrt(pow(distCylinder[0], 2) + pow(distCylinder[1], 2));
     503             :     condition1 = ((aux / RCYLAVERAGE) < 1.0);
     504             :     condition2 = ((pbcDistance(Vector(0.0, 0.0, ZuMem), getPosition(i + noChainBeads))[2] > 0) && (aux / RCYLAVERAGE) < 2.0);
     505             :     condition3 = ((pbcDistance(getPosition(i + noChainBeads), Vector(0.0, 0.0, ZlMem))[2] > 0) && (aux / RCYLAVERAGE) < 2.0);
     506             :     if (condition1 || condition2 || condition3)
     507             :     {
     508             :       ZMemDistance = pbcDistance(Vector(0.0, 0.0, ZMems), getPosition(i + noChainBeads))[2];
     509             :       PositionS = (ZMemDistance + firstSliceZDist) / DS[0];
     510             :       // If the following condition is met the particle is in the Z space of the cylinder.
     511             :       if ((PositionS >= (-0.5 - HCH[0])) && (PositionS <= (NS[0] + 0.5 - 1.0 + HCH[0])))
     512             :       {
     513             :         analyzeThisParticle[i] = 1.0;
     514             : 
     515             :         //Defining the slices to analyze each particle.
     516             :         if (PositionS < 1)
     517             :         {
     518             :           s1[i] = 0;
     519             :           s2[i] = 2;
     520             :         }
     521             :         else if (PositionS <= (NS[0] - 2.0))
     522             :         {
     523             :           s1[i] = floor(PositionS) - 1;
     524             :           s2[i] = floor(PositionS) + 1;
     525             :         }
     526             :         else
     527             :         {
     528             :           s1[i] = NS[0] - 3;
     529             :           s2[i] = NS[0] - 1;
     530             :         }
     531             : 
     532             :         Position = getPbc().realToScaled(pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + noChainBeads)));
     533             : 
     534             :         for (unsigned s = s1[i]; s <= s2[i]; s++)
     535             :         {
     536             :           x = (ZMemDistance - (s + 0.5 - NS[0] / 2.0) * DS[0]) * 2.0 / DS[0];
     537             :           if (!((x <= -1.0 - HCH[0]) || (x >= 1.0 + HCH[0])))
     538             :           {
     539             :             if (((-1.0 + HCH[0]) <= x) && (x <= (1.0 - HCH[0])))
     540             :             {
     541             :               faxial[i + chainBeads * s] = 1.0;
     542             :               Fs[s] += 1.0;
     543             :               sx[s] += sin(2.0 * M_PI * Position[0]);
     544             :               sy[s] += sin(2.0 * M_PI * Position[1]);
     545             :               cx[s] += cos(2.0 * M_PI * Position[0]);
     546             :               cy[s] += cos(2.0 * M_PI * Position[1]);
     547             :             }
     548             :             else if (((1.0 - HCH[0]) < x) && (x < (1.0 + HCH[0])))
     549             :             {
     550             :               aux = 0.5 - ((3.0 * x - 3.0) / (4.0 * HCH[0])) + (pow((x - 1.0), 3) / (4.0 * pow(HCH[0], 3)));
     551             :               faxial[i + chainBeads * s] = aux;
     552             :               d_faxial_dz[i + chainBeads * s] = ((-3.0 / (4.0 * HCH[0])) + ((3.0 * pow((x - 1), 2)) / (4.0 * pow(HCH[0], 3)))) * 2.0 / DS[0];
     553             :               Fs[s] += aux;
     554             :               sx[s] += aux * sin(2.0 * M_PI * Position[0]);
     555             :               sy[s] += aux * sin(2.0 * M_PI * Position[1]);
     556             :               cx[s] += aux * cos(2.0 * M_PI * Position[0]);
     557             :               cy[s] += aux * cos(2.0 * M_PI * Position[1]);
     558             :             }
     559             :             else if (((-1.0 - HCH[0]) < x) && (x < (-1.0 + HCH[0])))
     560             :             {
     561             :               aux = 0.5 + ((3.0 * x + 3.0) / (4.0 * HCH[0])) - (pow((x + 1.0), 3) / (4.0 * pow(HCH[0], 3)));
     562             :               faxial[i + chainBeads * s] = aux;
     563             :               d_faxial_dz[i + chainBeads * s] = ((3.0 / (4.0 * HCH[0])) - ((3.0 * pow((x + 1), 2)) / (4.0 * pow(HCH[0], 3)))) * 2.0 / DS[0];
     564             :               Fs[s] += aux;
     565             :               sx[s] += (aux * sin(2.0 * M_PI * Position[0]));
     566             :               sy[s] += (aux * sin(2.0 * M_PI * Position[1]));
     567             :               cx[s] += (aux * cos(2.0 * M_PI * Position[0]));
     568             :               cy[s] += (aux * cos(2.0 * M_PI * Position[1]));
     569             :             }
     570             :           }
     571             :         }
     572             :       }
     573             :     }
     574             :   }
     575             : 
     576         184 :   for (unsigned s = 0; s < NS[0]; s++)
     577             :   {
     578         180 :     if (Fs[s] != 0.0)
     579             :     {
     580          49 :       ws[s] = tanh(Fs[s]);
     581          49 :       W += ws[s];
     582          49 :       sx[s] = sx[s] / Fs[s];
     583          49 :       sy[s] = sy[s] / Fs[s];
     584          49 :       cx[s] = cx[s] / Fs[s];
     585          49 :       cy[s] = cy[s] / Fs[s];
     586          49 :       Xsc += sx[s] * ws[s];
     587          49 :       Ysc += sy[s] * ws[s];
     588          49 :       Xcc += cx[s] * ws[s];
     589          49 :       Ycc += cy[s] * ws[s];
     590             :     }
     591             :   }
     592             : 
     593           4 :   Xsc = Xsc / W;
     594           4 :   Ysc = Ysc / W;
     595           4 :   Xcc = Xcc / W;
     596           4 :   Ycc = Ycc / W;
     597             : 
     598             :   // Eq. 12 Hub & Awasthi JCTC 2017.
     599             :   double Xcyl, Ycyl;
     600             : 
     601             :   Xcyl = Xcyl_Mem;
     602             :   Ycyl = Ycyl_Mem;
     603             : 
     604             :   // Eq. 25, 26 and 27 Hub & Awasthi JCTC 2017.
     605             :   double d_sx_dx, d_sx_dz, d_sy_dy, d_sy_dz, d_cx_dx, d_cx_dz, d_cy_dy, d_cy_dz;
     606             : 
     607             :   // Eq. 29 Hub & Awasthi JCTC 2017.
     608             :   double d_ws_dz;
     609             : 
     610             :   // Eq. 31, 32 and 33 Hub & Awasthi JCTC 2017
     611             :   double d_Xsc_dx, d_Xsc_dz, d_Xcc_dx, d_Xcc_dz, d_Ysc_dy, d_Ysc_dz, d_Ycc_dy, d_Ycc_dz;
     612             : 
     613             :   // Center of the cylinder. X and Y are calculated (or defined), Z is the Z component of the geometric center of the membranes.
     614           4 :   Vector xyzCyl = pbcDistance(Vector(0.0, 0.0, 0.0), Vector(Xcyl, Ycyl, ZMems));
     615             : 
     616             :   // Distances from the oxygens to center of the cylinder.
     617           4 :   std::vector<Vector> CylDistances(chainBeads);
     618             : 
     619             :   // Modulo of the XY distances from the oxygens to the center of the cylinder.
     620             :   double ri;
     621             : 
     622             :   // Eq. 8 Hub & Awasthi JCTC 2017.
     623             :   double fradial;
     624             : 
     625             :   // Eq. 15 Hub & Awasthi JCTC 2017.
     626           4 :   std::vector<double> d_fradial_dx(chainBeads), d_fradial_dy(chainBeads);
     627             : 
     628             :   // Eq. 35, 36, 37 and 38 Hub & Awasthi JCTC 2017.
     629           4 :   std::vector<double> d_Xcyl_dx(chainBeads), d_Xcyl_dz(chainBeads), d_Ycyl_dy(chainBeads), d_Ycyl_dz(chainBeads);
     630             : 
     631             :   // To avoid rare instabilities auxX and auxY are truncated at a configurable value (default 500).
     632           4 :   double auxX = (1 / (pow(Xsc, 2) + pow(Xcc, 2))), auxY = (1 / (pow(Ysc, 2) + pow(Ycc, 2)));
     633             : 
     634           4 :   if (auxX > ONEOVERS2C2CUTOFF[0])
     635             :   {
     636           0 :     auxX = Lx * ONEOVERS2C2CUTOFF[0] / (2 * M_PI);
     637             :   }
     638             :   else
     639             :   {
     640           4 :     auxX = Lx * auxX / (2 * M_PI);
     641             :   }
     642             : 
     643           4 :   if (auxY > ONEOVERS2C2CUTOFF[0])
     644             :   {
     645           0 :     auxY = Ly * ONEOVERS2C2CUTOFF[0] / (2 * M_PI);
     646             :   }
     647             :   else
     648             :   {
     649           4 :     auxY = Ly * auxY / (2 * M_PI);
     650             :   }
     651             : 
     652             :   //Number of oxygens within the slice s of the membrane-spanning cylinder.
     653           4 :   std::vector<double> Nsp(NS[0]), psi(NS[0]), d_psi(NS[0]);
     654             : 
     655             :   // Eq. 3 Hub & Awasthi JCTC 2017.
     656           4 :   double b = (ZETA[0] / (1.0 - ZETA[0])), c = ((1.0 - ZETA[0]) * exp(b));
     657             : 
     658             :   // Eq. 19 Hub & Awasthi JCTC 2017.
     659           4 :   std::vector<double> fradial_d_faxial_dz(chainBeads * NS[0]);
     660             : 
     661             :   // Eq. 20 Hub & Awasthi JCTC 2017.
     662           4 :   std::vector<double> Axs(NS[0]), Ays(NS[0]);
     663             : 
     664             : #ifdef _OPENMP
     665             : #if _OPENMP >= 201307
     666           4 :   #pragma omp parallel for private(d_Xsc_dx,d_Xcc_dx,d_Ysc_dy,d_Ycc_dy,d_Xsc_dz,d_Xcc_dz,d_Ysc_dz,d_Ycc_dz,d_sx_dx,d_sy_dy,d_cx_dx,d_cy_dy,d_sx_dz,d_sy_dz,d_cx_dz,d_cy_dz,d_ws_dz,ri,x,fradial) reduction(vec_double_plus: Nsp, Axs, Ays)
     667             : #endif
     668             : #endif
     669             :   for (unsigned i = 0; i < chainBeads; i++)
     670             : {
     671             :   CylDistances[i] = pbcDistance(xyzCyl, pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + noChainBeads)));
     672             :     if (analyzeThisParticle[i])
     673             :     {
     674             :       Position = getPbc().realToScaled(pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + noChainBeads)));
     675             :       d_Xsc_dx = 0.0;
     676             :       d_Xcc_dx = 0.0;
     677             :       d_Ysc_dy = 0.0;
     678             :       d_Ycc_dy = 0.0;
     679             :       d_Xsc_dz = 0.0;
     680             :       d_Xcc_dz = 0.0;
     681             :       d_Ysc_dz = 0.0;
     682             :       d_Ycc_dz = 0.0;
     683             :       for (unsigned s = s1[i]; s <= s2[i]; s++)
     684             :       {
     685             :         if (Fs[s] != 0.0)
     686             :         {
     687             :           d_sx_dx = faxial[i + chainBeads * s] * 2.0 * M_PI * cos(2.0 * M_PI * Position[0]) / (Lx * Fs[s]);
     688             :           d_sy_dy = faxial[i + chainBeads * s] * 2.0 * M_PI * cos(2.0 * M_PI * Position[1]) / (Ly * Fs[s]);
     689             :           d_cx_dx = -faxial[i + chainBeads * s] * 2.0 * M_PI * sin(2.0 * M_PI * Position[0]) / (Lx * Fs[s]);
     690             :           d_cy_dy = -faxial[i + chainBeads * s] * 2.0 * M_PI * sin(2.0 * M_PI * Position[1]) / (Ly * Fs[s]);
     691             :           d_Xsc_dx += ws[s] * d_sx_dx / W;
     692             :           d_Xcc_dx += ws[s] * d_cx_dx / W;
     693             :           d_Ysc_dy += ws[s] * d_sy_dy / W;
     694             :           d_Ycc_dy += ws[s] * d_cy_dy / W;
     695             : 
     696             :           d_sx_dz = d_faxial_dz[i + chainBeads * s] * (sin(2.0 * M_PI * Position[0]) - sx[s]) / Fs[s];
     697             :           d_sy_dz = d_faxial_dz[i + chainBeads * s] * (sin(2.0 * M_PI * Position[1]) - sy[s]) / Fs[s];
     698             :           d_cx_dz = d_faxial_dz[i + chainBeads * s] * (cos(2.0 * M_PI * Position[0]) - cx[s]) / Fs[s];
     699             :           d_cy_dz = d_faxial_dz[i + chainBeads * s] * (cos(2.0 * M_PI * Position[1]) - cy[s]) / Fs[s];
     700             :           d_ws_dz = (1 - pow(ws[s], 2)) * d_faxial_dz[i + chainBeads * s];
     701             :           d_Xsc_dz += (ws[s] * d_sx_dz + d_ws_dz * (sx[s] - Xsc)) / W;
     702             :           d_Xcc_dz += (ws[s] * d_cx_dz + d_ws_dz * (cx[s] - Xcc)) / W;
     703             :           d_Ysc_dz += (ws[s] * d_sy_dz + d_ws_dz * (sy[s] - Ysc)) / W;
     704             :           d_Ycc_dz += (ws[s] * d_cy_dz + d_ws_dz * (cy[s] - Ycc)) / W;
     705             :         }
     706             :       }
     707             :       d_Xcyl_dx[i] = auxX * (-Xsc * d_Xcc_dx + Xcc * d_Xsc_dx);
     708             :       d_Xcyl_dz[i] = auxX * (-Xsc * d_Xcc_dz + Xcc * d_Xsc_dz);
     709             :       d_Ycyl_dy[i] = auxY * (-Ysc * d_Ycc_dy + Ycc * d_Ysc_dy);
     710             :       d_Ycyl_dz[i] = auxY * (-Ysc * d_Ycc_dz + Ycc * d_Ysc_dz);
     711             : 
     712             :       ri = sqrt(pow(CylDistances[i][0], 2) + pow(CylDistances[i][1], 2));
     713             :       x = ri / RCYL[0];
     714             :       if (!((x <= -1.0 - HCH[0]) || (x >= 1.0 + HCH[0])))
     715             :       {
     716             :         if (((-1.0 + HCH[0]) <= x) && (x <= (1.0 - HCH[0])))
     717             :         {
     718             :           fradial = 1.0;
     719             :         }
     720             :         else if (((1.0 - HCH[0]) < x) && (x < (1.0 + HCH[0])))
     721             :         {
     722             :           fradial = 0.5 - ((3.0 * x - 3.0) / (4.0 * HCH[0])) + (pow((x - 1.0), 3) / (4.0 * pow(HCH[0], 3)));
     723             :           d_fradial_dx[i] = ((-3.0 / (4.0 * HCH[0])) + ((3.0 * pow((x - 1), 2)) / (4.0 * pow(HCH[0], 3)))) * CylDistances[i][0] / (RCYL[0] * ri);
     724             :           d_fradial_dy[i] = ((-3.0 / (4.0 * HCH[0])) + ((3.0 * pow((x - 1), 2)) / (4.0 * pow(HCH[0], 3)))) * CylDistances[i][1] / (RCYL[0] * ri);
     725             :         }
     726             :         else if (((-1.0 - HCH[0]) < x) && (x < (-1.0 + HCH[0])))
     727             :         {
     728             :           fradial = 0.5 + ((3.0 * x + 3.0) / (4.0 * HCH[0])) - (pow((x + 1.0), 3) / (4.0 * pow(HCH[0], 3)));
     729             :           d_fradial_dx[i] = ((3.0 / (4.0 * HCH[0])) - ((3.0 * pow((x + 1), 2)) / (4.0 * pow(HCH[0], 3)))) * CylDistances[i][0] / (RCYL[0] * ri);
     730             :           d_fradial_dy[i] = ((3.0 / (4.0 * HCH[0])) - ((3.0 * pow((x + 1), 2)) / (4.0 * pow(HCH[0], 3)))) * CylDistances[i][1] / (RCYL[0] * ri);
     731             :         }
     732             : 
     733             :         for (unsigned s = s1[i]; s <= s2[i]; s++)
     734             :         {
     735             :           Nsp[s] += fradial * faxial[i + chainBeads * s];
     736             :           Axs[s] += faxial[i + chainBeads * s] * d_fradial_dx[i];
     737             :           Ays[s] += faxial[i + chainBeads * s] * d_fradial_dy[i];
     738             :           fradial_d_faxial_dz[i + chainBeads * s] = fradial * d_faxial_dz[i + chainBeads * s];
     739             :         }
     740             :       }
     741             :     }
     742             :   }
     743             : 
     744         184 :   for (unsigned s = 0; s < NS[0]; s++)
     745             :   {
     746         180 :     if (Nsp[s] <= 1.0)
     747             :     {
     748         149 :       psi[s] = ZETA[0] * Nsp[s];
     749         149 :       d_psi[s] = ZETA[0];
     750         149 :       Xi_n += psi[s];
     751             :     }
     752             :     else
     753             :     {
     754          31 :       psi[s] = 1.0 - c * exp(-b * Nsp[s]);
     755          31 :       d_psi[s] = b * c * exp(-b * Nsp[s]);
     756          31 :       Xi_n += psi[s];
     757             :     }
     758             :   }
     759             : 
     760           4 :   Xi_n = Xi_n / NS[0];
     761             : 
     762             :   // Eq. 18 Hub & Awasthi JCTC 2017.
     763           4 :   std::vector<double> faxial_d_fradial_dx(chainBeads * NS[0]), faxial_d_fradial_dy(chainBeads * NS[0]), faxial_d_fradial_dz(chainBeads * NS[0]);
     764             : 
     765             :   // Eq. 13 Hub & Awasthi JCTC 2017 modified to considere the Heaviside_Chain step function (this only affect during the transition).
     766           4 :   std::vector<Vector> derivatives_Chain(chainBeads);
     767             : 
     768             : #ifdef _OPENMP
     769             : #if _OPENMP >= 201307
     770           4 :   #pragma omp parallel for private(aux)
     771             : #endif
     772             : #endif
     773             :   for (unsigned i = 0; i < chainBeads; i++)
     774             : {
     775             :   if (analyzeThisParticle[i])
     776             :     {
     777             :       for (unsigned s = s1[i]; s <= s2[i]; s++)
     778             :       {
     779             :         if (faxial[i + chainBeads * s])
     780             :         {
     781             :           faxial_d_fradial_dx[i + chainBeads * s] = faxial[i + chainBeads * s] * d_fradial_dx[i] - d_Xcyl_dx[i] * Axs[s];
     782             :           faxial_d_fradial_dy[i + chainBeads * s] = faxial[i + chainBeads * s] * d_fradial_dy[i] - d_Ycyl_dy[i] * Ays[s];
     783             :           faxial_d_fradial_dz[i + chainBeads * s] = -d_Xcyl_dz[i] * Axs[s] - d_Ycyl_dz[i] * Ays[s];
     784             :         }
     785             :       }
     786             : 
     787             :       for (unsigned s = s1[i]; s <= s2[i]; s++)
     788             :       {
     789             :         aux = d_psi[s] / NS[0];
     790             :         derivatives_Chain[i][0] += aux * faxial_d_fradial_dx[i + chainBeads * s];
     791             :         derivatives_Chain[i][1] += aux * faxial_d_fradial_dy[i + chainBeads * s];
     792             :         derivatives_Chain[i][2] += aux * (faxial_d_fradial_dz[i + chainBeads * s] + fradial_d_faxial_dz[i + chainBeads * s]);
     793             :       }
     794             :     }
     795             :   }
     796             : 
     797           4 :   Tensor virial;
     798      134604 :   for (unsigned i = 0; i < chainBeads; i++)
     799             : {
     800      134600 :   setAtomsDerivatives((i + noChainBeads), derivatives_Chain[i]);
     801      134600 :     virial -= Tensor(CylDistances[i], derivatives_Chain[i]);
     802             :   }
     803             : 
     804           4 :   setValue(Xi_n);
     805           4 :   setBoxDerivatives(virial);
     806           4 : }
     807             : }
     808             : }

Generated by: LCOV version 1.16