LCOV - code coverage report
Current view: top level - membranefusion - FusionPoreExpansionP.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 138 158 87.3 %
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 FUSIONPOREEXPANSIONP
      35             : /*
      36             : A CV for inducing the expansion of a fusion pore from a nucleated fusion pore.
      37             : 
      38             : Calculate the collective variable designed by Hub \cite Hub2021  and implemented into PLUMED by Masone and collaborators.
      39             : This CV is capable of inducing the expansion of the fusion pore from a nucleated fusion pore.
      40             : 
      41             : \f[
      42             : \xi_e = \frac{R(r) - R_0}{R_0}
      43             : \f]
      44             : 
      45             : Where \f$\xi_e\f$ is the CV, \f$R_0\f$ is a normalization constant that makes zero the initial value of \f$\xi_e\f$, and
      46             : \f$R(r)\f$ is the approximate radius of the fusion pore, which is defined by the number of waters and phosphateoxygens
      47             : beads within a horizontal layer in the center of both membranes.
      48             : 
      49             : \par Examples
      50             : 
      51             : This example induces the expansion of a nucleated fusion pore (\f$\xi_e = 0.75\f$) from a just nucleated fusion pore (\f$\xi_e = 0.00\f$).
      52             : 
      53             : \plumedfile
      54             : lMem: GROUP ATOMS=1-10752,21505-22728,23953-24420 #All the lower membrane beads.
      55             : uMem: GROUP ATOMS=10753-21504,22729-23952,24421-24888 #All the upper membrane beads.
      56             : 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).
      57             : waters: GROUP ATOMS=24889-56589  #All the water beads.
      58             : po4: GROUP ATOMS=2-23942:12,23957-24875:18 #All the lipid phosphateoxygens beads.
      59             : 
      60             : fusionPoreExpansion: FUSIONPOREEXPANSIONP UMEMBRANE=uMem LMEMBRANE=lMem TAILS=tails WATERS=waters PHOSPHATEOXYGENS=po4 NSMEM=85 D=7.0 R0=0.57
      61             : 
      62             : MOVINGRESTRAINT ...
      63             :     ARG=fusionPoreExpansion
      64             :     STEP0=0 AT0=0.0 KAPPA0=10000.0
      65             :     STEP1=500000 AT1=0.75 KAPPA1=10000.0
      66             : ...
      67             : 
      68             : PRINT ARG=fusionPoreExpansion FILE=COLVAR STRIDE=1
      69             : 
      70             : \endplumedfile
      71             : 
      72             : */
      73             : //+ENDPLUMEDOC
      74             : class fusionPoreExpansionP : public Colvar
      75             : {
      76             :   std::vector<AtomNumber> UMEM, LMEM, TAILS, WATERS, POXYGENS;
      77             :   std::vector<double> NSMEM, DSMEM, HMEM, VO, D, H, RMAX, R0, XCYL, YCYL;
      78             : 
      79             : public:
      80             :   explicit fusionPoreExpansionP(const ActionOptions &);
      81             :   void calculate() override;
      82             :   static void registerKeywords(Keywords &keys);
      83             : };
      84             : 
      85             : PLUMED_REGISTER_ACTION(fusionPoreExpansionP, "FUSIONPOREEXPANSIONP")
      86             : 
      87           3 : void fusionPoreExpansionP::registerKeywords(Keywords &keys)
      88             : {
      89           3 :   Colvar::registerKeywords(keys);
      90           6 :   keys.add("atoms", "UMEMBRANE", "all the beads of the upper membrane.");
      91           6 :   keys.add("atoms", "LMEMBRANE", "all the beads of the lower membrane.");
      92           6 :   keys.add("atoms", "TAILS", "all the tail beads of the system.");
      93           6 :   keys.add("atoms", "WATERS", "all the water beads of the system.");
      94           6 :   keys.add("atoms", "PHOSPHATEOXYGENS", "all the lipid phosphateoxygens beads of the system.");
      95           6 :   keys.add("compulsory", "NSMEM", "the number of slices of the membrane fusion cylinder.");
      96           6 :   keys.add("optional", "DSMEM", "( default=0.1 ) thickness of the slices of the membrane fusion cylinder.");
      97           6 :   keys.add("optional", "HMEM", "( default=0.25 ) parameter of the step function θ(x,h) for the membrane fusion.");
      98           6 :   keys.add("optional", "VO", "( default=0.076879 ) beads' molecular volume.");
      99           6 :   keys.add("compulsory", "D", "horizontal layer thickness, it depends on the Z separation of the membranes.");
     100           6 :   keys.add("optional", "H", "( default=0.1 ) parameter of the step function θ(x,h) for the fusion pore expansion.");
     101           6 :   keys.add("optional", "RMAX", "( default=2.5 ) to avoid effects of membrane undulations in large membranes (more than 256 lipids).");
     102           6 :   keys.add("compulsory", "R0", "normalization constant that makes 0 the initial value of the CV.");
     103           6 :   keys.add("optional", "XCYL", "X coordinate of the fixed cylinder, if not present this will be calculated.");
     104           6 :   keys.add("optional", "YCYL", "X coordinate of the fixed cylinder, if not present this will be calculated.");
     105           3 :   keys.setValueDescription("the value of the CV");
     106           3 : }
     107             : 
     108           1 : fusionPoreExpansionP::fusionPoreExpansionP(const ActionOptions &ao) : PLUMED_COLVAR_INIT(ao)
     109             : {
     110           2 :   parseAtomList("UMEMBRANE", UMEM);
     111           1 :   if (UMEM.size() == 0)
     112           0 :     error("UMEMBRANE has not any atom specified.");
     113             : 
     114           2 :   parseAtomList("LMEMBRANE", LMEM);
     115           1 :   if (LMEM.size() == 0)
     116           0 :     error("LMEMBRANE has not any atom specified.");
     117             : 
     118           2 :   parseAtomList("TAILS", TAILS);
     119           1 :   if (TAILS.size() == 0)
     120           0 :     error("TAILS has not any atom specified.");
     121             : 
     122           2 :   parseAtomList("WATERS", WATERS);
     123           1 :   if (WATERS.size() == 0)
     124           0 :     error("WATERS has not any atom specified.");
     125             : 
     126           2 :   parseAtomList("PHOSPHATEOXYGENS", POXYGENS);
     127           1 :   if (POXYGENS.size() == 0)
     128           0 :     error("PHOSPHATEOXYGENS has not any atom specified.");
     129             : 
     130           2 :   parseVector("NSMEM", NSMEM);
     131           1 :   if (NSMEM.size() > 1)
     132           0 :     error("NSMEM cannot take more than one value.");
     133             : 
     134           2 :   parseVector("DSMEM", DSMEM);
     135           1 :   if (DSMEM.size() > 1)
     136           0 :     error("DSMEM cannot take more than one value.");
     137           1 :   if (DSMEM.size() == 0)
     138           0 :     DSMEM.push_back(0.1);
     139             : 
     140           2 :   parseVector("HMEM", HMEM);
     141           1 :   if (HMEM.size() > 1)
     142           0 :     error("HMEM cannot take more than one value.");
     143           1 :   if (HMEM.size() == 0)
     144           0 :     HMEM.push_back(0.25);
     145             : 
     146           2 :   parseVector("VO", VO);
     147           1 :   if (VO.size() > 1)
     148           0 :     error("VO cannot take more than one value.");
     149           1 :   if (VO.size() == 0)
     150           0 :     VO.push_back(0.076879);
     151             : 
     152           2 :   parseVector("D", D);
     153           1 :   if (D.size() > 1)
     154           0 :     error("D cannot take more than one value.");
     155             : 
     156           2 :   parseVector("H", H);
     157           1 :   if (H.size() > 1)
     158           0 :     error("H cannot take more than one value.");
     159           1 :   if (H.size() == 0)
     160           0 :     H.push_back(0.1);
     161             : 
     162           2 :   parseVector("RMAX", RMAX);
     163           1 :   if (RMAX.size() > 1)
     164           0 :     error("RMAX cannot take more than one value.");
     165           1 :   if (RMAX.size() == 0)
     166           0 :     RMAX.push_back(2.5);
     167             : 
     168           2 :   parseVector("R0", R0);
     169           1 :   if (R0.size() > 1)
     170           0 :     error("R0 cannot take more than one value.");
     171             : 
     172           2 :   parseVector("XCYL", XCYL);
     173           1 :   if (XCYL.size() > 1)
     174           0 :     error("XCYL cannot take more than one value.");
     175           1 :   if (XCYL.size() == 0)
     176           1 :     XCYL.push_back(-1.0);
     177             : 
     178           2 :   parseVector("YCYL", YCYL);
     179           1 :   if (YCYL.size() > 1)
     180           0 :     error("YCYL cannot take more than one value.");
     181           1 :   if (YCYL.size() == 0)
     182           1 :     YCYL.push_back(-1.0);
     183             : 
     184           1 :   checkRead();
     185             : 
     186             :   std::vector<AtomNumber> atoms;
     187       12445 :   for (unsigned i = 0; i < UMEM.size(); i++)
     188             :   {
     189       12444 :     atoms.push_back(UMEM[i]);
     190             :   }
     191       12445 :   for (unsigned i = 0; i < LMEM.size(); i++)
     192             :   {
     193       12444 :     atoms.push_back(LMEM[i]);
     194             :   }
     195        4097 :   for (unsigned i = 0; i < TAILS.size(); i++)
     196             :   {
     197        4096 :     atoms.push_back(TAILS[i]);
     198             :   }
     199       31800 :   for (unsigned i = 0; i < WATERS.size(); i++)
     200             :   {
     201       31799 :     atoms.push_back(WATERS[i]);
     202             :   }
     203        2049 :   for (unsigned i = 0; i < POXYGENS.size(); i++)
     204             :   {
     205        2048 :     atoms.push_back(POXYGENS[i]);
     206             :   }
     207             : 
     208           1 :   addValueWithDerivatives();
     209           1 :   setNotPeriodic();
     210           1 :   requestAtoms(atoms);
     211           1 : }
     212           4 : void fusionPoreExpansionP::calculate()
     213             : {
     214             :   /*************************
     215             :   *                        *
     216             :   *         System         *
     217             :   *                        *
     218             :   **************************/
     219             : 
     220             :   // Box dimensions.
     221           4 :   double Lx = getBox()[0][0], Ly = getBox()[1][1], Lz = getBox()[2][2];
     222             : 
     223             :   // 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 .
     224             :   double ZuMem, ZuMemcos = 0.0, ZuMemsin = 0.0, uMemAngle, ZlMem, ZlMemcos = 0.0, ZlMemsin = 0.0, lMemAngle;
     225             : 
     226             : #ifdef _OPENMP
     227             : #if _OPENMP >= 201307
     228           4 :   #pragma omp parallel for private(uMemAngle, lMemAngle) reduction(+:ZuMemcos, ZuMemsin, ZlMemcos, ZlMemsin)
     229             : #endif
     230             : #endif
     231             :   for (unsigned i = 0; i < UMEM.size(); i++)
     232             :   {
     233             :     uMemAngle = 2.0 * M_PI * getPbc().realToScaled(pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i)))[2];
     234             :     lMemAngle = 2.0 * M_PI * getPbc().realToScaled(pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + UMEM.size())))[2];
     235             :     ZuMemcos += cos(uMemAngle);
     236             :     ZuMemsin += sin(uMemAngle);
     237             :     ZlMemcos += cos(lMemAngle);
     238             :     ZlMemsin += sin(lMemAngle);
     239             :   }
     240           4 :   ZuMemcos = ZuMemcos / UMEM.size();
     241           4 :   ZuMemsin = ZuMemsin / UMEM.size();
     242           4 :   ZuMem = Lz * (atan2(-ZuMemsin, -ZuMemcos) + M_PI) / (2.0 * M_PI);
     243           4 :   ZlMemcos = ZlMemcos / UMEM.size();
     244           4 :   ZlMemsin = ZlMemsin / UMEM.size();
     245           4 :   ZlMem = Lz * (atan2(-ZlMemsin, -ZlMemcos) + M_PI) / (2.0 * M_PI);
     246             : 
     247             :   // Z center of the boths membranes (upper and lower).
     248           4 :   double ZMems = (ZuMem + ZlMem) / 2.0;
     249             : 
     250             :   /**************************
     251             :   *                         *
     252             :   *   Xcyl_Mem & Ycyl_Mem   *
     253             :   *                         *
     254             :   ***************************/
     255             : 
     256             :   // Quantity of beads of the membranes.
     257           4 :   unsigned membraneBeads = UMEM.size() + LMEM.size();
     258             : 
     259             :   // Z distance from the lipid tail to the geometric center of both membranes.
     260             :   double ZTailDistance;
     261             : 
     262             :   // Z position of the first slice.
     263           4 :   double firstSliceZ_Mem = ZMems + (0.0 + 0.5 - NSMEM[0] / 2.0) * DSMEM[0];
     264             : 
     265             :   // Z distance between the first slice and the Z center of the membrane.
     266           8 :   double firstSliceZDist_Mem = pbcDistance(Vector(0.0, 0.0, firstSliceZ_Mem), Vector(0.0, 0.0, ZMems))[2];
     267             : 
     268             :   // Position in the cylinder.
     269             :   double PositionS_Mem;
     270             : 
     271             :   // Slices to analyze per particle.
     272             :   unsigned s1_Mem, s2_Mem;
     273             : 
     274             :   // Eq. 7 Hub & Awasthi JCTC 2017.
     275           4 :   std::vector<double> faxial_Mem(TAILS.size() * NSMEM[0]);
     276             : 
     277             :   // Eq. 10 Hub & Awasthi JCTC 2017.
     278           4 :   std::vector<double> Fs_Mem(NSMEM[0]);
     279             : 
     280             :   // Eq. 11 Hub & Awasthi JCTC 2017.
     281           4 :   std::vector<double> ws_Mem(NSMEM[0]);
     282             : 
     283             :   // Eq. 10 Hub & Awasthi JCTC 2017.
     284             :   double W_Mem = 0.0;
     285             : 
     286             :   // Eq. 21 and 22 Hub & Awasthi JCTC 2017.
     287           4 :   std::vector<double> sx_Mem(NSMEM[0]), sy_Mem(NSMEM[0]), cx_Mem(NSMEM[0]), cy_Mem(NSMEM[0]);
     288             : 
     289             :   // Eq. 10 Hub & Awasthi JCTC 2017.
     290             :   double Xsc_Mem = 0.0, Xcc_Mem = 0.0, Ysc_Mem = 0.0, Ycc_Mem = 0.0;
     291             : 
     292             :   // Aux.
     293             :   double x, aux;
     294             : 
     295             :   // Scaled position of the lipid tail respect the origin of coordinates.
     296           4 :   Vector TailPosition;
     297             : 
     298             : #ifdef _OPENMP
     299             : #if _OPENMP >= 201307
     300             :   #pragma omp declare reduction(vec_double_plus : std::vector<double> : \
     301             :   std::transform(omp_out.begin(), omp_out.end(), omp_in.begin(), omp_out.begin(), std::plus<double>())) \
     302             :   initializer(omp_priv = decltype(omp_orig)(omp_orig.size()))
     303             : #endif
     304             : #endif
     305             : 
     306             : #ifdef _OPENMP
     307             : #if _OPENMP >= 201307
     308           4 :   #pragma omp parallel for private(ZTailDistance, PositionS_Mem, TailPosition, x, aux, s1_Mem, s2_Mem) reduction(vec_double_plus:Fs_Mem, sx_Mem, sy_Mem, cx_Mem, cy_Mem)
     309             : #endif
     310             : #endif
     311             :   for (unsigned i = 0; i < TAILS.size(); i++)
     312             :   {
     313             :     ZTailDistance = pbcDistance(Vector(0.0, 0.0, ZMems), getPosition(i + membraneBeads))[2];
     314             :     PositionS_Mem = (ZTailDistance + firstSliceZDist_Mem) / DSMEM[0];
     315             :     // If the following condition is met the particle is in the Z space of the cylinder.
     316             :     if ((PositionS_Mem >= (-0.5 - HMEM[0])) && (PositionS_Mem <= (NSMEM[0] + 0.5 - 1.0 + HMEM[0])))
     317             :     {
     318             :       //Defining the slices to analyze each particle.
     319             :       if (PositionS_Mem < 1)
     320             :       {
     321             :         s1_Mem = 0;
     322             :         s2_Mem = 2;
     323             :       }
     324             :       else if (PositionS_Mem <= (NSMEM[0] - 2.0))
     325             :       {
     326             :         s1_Mem = floor(PositionS_Mem) - 1;
     327             :         s2_Mem = floor(PositionS_Mem) + 1;
     328             :       }
     329             :       else
     330             :       {
     331             :         s1_Mem = NSMEM[0] - 3;
     332             :         s2_Mem = NSMEM[0] - 1;
     333             :       }
     334             : 
     335             :       TailPosition = getPbc().realToScaled(pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + membraneBeads)));
     336             : 
     337             :       for (unsigned s = s1_Mem; s <= s2_Mem; s++)
     338             :       {
     339             :         x = (ZTailDistance - (s + 0.5 - NSMEM[0] / 2.0) * DSMEM[0]) * 2.0 / DSMEM[0];
     340             :         if (!((x <= -1.0 - HMEM[0]) || (x >= 1.0 + HMEM[0])))
     341             :         {
     342             :           if (((-1.0 + HMEM[0]) <= x) && (x <= (1.0 - HMEM[0])))
     343             :           {
     344             :             faxial_Mem[i + TAILS.size() * s] = 1.0;
     345             :             Fs_Mem[s] += 1.0;
     346             :             sx_Mem[s] += sin(2.0 * M_PI * TailPosition[0]);
     347             :             sy_Mem[s] += sin(2.0 * M_PI * TailPosition[1]);
     348             :             cx_Mem[s] += cos(2.0 * M_PI * TailPosition[0]);
     349             :             cy_Mem[s] += cos(2.0 * M_PI * TailPosition[1]);
     350             :           }
     351             :           else if (((1.0 - HMEM[0]) < x) && (x < (1.0 + HMEM[0])))
     352             :           {
     353             :             aux = 0.5 - ((3.0 * x - 3.0) / (4.0 * HMEM[0])) + (pow((x - 1.0), 3) / (4.0 * pow(HMEM[0], 3)));
     354             :             faxial_Mem[i + TAILS.size() * s] = aux;
     355             :             Fs_Mem[s] += aux;
     356             :             sx_Mem[s] += aux * sin(2.0 * M_PI * TailPosition[0]);
     357             :             sy_Mem[s] += aux * sin(2.0 * M_PI * TailPosition[1]);
     358             :             cx_Mem[s] += aux * cos(2.0 * M_PI * TailPosition[0]);
     359             :             cy_Mem[s] += aux * cos(2.0 * M_PI * TailPosition[1]);
     360             :           }
     361             :           else if (((-1.0 - HMEM[0]) < x) && (x < (-1.0 + HMEM[0])))
     362             :           {
     363             :             aux = 0.5 + ((3.0 * x + 3.0) / (4.0 * HMEM[0])) - (pow((x + 1.0), 3) / (4.0 * pow(HMEM[0], 3)));
     364             :             faxial_Mem[i + TAILS.size() * s] = aux;
     365             :             Fs_Mem[s] += aux;
     366             :             sx_Mem[s] += (aux * sin(2.0 * M_PI * TailPosition[0]));
     367             :             sy_Mem[s] += (aux * sin(2.0 * M_PI * TailPosition[1]));
     368             :             cx_Mem[s] += (aux * cos(2.0 * M_PI * TailPosition[0]));
     369             :             cy_Mem[s] += (aux * cos(2.0 * M_PI * TailPosition[1]));
     370             :           }
     371             :         }
     372             :       }
     373             :     }
     374             :   }
     375             : 
     376         344 :   for (unsigned s = 0; s < NSMEM[0]; s++)
     377             :   {
     378         340 :     if (Fs_Mem[s] != 0.0)
     379             :     {
     380         340 :       ws_Mem[s] = tanh(Fs_Mem[s]);
     381         340 :       W_Mem += ws_Mem[s];
     382         340 :       sx_Mem[s] = sx_Mem[s] / Fs_Mem[s];
     383         340 :       sy_Mem[s] = sy_Mem[s] / Fs_Mem[s];
     384         340 :       cx_Mem[s] = cx_Mem[s] / Fs_Mem[s];
     385         340 :       cy_Mem[s] = cy_Mem[s] / Fs_Mem[s];
     386         340 :       Xsc_Mem += sx_Mem[s] * ws_Mem[s];
     387         340 :       Ysc_Mem += sy_Mem[s] * ws_Mem[s];
     388         340 :       Xcc_Mem += cx_Mem[s] * ws_Mem[s];
     389         340 :       Ycc_Mem += cy_Mem[s] * ws_Mem[s];
     390             :     }
     391             :   }
     392             : 
     393           4 :   Xsc_Mem = Xsc_Mem / W_Mem;
     394           4 :   Ysc_Mem = Ysc_Mem / W_Mem;
     395           4 :   Xcc_Mem = Xcc_Mem / W_Mem;
     396           4 :   Ycc_Mem = Ycc_Mem / W_Mem;
     397             : 
     398             :   // Eq. 12 Hub & Awasthi JCTC 2017.
     399             :   double Xcyl_Mem, Ycyl_Mem;
     400             : 
     401           4 :   if ((XCYL[0] > 0.0) && (YCYL[0] > 0.0))
     402             :   {
     403             :     Xcyl_Mem = XCYL[0];
     404             :     Ycyl_Mem = YCYL[0];
     405             :   }
     406             :   else
     407             :   {
     408           4 :     Xcyl_Mem = (atan2(-Xsc_Mem, -Xcc_Mem) + M_PI) * Lx / (2 * M_PI);
     409           4 :     Ycyl_Mem = (atan2(-Ysc_Mem, -Ycc_Mem) + M_PI) * Ly / (2 * M_PI);
     410             :   }
     411             : 
     412             :   /*************************
     413             :   *                        *
     414             :   *         Xi_Exp         *
     415             :   *                        *
     416             :   **************************/
     417             : 
     418             :   // Quantity of beads that could participate in the calculation of the Xi_Chain
     419           4 :   unsigned chainBeads = WATERS.size() + POXYGENS.size();
     420             : 
     421             :   // Quantity of beads that don't participate in the calculation of the Xi_Chain
     422           4 :   unsigned noChainBeads = (UMEM.size() * 2) + TAILS.size();
     423             : 
     424             :   // Center of the cylinder. X and Y are calculated (or defined), Z is the Z component of the geometric center of the membranes.
     425           8 :   Vector xyzCyl = pbcDistance(Vector(0.0, 0.0, 0.0), Vector(Xcyl_Mem, Ycyl_Mem, ZMems));
     426             : 
     427             :   // Estimation of RO with the Hub 2021 JCTC method. Only needed for the expansion.
     428           4 :   double RO = R0[0];
     429             : 
     430             :   // Number of polar atoms inside the horizontal layer. Eq. 3 Hub 2021 JCTC.
     431             :   double np = 0.0, fz, fr, fz_prime, fr_prime;
     432             : 
     433             :   // Derivative of np. Eq. 8 Hub 2021 JCTC.
     434           4 :   std::vector<double> d_np_dx(chainBeads), d_np_dy(chainBeads), d_np_dz(chainBeads);
     435             : 
     436             :   // Pore radius of the defect. Eq. 2 Hub 2021 JCTC.
     437             :   double poreR = 1.0;
     438             : 
     439             :   // Z center of the Membrane in the RMAX radius.
     440             :   double ZMemRMAX, ZMemRMAXcos = 0.0, ZMemRMAXsin = 0.0, countAux = 0.0, auxcos, auxsin;
     441             : 
     442             :   ZMemRMAX = ZMems;
     443             : 
     444             :   // The curvature of large membranes (1024 lipids) makes the Z-center of the membranes not to be representative
     445             :   // in some sectors, particularly in the region of ​​the defect.
     446             :   //
     447             :   // To solve this, the center Z of the membranes in the defect sector is calculated and used to calculate
     448             :   // the number of polar atoms within the horizontal layer AND in the radious of the defect.
     449             :   //
     450             :   // ________       | |       ________
     451             :   // ________ \_____| |______/ _______<-- Top membrane.
     452             :   //         \______|P|_______/
     453             :   //                |O|
     454             :   //                | |               <-- Z-center of the membranes in the region of the defect.
     455             :   //          ______|R|_______        <-- Z-center of the membranes
     456             :   //         / _____|E|______ \ 
     457             :   //        / /     | |      \ \ 
     458             :   // ______/ /      | |       \ \______
     459             :   // _______/                  \_______<-- Bottom membrane.
     460             : 
     461             :   // Center of mass for systems with PBC: https://en.wikipedia.org/wiki/Center_of_mass#Systems_with_periodic_boundary_conditions
     462           4 :   Vector MemCylDistances, distCylinder;
     463             :   double angle, ri;
     464             : 
     465             : #ifdef _OPENMP
     466             : #if _OPENMP >= 201307
     467           4 :   #pragma omp parallel for private(MemCylDistances, x, angle, auxcos, auxsin) reduction(+:ZMemRMAXcos, ZMemRMAXsin, countAux)
     468             : #endif
     469             : #endif
     470             :   for (unsigned i = 0; i < membraneBeads; i++)
     471             : {
     472             :   MemCylDistances = pbcDistance(xyzCyl, pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i)));
     473             :     x = sqrt(pow(MemCylDistances[0], 2) + pow(MemCylDistances[1], 2)) / RMAX[0];
     474             :     if (!((x <= -1.0 - H[0]) || (x >= 1.0 + H[0])))
     475             :     {
     476             :       angle = 2.0 * M_PI * getPbc().realToScaled(pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i)))[2];
     477             :       auxcos = cos(angle);
     478             :       auxsin = sin(angle);
     479             :       if (((-1.0 + H[0]) <= x) && (x <= (1.0 - H[0])))
     480             :       {
     481             :         ZMemRMAXcos += 1.0 * auxcos;
     482             :         ZMemRMAXsin += 1.0 * auxsin;
     483             :         countAux += 1.0;
     484             :       }
     485             :       else if (((1.0 - H[0]) < x) && (x < (1.0 + H[0])))
     486             :       {
     487             :         ZMemRMAXcos += (0.5 - 0.75 * (x - 1.0) / H[0] + 0.25 * pow((x - 1.0), 3) / pow(H[0], 3)) * auxcos;
     488             :         ZMemRMAXsin += (0.5 - 0.75 * (x - 1.0) / H[0] + 0.25 * pow((x - 1.0), 3) / pow(H[0], 3)) * auxsin;
     489             :         countAux += (0.5 - 0.75 * (x - 1.0) / H[0] + 0.25 * pow((x - 1.0), 3) / pow(H[0], 3));
     490             :       }
     491             :       else if (((-1.0 - H[0]) < x) && (x < (-1.0 + H[0])))
     492             :       {
     493             :         ZMemRMAXcos += (0.5 + 0.75 * (x + 1.0) / H[0] - 0.25 * pow((x + 1.0), 3) / pow(H[0], 3)) * auxcos;
     494             :         ZMemRMAXsin += (0.5 + 0.75 * (x + 1.0) / H[0] - 0.25 * pow((x + 1.0), 3) / pow(H[0], 3)) * auxsin;
     495             :         countAux += (0.5 + 0.75 * (x + 1.0) / H[0] - 0.25 * pow((x + 1.0), 3) / pow(H[0], 3));
     496             :       }
     497             :     }
     498             :   }
     499             : 
     500           4 :   ZMemRMAXcos = ZMemRMAXcos / countAux;
     501           4 :   ZMemRMAXsin = ZMemRMAXsin / countAux;
     502           4 :   ZMemRMAX = Lz * (atan2(-ZMemRMAXsin, -ZMemRMAXcos) + M_PI) / (2.0 * M_PI);
     503             : 
     504           4 :   xyzCyl = pbcDistance(Vector(0.0, 0.0, 0.0), Vector(Xcyl_Mem, Ycyl_Mem, ZMemRMAX));
     505             : 
     506             : #ifdef _OPENMP
     507             : #if _OPENMP >= 201307
     508           4 :   #pragma omp parallel for private(distCylinder, fz, fz_prime, fr, fr_prime, ri, x) reduction(+:np)
     509             : #endif
     510             : #endif
     511             :   for (unsigned i = 0; i < chainBeads; i++)
     512             : {
     513             :   distCylinder = pbcDistance(xyzCyl, pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + noChainBeads)));
     514             :     fz = 0.0;
     515             :     fz_prime = 0.0;
     516             :     fr = 0.0;
     517             :     fr_prime = 0.0;
     518             : 
     519             :     ri = sqrt(pow(distCylinder[0], 2) + pow(distCylinder[1], 2));
     520             :     x = ri / RMAX[0];
     521             :     if (!((x <= -1.0 - H[0]) || (x >= 1.0 + H[0])))
     522             :     {
     523             :       if (((-1.0 + H[0]) <= x) && (x <= (1.0 - H[0])))
     524             :       {
     525             :         fr = 1.0;
     526             :       }
     527             :       else if (((1.0 - H[0]) < x) && (x < (1.0 + H[0])))
     528             :       {
     529             :         fr = 0.5 - 0.75 * (x - 1.0) / H[0] + 0.25 * pow((x - 1.0), 3) / pow(H[0], 3);
     530             :         fr_prime = (-0.75 / H[0] + 0.75 * pow((x - 1.0), 2) / pow(H[0], 3)) / (RMAX[0] * ri);
     531             :       }
     532             :       else if (((-1.0 - H[0]) < x) && (x < (-1.0 + H[0])))
     533             :       {
     534             :         fr = 0.5 + 0.75 * (x + 1.0) / H[0] - 0.25 * pow((x + 1.0), 3) / pow(H[0], 3);
     535             :         fr_prime = (0.75 / H[0] - 0.75 * pow((x + 1), 2) / pow(H[0], 3)) / (RMAX[0] * ri);
     536             :       }
     537             : 
     538             :       x = distCylinder[2] * 2.0 / D[0];
     539             :       if (!((x <= -1.0 - H[0]) || (x >= 1.0 + H[0])))
     540             :       {
     541             :         if (((-1.0 + H[0]) <= x) && (x <= (1.0 - H[0])))
     542             :         {
     543             :           fz = 1.0;
     544             :         }
     545             :         else if (((1.0 - H[0]) < x) && (x < (1.0 + H[0])))
     546             :         {
     547             :           fz = 0.5 - 0.75 * (x - 1.0) / H[0] + 0.25 * pow((x - 1.0), 3) / pow(H[0], 3);
     548             :           fz_prime = (-0.75 / H[0] + 0.75 * pow((x - 1.0), 2) / pow(H[0], 3)) * 2.0 / D[0];
     549             :         }
     550             :         else if (((-1.0 - H[0]) < x) && (x < (-1.0 + H[0])))
     551             :         {
     552             :           fz = 0.5 + 0.75 * (x + 1.0) / H[0] - 0.25 * pow((x + 1.0), 3) / pow(H[0], 3);
     553             :           fz_prime = (0.75 / H[0] - 0.75 * pow((x + 1), 2) / pow(H[0], 3)) * 2.0 / D[0];
     554             :         }
     555             : 
     556             :         np += fz * fr;
     557             :         d_np_dx[i] = fz * fr_prime * distCylinder[0];
     558             :         d_np_dy[i] = fz * fr_prime * distCylinder[1];
     559             :         d_np_dz[i] = fz_prime * fr;
     560             :       }
     561             :     }
     562             :   }
     563           4 :   poreR = sqrt(np * VO[0] / (M_PI * D[0]));
     564             : 
     565             :   // This is the CV that describes the Pore Expansion.
     566           4 :   double Xi_Exp = (poreR - RO) / RO;
     567             : 
     568             :   // Derivatives vector.
     569           4 :   std::vector<Vector> derivatives(chainBeads);
     570             : 
     571             :   // Aux for the derivatives calculations. Eq. 7 Hub 2021 JCTC.
     572             :   double fact2 = 0.0;
     573             : 
     574           4 :   if (poreR != 0.0)
     575             : {
     576           4 :   fact2 = VO[0] / (2.0 * M_PI * RO * D[0] * poreR);
     577             :   }
     578             : 
     579             :   // Distances from the oxygens to center of the cylinder.
     580           4 :   std::vector<Vector> CylDistances(chainBeads);
     581             : 
     582             : #ifdef _OPENMP
     583             : #if _OPENMP >= 201307
     584           4 :   #pragma omp parallel for
     585             : #endif
     586             : #endif
     587             :   for (unsigned i = 0; i < chainBeads; i++)
     588             : {
     589             :   derivatives[i][0] = fact2 * d_np_dx[i];
     590             :     derivatives[i][1] = fact2 * d_np_dy[i];
     591             :     derivatives[i][2] = fact2 * d_np_dz[i];
     592             :     CylDistances[i] = pbcDistance(xyzCyl, pbcDistance(Vector(0.0, 0.0, 0.0), getPosition(i + noChainBeads)));
     593             :   }
     594             : 
     595           4 :   Tensor virial;
     596      135392 :   for (unsigned i = 0; i < chainBeads; i++)
     597             : {
     598      135388 :   setAtomsDerivatives((i + noChainBeads), derivatives[i]);
     599      135388 :     virial -= Tensor(CylDistances[i], derivatives[i]);
     600             :   }
     601             : 
     602           4 :   setValue(Xi_Exp);
     603           4 :   setBoxDerivatives(virial);
     604           4 : }
     605             : }
     606             : }

Generated by: LCOV version 1.16