diff --git a/arbdmodel.py b/arbdmodel.py index fb2ed3167114e2e662e2b27b3c3c47b06cf5d552..aab8e96e06f557f5318182509c40a97f80dd22fc 100644 --- a/arbdmodel.py +++ b/arbdmodel.py @@ -81,6 +81,16 @@ class Parent(): x.parent = self self.children.append(x) + def clear_all(self, keep_children=False): + if keep_children == False: + for x in self.children: + x.parent = None + self.children = [] + self.bonds = [] + self.angles = [] + self.dihedrals = [] + self.exclusions = [] + def remove(self,x): if x in self.children: self.children.remove(x) @@ -462,6 +472,13 @@ class ArbdModel(PdbModel): self.cacheUpToDate = False + def clear_all(self, keep_children=False): + Parent.clear_all(self, keep_children=keep_children) + self.particles = [] + self.numParticles = 0 + self.type_counts = None + self._nbParamFiles = [] + self._written_bond_files = dict() def _getNbScheme(self, typeA, typeB): scheme = None diff --git a/coords.py b/coords.py index 0a4d4fb38c075904801be00b415e92dab06e4fc9..368b32d70d78ad2bc0cc80d018d4064adfbebed7 100644 --- a/coords.py +++ b/coords.py @@ -13,7 +13,7 @@ def minimizeRmsd(coordsB, coordsA, weights=None, maxIter=100): while tol > 1e-6: q,cB,comA = _minimizeRmsd(cNext,coordsA, weights) - R = R.dot(quaternionToMatrix3(q)) + R = R.dot(quaternion_to_matrix(q)) assert( np.all(np.isreal( R )) ) comB += cB @@ -32,7 +32,7 @@ def minimizeRmsd(coordsB, coordsA, weights=None, maxIter=100): def minimizeRmsd(coordsB, coordsA, weights=None): q,comA,comB = _minimizeRmsd(coordsB, coordsA, weights) assert( np.all(np.isreal( q )) ) - return quaternionToMatrix3(q),comA,comB + return quaternion_to_matrix(q),comA,comB ## http://onlinelibrary.wiley.com/doi/10.1002/jcc.21439/full @@ -92,7 +92,7 @@ def _minimizeRmsd(coordsB, coordsA, weights=None): # print("CHECK:", K.dot(q)-vals[i]*q ) return q, comB, comA -def quaternionToMatrix3(q): +def quaternion_to_matrix(q): assert(len(q) == 4) ## It looks like the wikipedia article I used employed a less common convention for q (see below @@ -109,10 +109,48 @@ def quaternionToMatrix3(q): return np.array(R) +def quaternion_from_matrix( R ): + e1 = R[0] + e2 = R[1] + e3 = R[2] + + # d1 = 0.5 * np.sqrt( 1+R[0,0]+R[1,1]+R[2,2] ) + # d2 = 0.5 * np.sqrt( 1+R[0,0]-R[1,1]-R[2,2] ) + # d2 = 0.5 * np.sqrt( 1+R[0,0]-R[1,1]-R[2,2] ) + # d2 = 0.5 * np.sqrt( 1+R[0,0]-R[1,1]-R[2,2] ) + + d1 = 1+R[0,0]+R[1,1]+R[2,2] + d2 = 1+R[0,0]-R[1,1]-R[2,2] + d2 = 1+R[0,0]-R[1,1]-R[2,2] + d2 = 1+R[0,0]-R[1,1]-R[2,2] + + # minD = min((d1,d2,d3,d3)) + maxD = max((d1,d2)) + + ## TODO: add more approaches TypeError: 0 < idim < 11 must hold + d = 0.5 / np.sqrt(maxD) + if d1 == maxD: + return np.array(( 1.0/(4*d), + d * (R[2,1]-R[1,2]), + d * (R[0,2]-R[2,0]), + d * (R[1,0]-R[0,1]) )) + elif d2 == maxD: + return np.array(( d * (R[2,1]-R[1,2]), + 1.0/(4*d), + d * (R[0,1]+R[1,0]), + d * (R[0,2]+R[2,0]) )) + def rotationAboutAxis(axis,angle, normalizeAxis=True): if normalizeAxis: axis = axis / np.linalg.norm(axis) angle *= 0.5 * np.pi/180 cos = np.cos( angle ) sin = np.sin( angle ) q = [cos] + [sin*x for x in axis] - return quaternionToMatrix3(q) + return quaternion_to_matrix(q) + +def readArbdCoords(fname): + coords = [] + with open(fname) as fh: + for line in fh: + coords.append([float(x) for x in line.split()[1:]]) + return np.array(coords) diff --git a/segmentmodel.py b/segmentmodel.py index a67bfdd2f5e59f90a6f4b495baa34bd8b3f88794..b3c14caee58329f4a71f2ba48759b9108d7e5b0e 100644 --- a/segmentmodel.py +++ b/segmentmodel.py @@ -1,13 +1,15 @@ import numpy as np from arbdmodel import PointParticle, ParticleType, Group, ArbdModel -from coords import rotationAboutAxis +from coords import rotationAboutAxis, quaternion_from_matrix, quaternion_to_matrix from nonbonded import * from copy import copy, deepcopy from nbPot import nbDnaScheme from scipy.special import erf import scipy.optimize as opt +from scipy import interpolate +import types """ TODO: @@ -94,6 +96,41 @@ class Segment(ConnectableElement, Group): self.start_position = start_position self.end_position = end_position + ## Set up interpolation for positions + a = np.array([self.start_position,self.end_position]).T + tck, u = interpolate.splprep( a, u=[0,1], s=0, k=1) + self.position_spline_params = tck + + + def contour_to_position(self,s): + p = interpolate.splev( s, self.position_spline_params ) + if len(p) > 1: p = np.array(p).T + return p + + def contour_to_tangent(self,s): + t = interpolate.splev( s, self.position_spline_params, der=1 ) + if len(t) > 1: t = np.array(t).T + return t + + + def contour_to_orientation(self,s): + if self.start_orientation is not None: + # axis = self.start_orientation.dot( np.array((0,0,1)) ) + if self.quaternion_spline_params is None: + axis = self.contour_to_tangent(s) + # orientation = rotationAboutAxis( axis, s*self.twist_per_nt*self.num_nts, normalizeAxis=False ) + orientation = rotationAboutAxis( axis, s*self.twist_per_nt*self.num_nts, normalizeAxis=False ) + ## TODO: ensure this is correct + # orientation = self.start_orientation.dot(orientation) # .dot( self.start_orientation ) + orientation = orientation.dot( self.start_orientation ) + else: + q = interpolate.splev(s, self.quaternion_spline_params) + orientation = quaternion_to_matrix(q) + else: + orientation = None + return orientation + + def _get_num_beads(self, max_basepairs_per_bead, max_nucleotides_per_bead ): raise NotImplementedError @@ -143,13 +180,11 @@ class Segment(ConnectableElement, Group): """ Generate beads (positions, types, etcl) and bonds, angles, dihedrals, exclusions """ ## TODO: decide whether to remove bead_model argument + ## (currently unused) # self._bead_model_generation += 1 # self._bead_model_max_nts_per_bead = max_nts_per_bead - direction = self.end_position - self.start_position - - num_beads = self._get_num_beads( max_basepairs_per_bead, max_nucleotides_per_bead ) nts_per_bead = float(self.num_nts)/num_beads twist_per_bead = nts_per_bead * self.twist_per_nt @@ -159,7 +194,8 @@ class Segment(ConnectableElement, Group): if num_beads <= 2: ## not yet implemented for dsDNA assert( isinstance(self, SingleStrandedSegment) ) - b = self._generate_one_bead(self.start_position, + b = self._generate_one_bead(0.5, + self.start_position, self.num_nts, self.start_orientation) self.children.append(b) @@ -167,7 +203,6 @@ class Segment(ConnectableElement, Group): self._assign_particles_to_locations() return - assert( np.linalg.norm(direction) > 0 ) for i in range(num_beads+1): nts = nts_per_bead @@ -175,7 +210,7 @@ class Segment(ConnectableElement, Group): nts *= 0.5 s = i*float(nts_per_bead)/(self.num_nts) # contour - pos = direction * s + self.start_position + pos = self.contour_to_position(s) if self.start_orientation is not None: axis = self.start_orientation.dot( np.array((0,0,1)) ) orientation = rotationAboutAxis( axis, s*self.twist_per_nt*self.num_nts, normalizeAxis=False ) @@ -185,7 +220,7 @@ class Segment(ConnectableElement, Group): else: orientation = None - b = self._generate_one_bead(pos,nts,orientation) + b = self._generate_one_bead(s,pos,nts,orientation) self.children.append(b) self.beads.append(b) # don't add orientation bead @@ -242,6 +277,15 @@ class DoubleStrandedSegment(Segment): self.end5 = Location( self, address=-1, type_= "end5" ) self.end3 = Location( self, address=-1, type_ = "end3" ) + ## Set up interpolation for azimuthal angles + a = np.array([self.start_position,self.end_position]).T + tck, u = interpolate.splprep( a, u=[0,1], s=0, k=1) + self.position_spline_params = tck + + ## TODO: initialize sensible spline for orientation + self.quaternion_spline_params = None + + ## Convenience methods def connect_start5(self, end3, type_="intrahelical", force_connection=False): if isinstance(end3, SingleStrandedSegment): @@ -275,7 +319,7 @@ class DoubleStrandedSegment(Segment): def _get_num_beads(self, max_basepairs_per_bead, max_nucleotides_per_bead): return (self.num_nts // max_basepairs_per_bead) + 1 - def _generate_one_bead(self, pos, nts, orientation=None): + def _generate_one_bead(self, contour_position, pos, nts, orientation=None): if self.local_twist: assert(orientation is not None) # opos = pos + np.array((2,0,0)).dot(orientation) @@ -283,11 +327,14 @@ class DoubleStrandedSegment(Segment): o = PointParticle( Segment.orientation_particle, opos, nts, num_nts=nts, parent=self ) bead = PointParticle( Segment.dsDNA_particle, pos, nts, - num_nts=nts, parent=self, orientation_bead=o ) + num_nts=nts, parent=self, + orientation_bead=o, + contour_position=contour_position ) else: bead = PointParticle( Segment.dsDNA_particle, pos, nts, - num_nts=nts, parent=self ) + num_nts=nts, parent=self, + contour_position=contour_position ) return bead @@ -359,9 +406,10 @@ class SingleStrandedSegment(Segment): # pdb.set_trace() return (self.num_nts // max_nucleotides_per_bead) + 1 - def _generate_one_bead(self, pos, nts, orientation=None): + def _generate_one_bead(self, contour_position, pos, nts, orientation=None): return PointParticle( Segment.ssDNA_particle, pos, nts, - num_nts=nts, parent=self ) + num_nts=nts, parent=self, + contour_position=contour_position ) def _assign_particles_to_locations(self): self.start.particle = self.children[0] @@ -392,7 +440,7 @@ class SegmentModel(ArbdModel): self._bonded_potential = dict() # cache bonded potentials - self._generate_bead_model(segments, max_basepairs_per_bead, max_nucleotides_per_bead, local_twist) + self._generate_bead_model( max_basepairs_per_bead, max_nucleotides_per_bead, local_twist) self.useNonbondedScheme( nbDnaScheme ) @@ -526,14 +574,72 @@ class SegmentModel(ArbdModel): fitFun = lambda x: np.real(erf( (4*np.pi*x + 1j)/(2*np.sqrt(x)) )) * np.exp(-1/(4*x)) / erf(2*np.sqrt(x)*np.pi) - np.exp(-sep/Lp) k = opt.leastsq( fitFun, x0=np.exp(-sep/Lp) ) return k[0][0] * 2*kT*0.00030461742 + + # def _update_segment_positions(self, bead_coordinates): + # """ Set new function for each segments functions + # contour_to_position and contour_to_orientation """ + + # dsDnaHelixNeighborDist=50 + # dsDnaAllNeighborDist=30 + # ssDnaHelixNeighborDist=25 + # ssDnaAllNeighborDist=25 + # beads = [b in s.beads for s in self.segments] + # positions = np.array([b.position for b in beads]) + # neighborhood = dict() + + # ## Assign neighborhood to each bead + # for b in beads: + # dists = b.position[np.newaxis,:] - positions + # dists = np.linalg.norm(dists, axis=-1) + # neighborhood[b] = np.where( dists < 50 ) + + """ Mapping between different resolution models """ + def _clear_beads(self): + for s in self.segments: + s.clear_all() + s.beads = [] + self.clear_all(keep_children=True) + + def _update_segment_positions(self, bead_coordinates): + """ Set new function for each segments functions + contour_to_position and contour_to_orientation """ - def _generate_bead_model(self, segments, + for s in self.segments: + beads = [b for b in s.beads] + ids = [b.idx for b in beads] + + """ Get positions """ + positions = bead_coordinates[ids,:].T + contours = [b.contour_position for b in beads] + tck, u = interpolate.splprep( positions, u=contours, s=0, ) + + s.position_spline_params = tck + + """ Get twist """ + if 'orientation_bead' in beads[0].__dict__: + tangents = s.contour_to_tangent(contours) + quats = [] + for b,t in zip(beads,tangents): + o = b.orientation_bead + angleVec = o.position - b.position + angleVec = angleVec - angleVec.dot(t)*t + angleVec = angleVec/np.linalg.norm(angleVec) + y = np.cross(t,angleVec) + quats.append( quaternion_from_matrix( np.array([t,y,angleVec])) ) + quats = np.array(quats) + tck, u = interpolate.splprep( quats.T, u=contours, s=0, ) + s.quaternion_spline_params = tck + + + ## TODO: set twist + + def _generate_bead_model(self, max_basepairs_per_bead = 7, max_nucleotides_per_bead = 4, - local_twist=False - ): + local_twist=False): + segments = self.segments """ Generate beads """ if self.DEBUG: print("Generating beads") @@ -715,6 +821,9 @@ class SegmentModel(ArbdModel): pot = self.get_bond_potential(4,18.5) self.add_bond(b1,b2, pot) + self._updateParticleOrder() + + # def get_bead(self, location): # if type(location.container) is not list: # s = self.segments.index(location.container)