From 9635a94ec0a267bc22c6c55e72cb34246dcf08c9 Mon Sep 17 00:00:00 2001
From: Chris Maffeo <cmaffeo2@illinois.edu>
Date: Mon, 8 Jul 2019 15:31:45 -0500
Subject: [PATCH] Added Kim-Hummer model from R. Best's paper
 10.1371/journal.pcbi.1005941

---
 arbdmodel/kh_polymer_model.py              | 313 +++++++++++++++++++++
 arbdmodel/kh_polymer_model_pair_epsilon.py | 229 +++++++++++++++
 2 files changed, 542 insertions(+)
 create mode 100644 arbdmodel/kh_polymer_model.py
 create mode 100644 arbdmodel/kh_polymer_model_pair_epsilon.py

diff --git a/arbdmodel/kh_polymer_model.py b/arbdmodel/kh_polymer_model.py
new file mode 100644
index 0000000..ffb7432
--- /dev/null
+++ b/arbdmodel/kh_polymer_model.py
@@ -0,0 +1,313 @@
+# -*- coding: utf-8 -*-
+## Test with `python -m arbdmodel.kh_polymer_model`
+
+import numpy as np
+import sys
+
+## Local imports
+from . import ArbdModel, ParticleType, PointParticle, Group, get_resource_path    
+from .abstract_polymer import PolymerSection, AbstractPolymerGroup
+from .interactions import NonbondedScheme, HarmonicBond, HarmonicAngle, HarmonicDihedral
+from .coords import quaternion_to_matrix
+from .kh_polymer_model_pair_epsilon import epsilon_mj
+
+"""Define particle types"""
+_types = dict(
+    A = ParticleType("ALA",
+                     mass = 71.08,
+                     charge = 0,
+                     sigma = 5.04,
+                 ),
+    R = ParticleType("ARG",
+                     mass = 156.2,
+                     charge = 1,
+                     sigma = 6.56,
+                 ),
+    N = ParticleType("ASN",
+                     mass = 114.1,
+                     charge = 0,
+                     sigma = 5.68,
+                 ),
+    D = ParticleType("ASP",
+                     mass = 115.1,
+                     charge = -1,
+                     sigma = 5.58,
+                 ),
+    C = ParticleType("CYS",
+                     mass = 103.1,
+                     charge = 0,
+                     sigma = 5.48,
+                 ),
+    Q = ParticleType("GLN",
+                     mass = 128.1,
+                     charge = 0,
+                     sigma = 6.02,
+                 ),
+    E = ParticleType("GLU",
+                     mass = 129.1,
+                     charge = -1,
+                     sigma = 5.92,
+                 ),
+    G = ParticleType("GLY",
+                     mass = 57.05,
+                     charge = 0,
+                     sigma = 4.5,
+                 ),
+    H = ParticleType("HIS",
+                     mass = 137.1,
+                     charge = 0.5,
+                     sigma = 6.08,
+                 ),
+    I = ParticleType("ILE",
+                     mass = 113.2,
+                     charge = 0,
+                     sigma = 6.18,
+                 ),
+    L = ParticleType("LUE",
+                     mass = 113.2,
+                     charge = 0,
+                     sigma = 6.18,
+                 ),
+    K = ParticleType("LYS",
+                     mass = 128.2,
+                     charge = 1,
+                     sigma = 6.36,
+                 ),
+    M = ParticleType("MET",
+                     mass = 131.2,
+                     charge = 0,
+                     sigma = 6.18,
+                 ),
+    F = ParticleType("PHE",
+                     mass = 147.2,
+                     charge = 0,
+                     sigma = 6.36,
+                 ),
+    P = ParticleType("PRO",
+                     mass = 97.12,
+                     charge = 0,
+                     sigma = 5.56,
+                 ),
+    S = ParticleType("SER",
+                     mass = 87.08,
+                     charge = 0,
+                     sigma = 5.18,
+                 ),
+    T = ParticleType("THR",
+                     mass = 101.1,
+                     charge = 0,
+                     sigma = 5.62,
+                 ),
+    W = ParticleType("TRP",
+                     mass = 186.2,
+                     charge = 0,
+                     sigma = 6.78,
+                 ),
+    Y = ParticleType("TYR",
+                     mass = 163.2,
+                     charge = 0,
+                     sigma = 6.46,
+                 ),
+    V = ParticleType("VAL",
+                     mass = 99.07,
+                     charge = 0,
+                     sigma = 5.86,
+                 )
+)
+for k,t in _types.items():
+    t.resname = t.name
+
+class KhNonbonded(NonbondedScheme):
+    def __init__(self, debye_length=10, resolution=0.1, rMin=0):
+        NonbondedScheme.__init__(self, typesA=None, typesB=None, resolution=resolution, rMin=rMin)
+        self.debye_length = debye_length
+        self.maxForce = 50
+
+    def potential(self, r, typeA, typeB):
+        """ Electrostatics """
+        ld = self.debye_length 
+        q1 = typeA.charge
+        q2 = typeB.charge
+        D = 80                  # dielectric of water
+        ## units "e**2 / (4 * pi * epsilon0 AA)" kcal_mol
+        A =  332.06371
+        u_elec = (A*q1*q2/D)*np.exp(-r/ld) / r 
+        
+        """ KH scale model """
+        # alpha = 0.228
+        # epsilon0 = -1.0
+        # e_mj = epsilon_mj[(typeA.name,typeB.name)]        
+        # epsilon = alpha * np.abs( e_mj - epsilon0 )
+        epsilon = epsilon_mj[(typeA.name,typeB.name)] 
+        lambda_ = -1 if epsilon > 0 else 1
+        epsilon = np.abs(epsilon)
+
+        sigma = 0.5 * (typeA.sigma + typeB.sigma)
+        
+        r6 = (sigma/r)**6
+        r12 = r6**2
+        u_lj = 4 * epsilon * (r12-r6)
+        u_hps = lambda_ * np.array(u_lj)
+        s = r<=sigma*2**(1/6)
+        u_hps[s] = u_lj[s] + (1-lambda_) * epsilon
+
+        u = u_elec + u_hps
+        u[0] = u[1]             # Remove NaN
+
+        maxForce = self.maxForce
+        if maxForce is not None:
+            assert(maxForce > 0)
+            f = np.diff(u)/np.diff(r)
+            f[f>maxForce] = maxForce
+            f[f<-maxForce] = -maxForce
+            u[0] = 0
+            u[1:] = np.cumsum(f*np.diff(r))
+        
+        u = u-u[-1]
+            
+        return u
+
+class KhBeadsFromPolymer(Group):
+    # p = PointParticle(_P, (0,0,0), "P")
+    # b = PointParticle(_B, (3,0,1), "B")
+    # nt = Group( name = "nt", children = [p,b])
+    # nt.add_bond( i=p, j=b, bond = get_resource_path('two_bead_model/BPB.dat') )
+
+    def __init__(self, polymer, sequence=None, **kwargs):
+
+        if sequence is None:
+            raise NotImplementedError
+            # ... set random sequence
+
+        self.polymer = polymer
+        self.sequence = sequence
+
+        for prop in ('segname','chain'):
+            if prop not in kwargs:
+                # import pdb
+                # pdb.set_trace()
+                try:
+                    self.__dict__[prop] = polymer.__dict__[prop]
+                except:
+                    pass
+
+        if len(sequence) != polymer.num_monomers:
+            raise ValueError("Length of sequence does not match length of polymer")
+        Group.__init__(self, **kwargs)
+        
+    def _clear_beads(self):
+        ...
+        
+    def _generate_beads(self):
+        # beads = self.children
+
+        for i in range(self.polymer.num_monomers):
+            c = self.polymer.monomer_index_to_contour(i)
+            r = self.polymer.contour_to_position(c)
+            s = self.sequence[i]
+
+            bead = PointParticle(_types[s], r,
+                                 name = s,
+                                 resid = i+1)
+            self.add(bead)
+            # import pdb
+            # pdb.set_trace()
+            # continue
+
+        ## Two consecutive nts 
+        for i in range(len(self.children)-1):
+            b1 = self.children[i]
+            b2 = self.children[i+1]
+            """ units "10 kJ/N_A" kcal_mol """
+            bond = HarmonicBond(k = 2.3900574,
+                                r0 = 3.8,
+                                rRange = (0,500),
+                                resolution = 0.01,
+                                maxForce = 10)
+            self.add_bond( i=b1, j=b2, bond = bond, exclude=True )
+
+
+class KhModel(ArbdModel):
+    def __init__(self, polymers,
+                 sequences = None,
+                 debye_length = 10,
+                 damping_coefficient = 10,
+                 DEBUG=False,
+                 **kwargs):
+
+        """ 
+        [debye_length]: angstroms
+        [damping_coefficient]: ns
+        """
+        kwargs['timestep'] = 10e-6
+        kwargs['cutoff'] = max(4*debye_length,20)
+
+        if 'decompPeriod' not in kwargs:
+            kwargs['decompPeriod'] = 1000
+
+        """ Assign sequences """
+        if sequences is None:
+            raise NotImplementedError("KhModel must be provided a sequences argument")
+
+        self.polymer_group = AbstractPolymerGroup(polymers)
+        self.sequences = sequences
+        ArbdModel.__init__(self, [], **kwargs)
+
+        """ Update type diffusion coefficients """
+        self.types = all_types = [t for key,t in _types.items()]
+        self.set_damping_coefficient( damping_coefficient )
+
+        """ Set up nonbonded interactions """
+        nonbonded = KhNonbonded(debye_length)
+        for i in range(len(all_types)):
+            t1 = all_types[i]
+            for j in range(i,len(all_types)):
+                t2 = all_types[j]
+                self.useNonbondedScheme( nonbonded, typeA=t1, typeB=t2 )
+                
+        """ Generate beads """
+        self.generate_beads()
+
+    def update_splines(self, coords):
+        i = 0
+        for p in self.polymer_group.polymers:
+            n = p.num_monomers
+            p.set_splines(np.linspace(0,1,n), coords[i:i+n])
+            i += n
+
+        self.clear_all()
+        self.generate_beads()
+        ## TODO Apply restraints, etc
+
+    def generate_beads(self):
+        self.peptides = [KhBeadsFromPolymer(p,s)
+                         for p,s in zip(self.polymer_group.polymers,self.sequences)]
+
+        for s in self.peptides:
+            self.add(s)
+            s._generate_beads()
+
+    def set_damping_coefficient(self, damping_coefficient):
+        for t in self.types:
+            t.damping_coefficient = damping_coefficient
+            # t.diffusivity = 831447.2 * temperature / (t.mass * damping_coefficient)
+
+if __name__ == "__main__":
+
+    from matplotlib import pyplot as plt
+    nt = len(_types)
+    # print("TYPES")
+    # for n,t in _types.items():
+    #     print("{}\t{}\t{}\t{}\t{}".format(t.name, t.mass, t.charge, t.sigma, t.lambda_))
+    type_string = 'WYFMLIVAPGCQNTSEDKHR'
+    d = np.zeros([nt,nt])
+    for i in range(nt):
+        n1 = type_string[i]
+        t1 = _types[n1]
+        for j in range(nt):
+            n2 = type_string[j]
+            t2 = _types[n2]
+            d[nt-i-1,j] = epsilon_mj[(t1.name,t2.name)]
+
+    plt.imshow(d.T)
+    plt.show()
diff --git a/arbdmodel/kh_polymer_model_pair_epsilon.py b/arbdmodel/kh_polymer_model_pair_epsilon.py
new file mode 100644
index 0000000..e696b2c
--- /dev/null
+++ b/arbdmodel/kh_polymer_model_pair_epsilon.py
@@ -0,0 +1,229 @@
+
+_txtdata = """CYS CYS -0.509719
+CYS MET -0.448877
+MET MET -0.512423
+CYS PHE -0.558393
+MET PHE -0.661148
+PHE PHE -0.755790
+CYS ILE -0.517831
+MET ILE -0.588137
+PHE ILE -0.699005
+ILE ILE -0.658443
+CYS LEU -0.562449
+MET LEU -0.640867
+PHE LEU -0.758494
+ILE LEU -0.726045
+LEU LEU -0.770663
+CYS VAL -0.444821
+MET VAL -0.493495
+PHE VAL -0.624642
+ILE VAL -0.592194
+LEU VAL -0.650331
+VAL VAL -0.520535
+CYS TRP -0.443469
+MET TRP -0.524592
+PHE TRP -0.607066
+ILE TRP -0.555688
+LEU TRP -0.604362
+VAL TRP -0.474566
+TRP TRP -0.458342
+CYS TYR -0.336658
+MET TYR -0.438061
+PHE TYR -0.539464
+ILE TYR -0.484030
+LEU TYR -0.540816
+VAL TYR -0.398852
+TRP TYR -0.404260
+TYR TYR -0.338010
+CYS ALA -0.256888
+MET ALA -0.306913
+PHE ALA -0.424541
+ILE ALA -0.393444
+LEU ALA -0.438061
+VAL ALA -0.320433
+TRP ALA -0.290689
+TYR ALA -0.228495
+ALA ALA -0.141964
+CYS GLY -0.201454
+MET GLY -0.232551
+PHE GLY -0.332602
+ILE GLY -0.285280
+LEU GLY -0.336658
+VAL GLY -0.231199
+TRP GLY -0.236607
+TYR GLY -0.181173
+ALA GLY -0.086531
+GLY GLY -0.077066
+CYS THR -0.194694
+MET THR -0.248775
+PHE THR -0.352882
+ILE THR -0.319081
+LEU THR -0.360995
+VAL THR -0.242015
+TRP THR -0.209566
+TYR THR -0.181173
+ALA THR -0.087883
+GLY THR -0.055434
+THR THR -0.060842
+CYS SER -0.160893
+MET SER -0.183877
+PHE SER -0.317729
+ILE SER -0.250127
+LEU SER -0.304209
+VAL SER -0.186582
+TRP SER -0.178469
+TYR SER -0.150076
+ALA SER -0.045969
+GLY SER -0.020281
+THR SER -0.039209
+SER SER  0.000000
+CYS ASN -0.124388
+MET ASN -0.173061
+PHE ASN -0.281224
+ILE ASN -0.212270
+LEU ASN -0.279872
+VAL ASN -0.156837
+TRP ASN -0.189286
+TYR ASN -0.147372
+ALA ASN -0.022985
+GLY ASN -0.009464
+THR ASN -0.028393
+SER ASN  0.012168
+ASN ASN -0.001352
+CYS GLN -0.159541
+MET GLN -0.220383
+PHE GLN -0.328546
+ILE GLN -0.270408
+LEU GLN -0.320433
+VAL GLN -0.189286
+TRP GLN -0.194694
+TYR GLN -0.175765
+ALA GLN -0.029745
+GLY GLN  0.001352
+THR GLN -0.031097
+SER GLN  0.024337
+ASN GLN -0.005408
+GLN GLN  0.017577
+CYS ASP -0.100051
+MET ASP -0.121684
+PHE ASP -0.244719
+ILE ASP -0.202806
+LEU ASP -0.233903
+VAL ASP -0.109515
+TRP ASP -0.158189
+TYR ASP -0.147372
+ALA ASP -0.004056
+GLY ASP  0.010816
+THR ASP -0.017577
+SER ASP  0.005408
+ASN ASP -0.001352
+GLN ASP  0.028393
+ASP ASP  0.062194
+CYS GLU -0.081122
+MET GLU -0.164949
+PHE GLU -0.255536
+ILE GLU -0.216326
+LEU GLU -0.259592
+VAL GLU -0.135204
+TRP GLU -0.178469
+TYR GLU -0.151428
+ALA GLU  0.021633
+GLY GLU  0.060842
+THR GLU -0.009464
+SER GLU  0.025689
+ASN GLU  0.021633
+GLN GLU  0.033801
+ASP GLU  0.087883
+GLU GLU  0.102755
+CYS HIS -0.260944
+MET HIS -0.312321
+PHE HIS -0.419132
+ILE HIS -0.333954
+LEU HIS -0.388035
+VAL HIS -0.258240
+TRP HIS -0.312321
+TYR HIS -0.250127
+ALA HIS -0.100051
+GLY HIS -0.064898
+THR HIS -0.101403
+SER HIS -0.059490
+ASN HIS -0.055434
+GLN HIS -0.041913
+ASP HIS -0.087883
+GLU HIS -0.064898
+HIS HIS -0.186582
+CYS ARG -0.121684
+MET ARG -0.196046
+PHE ARG -0.312321
+ILE ARG -0.265000
+LEU ARG -0.319081
+VAL ARG -0.189286
+TRP ARG -0.235255
+TYR ARG -0.201454
+ALA ARG -0.021633
+GLY ARG -0.006760
+THR ARG -0.031097
+SER ARG  0.006760
+ASN ARG  0.004056
+GLN ARG -0.017577
+ASP ARG -0.083826
+GLU ARG -0.081122
+HIS ARG -0.066250
+ARG ARG  0.016224
+CYS LYS -0.037857
+MET LYS -0.109515
+PHE LYS -0.228495
+ILE LYS -0.181173
+LEU LYS -0.229847
+VAL LYS -0.110867
+TRP LYS -0.137908
+TYR LYS -0.125740
+ALA LYS  0.048673
+GLY LYS  0.070306
+THR LYS  0.048673
+SER LYS  0.083826
+ASN LYS  0.062194
+GLN LYS  0.051378
+ASP LYS -0.001352
+GLU LYS -0.017577
+HIS LYS  0.043265
+ARG LYS  0.146020
+LYS LYS  0.209566
+CYS PRO -0.189286
+MET PRO -0.240663
+PHE PRO -0.348826
+ILE PRO -0.282576
+LEU PRO -0.342066
+VAL PRO -0.223087
+TRP PRO -0.278520
+TYR PRO -0.205510
+ALA PRO -0.048673
+GLY PRO -0.027041
+THR PRO -0.031097
+SER PRO  0.013520
+ASN PRO  0.018929
+GLN PRO -0.008112
+ASP PRO  0.045969
+GLU PRO  0.055434
+HIS PRO -0.078418
+ARG PRO -0.004056
+LYS PRO  0.094643
+PRO PRO -0.010816"""
+
+epsilon_mj = dict()
+
+def __add_value(key,v):
+    if key not in epsilon_mj:
+        epsilon_mj[key] = v
+    else:
+        assert(epsilon_mj[key] == v)
+
+for line in _txtdata.split('\n'):
+    n1,n2,v = line.split()
+    v = float(v)
+    
+    __add_value((n1,n2), v)
+    __add_value((n2,n1), v)
+    
+if __name__ == '__main__':
+    print(epsilon_mj)
-- 
GitLab