Skip to content
Snippets Groups Projects
refine.py 13.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • cmaffeo2's avatar
    cmaffeo2 committed
    # -*- coding: utf-8 -*-
    from pathlib import Path
    import numpy as np
    from scipy.optimize import curve_fit
    import sys, os
    
    ## Local imports
    from arbdmodel import ArbdModel, ParticleType, PointParticle, Group, get_resource_path
    from arbdmodel.abstract_polymer import PolymerSection, AbstractPolymerGroup
    from arbdmodel.interactions import TabulatedPotential, HarmonicBond, HarmonicAngle, HarmonicDihedral
    from arbdmodel.coords import quaternion_to_matrix, readArbdCoords
    
    import MDAnalysis as mda
    from gridData import Grid
    from writeDx import writeDx
    
    """Define particle types"""
    
    n_replicas = 10
    
    ## units "295 k K/(160 amu * 1.24/ps)" "AA**2/ns"
    ## units "295 k K/(180 amu * 1.24/ps)" "AA**2/ns"
    _P = ParticleType("P",
                     diffusivity = 1621,
                     mass = 121,
                     radius = 5,
                     nts = 0.5      # made compatible with nbPot
    )
    
    _B = ParticleType("B",
                     diffusivity = 1093,
                     mass = 181,    # thymine
                     radius = 3,                 
                     nts = 0.5      # made compatible with nbPot
    )
    
    class DnaStrandFromPolymer(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 = '../../common/two_bead_model/BPB.dat', exclude = True )
    
        def __init__(self, polymer, **kwargs):
            self.polymer = polymer
            Group.__init__(self, **kwargs)
            
        def _clear_beads(self):
            ...
            
        def _generate_beads(self):
            nts = self.nts = 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)
                o = self.polymer.contour_to_orientation(c)
                
                new = DnaStrandFromPolymer.nt.duplicate()
                new.orientation = o
                new.position = r
                self.add(new)
    
            ## Two consecutive nts 
            for i in range(len(nts)-1):
                p1,b1 = nts[i].children
                p2,b2 = nts[i+1].children
                self.add_bond( i=b1, j=p2, bond = '../../common/two_bead_model/BBP.dat', exclude=True )
                self.add_bond( i=p1, j=p2, bond = '../../common/two_bead_model/BPP.dat', exclude=True )
                self.add_angle( i=p1, j=p2, k=b2, angle = '../../common/two_bead_model/p1p2b2.dat' )
                self.add_angle( i=b1, j=p2, k=b2, angle = '../../common/two_bead_model/b1p2b2.dat' )
                self.add_dihedral( i=b1, j=p1, k=p2, l=b2, dihedral = '../../common/two_bead_model/b1p1p2b2.dat' )
                self.add_exclusion( i=b1, j=b2 )
                self.add_exclusion( i=p1, j=b2 )
    
            ## Three consecutive nts 
            for i in range(len(nts)-2):
                p1,b1 = nts[i].children
                p2,b2 = nts[i+1].children
                p3,b3 = nts[i+2].children
                self.add_angle( i=p1, j=p2, k=p3, angle = '../../common/two_bead_model/p1p2p3.dat' )
                self.add_angle( i=b1, j=p2, k=p3, angle = '../../common/two_bead_model/b1p2p3.dat' )
                self.add_dihedral( i=b1, j=p2, k=p3, l=b3, dihedral = '../../common/two_bead_model/b1p2p3b3.dat' )
                self.add_exclusion( i=p1, j=p3 )
                self.add_exclusion( i=b1, j=p3 )
    
            ## Four consecutive nts 
            for i in range(len(nts)-3):
                p1,b1 = nts[i].children
                p2,b2 = nts[i+1].children
                p3,b3 = nts[i+2].children
                p4,b4 = nts[i+3].children
                self.add_dihedral( i=p1, j=p2, k=p3, l=p4, dihedral = '../../common/two_bead_model/p0p1p2p3.dat' )
    
    class IndependentDnaStrandFromPolymer(DnaStrandFromPolymer):
    
        particle_type_dict = {}
        
        def __init__(self, polymer, index, grid_path, **kwargs):
            if index not in IndependentDnaStrandFromPolymer.particle_type_dict:
    
                _P = ParticleType("P{:03d}".format(index),
                                  diffusivity = 1621,
                                  mass = 121,
                                  radius = 5,
                                  nts = 0.5,      # made compatible with nbPot
                                  grid=[('../{}/grid-P.dx'.format(grid_path), 0.57827709)]
                                  )
                _B = ParticleType("B{:03d}".format(index),
                                  diffusivity = 1093,
                                  mass = 181,    # thymine
                                  radius = 3,                 
                                  nts = 0.5,      # made compatible with nbPot
                                  grid=[('../{}/grid-B.dx'.format(grid_path), 0.57827709)]
                                  )
                IndependentDnaStrandFromPolymer.particle_type_dict[index] = (_P,_B)
            _P,_B = self.types = IndependentDnaStrandFromPolymer.particle_type_dict[index]
            p = PointParticle(_P, (0,0,0), "P")
            b = PointParticle(_B, (3,0,1), "B")
            
            self.nt = nt = Group( name = "nt", children = [p,b])
            nt.add_bond( i=p, j=b, bond = '../../common/two_bead_model/BPB.dat', exclude = True )
            
            self.polymer = polymer
            Group.__init__(self, **kwargs)
            
        def _clear_beads(self):
            ...
            
        def _generate_beads(self):
            nts = self.nts = 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)
                o = self.polymer.contour_to_orientation(c)
                
                new = self.nt.duplicate()
                new.orientation = o
                new.position = r
                self.add(new)
    
            ## Two consecutive nts 
            for i in range(len(nts)-1):
                p1,b1 = nts[i].children
                p2,b2 = nts[i+1].children
                self.add_bond( i=b1, j=p2, bond = '../../common/two_bead_model/BBP.dat', exclude=True )
                self.add_bond( i=p1, j=p2, bond = '../../common/two_bead_model/BPP.dat', exclude=True )
                self.add_angle( i=p1, j=p2, k=b2, angle = '../../common/two_bead_model/p1p2b2.dat' )
                self.add_angle( i=b1, j=p2, k=b2, angle = '../../common/two_bead_model/b1p2b2.dat' )
                self.add_dihedral( i=b1, j=p1, k=p2, l=b2, dihedral = '../../common/two_bead_model/b1p1p2b2.dat' )
                self.add_exclusion( i=b1, j=b2 )
                self.add_exclusion( i=p1, j=b2 )
    
            ## Three consecutive nts 
            for i in range(len(nts)-2):
                p1,b1 = nts[i].children
                p2,b2 = nts[i+1].children
                p3,b3 = nts[i+2].children
                self.add_angle( i=p1, j=p2, k=p3, angle = '../../common/two_bead_model/p1p2p3.dat' )
                self.add_angle( i=b1, j=p2, k=p3, angle = '../../common/two_bead_model/b1p2p3.dat' )
                self.add_dihedral( i=b1, j=p2, k=p3, l=b3, dihedral = '../../common/two_bead_model/b1p2p3b3.dat' )
                self.add_exclusion( i=p1, j=p3 )
                self.add_exclusion( i=b1, j=p3 )
    
            ## Four consecutive nts 
            for i in range(len(nts)-3):
                p1,b1 = nts[i].children
                p2,b2 = nts[i+1].children
                p3,b3 = nts[i+2].children
                p4,b4 = nts[i+3].children
                self.add_dihedral( i=p1, j=p2, k=p3, l=p4, dihedral = '../../common/two_bead_model/p0p1p2p3.dat' )
    
    class DnaModel(ArbdModel):
        def __init__(self, polymers, grid_path, num_polymers_per_replica,
                     DEBUG=False,
                     **kwargs):
    
            kwargs['particle_integrator'] = 'Langevin'
            kwargs['extra_bd_file_lines'] = 'ParticleLangevinIntegrator BAOAB'
            kwargs['timestep'] = 20e-6
            kwargs['temperature'] = 291
            kwargs['cutoff'] = 35
            kwargs['pairlist_distance'] = 60
            kwargs['decomp_period'] = 1000
            
            self.polymer_group = AbstractPolymerGroup(polymers)
            self.strands = [IndependentDnaStrandFromPolymer(p,i//num_polymers_per_replica, grid_path)
                            for i,p in enumerate(self.polymer_group.polymers)]
            ArbdModel.__init__(self, self.strands, **kwargs)
            self.nbSchemes = []
    
            processed = set()
            for strand in self.strands:
                if strand.types not in processed:
                    _P,_B = strand.types
                    self.useNonbondedScheme( TabulatedPotential('../../common/two_bead_model/NBBB.dat'), typeA=_B, typeB=_B )
                    self.useNonbondedScheme( TabulatedPotential('../../common/two_bead_model/NBPB.dat'), typeA=_P, typeB=_B )
                    self.useNonbondedScheme( TabulatedPotential( '../../common/two_bead_model/NBPP.dat'), typeA=_P, typeB=_P )
                    processed.add( strand.types )            
            self.generate_beads()
            
        def generate_beads(self):
            for s in self.strands:
                s._generate_beads()
            
    
    def run_round(index, last_coordinates = None, generate_grid=False):
    
        strands_per_replica = 31
        dimensions = [106.620003]*3
        name = 'many-strands'
    
        if generate_grid: 
            path = 'iter-{}'.format(index-1)
            try:
                last_coordinates = readArbdCoords('{}/output/{}.restart'.format(path,name))
            except:
                pass
            generate_new_grid( index-1, name )
        
        IndependentDnaStrandFromPolymer.particle_type_dict = {} # Ugly hack to clear cached particle types that have wrong grids
        strands = []
        for i in range(strands_per_replica*n_replicas):
            r0 = np.array( [(a-0.5)*b for a,b in 
                            zip( np.random.uniform(size=3), dimensions )] )
            r1 = r0 + (np.random.uniform(size=3)-0.5)*5*5
    
            s = PolymerSection("D{}".format(i), num_monomers=5, monomer_length=5,
                               start_position=r0, end_position=r1)
            strands.append(s)
    
        ## Randomly place strands through system
        model = DnaModel( strands, grid_path='grids-{}'.format(index), num_polymers_per_replica=strands_per_replica, dimensions=dimensions )
    
    
        if last_coordinates is not None:
            for p,c in zip([p for p in model],last_coordinates):
                p.position = c
                
        path = 'iter-{}'.format(index)
        model.simulate( output_name = name, output_period=1e3, num_steps=1e6, directory=path, gpu=1 ) # 20 ns
    
        coords = readArbdCoords('{}/output/{}.restart'.format(path,name))
        generate_new_grid( index, name )
        return coords
    
    def symmetrize_grid(asym):
        sym = np.array(asym)
        sym = sym + asym[::-1,:,::-1]
        sym = sym + asym[::-1,::-1,:]
        sym = sym + asym[:,::-1,::-1]
        sym = 0.25 * sym
        return sym
    
    target_density_grids = {c:Grid('target_density/grid-{}.dx'.format(c)) for c in ('P','B')}
    
    _last_grids = {c:None for c in ('P','B')}
    for k,g in target_density_grids.items():
        g.grid = symmetrize_grid(g.grid)
    
    def get_mask():
        if Path('mask.dx').exists():
            return Grid('mask.dx').grid
        from scipy.ndimage import gaussian_filter
        mask_grid = Grid('ssb-density.dx')
        mask = mask_grid.grid
        mask = gaussian_filter( mask, sigma=1.5/mask_grid.delta, mode='constant' )
        mask = symmetrize_grid(mask)
        mask = (mask - 0.001) / (0.025-0.001)
        mask[mask>1] = 1
        mask[mask<0] = 0
        mask = symmetrize_grid(mask)
        writeDx('mask.dx', mask,
                origin=mask_grid.origin, delta=mask_grid.delta, fmt='%.6f')
        return mask
    mask = get_mask()
        
    def get_avg_edge_value(u):
        return (u[0,:,:].mean() + u[-1,:,:].mean() +
                u[:,0,:].mean() + u[:,-1,:].mean() +
                u[:,:,0].mean() + u[:,:,-1].mean())/6
        
    def generate_initial_grid( d ):
        for bead_type in ('P','B'):
            target = target_density_grids[bead_type]
            u = np.array(target.grid)
            u = symmetrize_grid(u)
            u = -1.0*np.log( (u + 1e-15) ) * mask
            u = u - get_avg_edge_value(u)
            writeDx('{}/grid-{}.dx'.format(d,bead_type), u,
                    origin=target.origin, delta=target.delta, fmt='%.6f')
            _last_grids[bead_type] = Grid(u, origin=target.origin, delta=target.delta)
    
    def generate_new_grid( index, name ):
        d = 'grids-{}'.format(index+1)
        d_old = 'grids-{}'.format(index)
        try:
            os.makedirs(d)
        except:
            pass
    
        if index == 0:
            return generate_initial_grid(d)
        
        import subprocess
        from scipy.ndimage import gaussian_filter
        path = 'iter-{}'.format(index)
    
        vmdin = """
    set ID [mol new {path}/{name}.psf]
    mol addfile {path}/output/{name}.dcd beg 200 waitfor all
        
    foreach type "P B" {{
        set sel [atomselect $ID "name $type"]
        $sel set radius 3 
        volmap density $sel -o {path}/{name}.$type-density.dx -res 0.5 -combine avg -minmax "{{-38 -36 -50}} {{38 36 50}}" -allframes -checkpoint 0
    }}
        """.format( path=path, name=name )
        subprocess.run(['vmd','-dispdev','text'], input=vmdin, encoding='ascii', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    
        for bead_type in ('P','B'):
            density = Grid('{}/{}.{}-density.dx'.format(path,name,bead_type)).grid
            density = density / n_replicas
            density = symmetrize_grid(density)
    
            target = target_density_grids[bead_type] 
    
            dU = -1.0*np.log( (target.grid+0.5*1e-6)/(density+0.5*1e-6) ) * mask
            dU = dU - get_avg_edge_value(dU)
    
            """ ibi-combine """
            if _last_grids[bead_type] is None:
                _last_grids[bead_type] = Grid('{}/grid-{}.dx'.format(d_old,bead_type))
                
            ulast = _last_grids[bead_type].grid
            out = ulast+dU
            sl = (target.grid < 1e-6) | (out > 20)
            out[sl] = 20
    
            writeDx('{}/grid-{}.dx'.format(d,bead_type), out,
                    origin=target.origin, delta=target.delta, fmt='%.6f')
            _last_grids[bead_type] = Grid(out, origin=target.origin, delta=target.delta)
    
            
    if __name__ == "__main__":
        
        # """ #START
        c = None
        start=1
        for i in range(start,start+200):
            c = run_round( i, c, generate_grid = (i==start) )
            print("Round {}, c.shape {}".format(i,c.shape))
        """ #END """