def generateBuildingBlockDirector(self): if self.numPoints > 9: director = coords.axisFromHelix(self.buildingBlockXYZ) else: director = self.buildingBlockXYZ[-1] - self.buildingBlockXYZ[0] directorHat = director / np.linalg.norm(director) return directorHat
def generateBuildingBlockXYZ(self): # create the first residue in the BB if self.seedResidue == None: strand = self.startResidue() else: strand = self.seedResidue # tally the number of residues we added to strand numResidues = 1 # loop until we've added the right number of residues while numResidues < self.numResidues: # create new residue from the last three points residue = self.generateResidue(strand[-3:]) # add the new Residues to the strandVec list [strand.append(monomer) for monomer in residue] numResidues += 1 # perform final orientation depending on whether or not a seed residue was provided. # If seed was provided then don't fiddle with orientation at all. # if seed wasn't provided then return the strand with it's centre of mass # at the origin and it's helical axis pointing along the z-axis if self.seedResidue == None and self.numResidues > 3: # This structure was created in such a way that the first two atoms are # aligned with Z vector. We want the structure to be returned so it is at it's # centre of mass and also with the helical axis pointing up the z axis. # We will use the transformation from Lab to Block to achieve this, # with a little bit of hacking of the member variables. # find the helix axis and the centre of mass of the construct # as it is right now. currentDirector = coords.axisFromHelix(strand) currentDirectorHat = currentDirector / np.linalg.norm( currentDirector) currentCOM = cart.getCentreOfMass(strand) # Now specify the orientation and position that we want to map to. # store the refPoint as a member variable. targetRefPoint = np.array([0.0, 0.0, 0.0]) targetDirectorHat = np.array([0.0, 0.0, 1.0]) # modify the strand accordingly with the universal transformation function strand = coords.transformFromBlockFrameToLabFrame( targetDirectorHat, targetRefPoint, 0.0, currentDirectorHat, currentCOM, strand) return list( np.around(strand, 12) ) # round the positions to 12.dp - cleans up machine precision noise on zeroes
def generateSpaceCurve(self): # generates a space curve with a specified bond angle, dihedrals and bondlength b = self.bondLength n = self.numPoints # generate 3 points a distance b apart one of which is pointA s2 = self.pointA - b * self.blockDirectorHat s1Dir = np.array([ rnd.uniform(0.0, 1.0), rnd.uniform(0.0, 1.0), rnd.uniform(0.0, 1.0) ]) s1 = s2 - b * s1Dir / np.linalg.norm(s1Dir) spaceCurve = [s1, s2, self.pointA] # have first point so add a further n-1 points for _ in range(n - 1): # construct TNB frame from last three points of the space curve tnb = coords.constructTNBFrame(spaceCurve[-3], spaceCurve[-2], spaceCurve[-1]) if self.angularRange[0] == 'None': # compute a new direction based on beta and alpha alpha = self.alpha beta = self.beta else: # if the angular range is set then pick a random direction. (hope it's not too madly self intersecting. Got a bit chill about that later on. # careful narrow choices of ranges can give good results without checking for intersections. alpha = rnd.uniform(self.angularRange[0], self.angularRange[1]) beta = rnd.uniform(self.angularRange[2], self.angularRange[3]) dirn = coords.generateTNBVecXYZ(tnb, beta, alpha) # construct next space curve point spaceCurve.append(spaceCurve[-1] + b * dirn / np.linalg.norm(dirn)) currentAxis = coords.axisFromHelix(spaceCurve[2:]) currentRefPoint = self.pointA spaceCurve = coords.transformFromBlockFrameToLabFrame( self.blockDirectorHat, self.pointA, 0.0, currentAxis, currentRefPoint, spaceCurve[2:]) return spaceCurve
def generateSpaceCurve(self): # generates a space curve with specified bondLength but with bond angles and dihedrals # chosen from a range b = self.bondLength n = self.numPoints # generate 3 points a distance b apart one of which is pointA s2 = self.pointA - b * self.blockDirectorHat s1Dir = np.array([ rnd.uniform(0.0, 1.0), rnd.uniform(0.0, 1.0), rnd.uniform(0.0, 1.0) ]) s1 = s2 - b * s1Dir / np.linalg.norm(s1Dir) spaceCurve = [s1, s2, self.pointA] # have first point so add a further n-1 points for _ in range(n - 1): # construct TNB frame from last three points of the space curve tnb = coords.constructTNBFrame(spaceCurve[-3], spaceCurve[-2], spaceCurve[-1]) beta = rnd.uniform(self.beta1, self.beta2) alpha = rnd.uniform(self.alpha1, self.alpha2) # compute a new direction based on beta and alpha dirn = coords.generateTNBVecXYZ(tnb, beta, alpha) # construct next space curve point spaceCurve.append(spaceCurve[-1] + b * dirn / np.linalg.norm(dirn)) # chop the starting points off spaceCurve = spaceCurve[2:] if self.SpaceCurveTransform == True: currentAxis = coords.axisFromHelix(spaceCurve) currentRefPoint = self.pointA spaceCurve = coords.transformFromBlockFrameToLabFrame( self.blockDirectorHat, self.pointA, 0.0, currentAxis, currentRefPoint, spaceCurve) return spaceCurve
def connectBackboneSegments(backboneSet, connectorDictList): ''' connects together a set of polymer building block objects according to the connection information in the connectorDictList. The order of the segments is defined by the order they appear in the backboneSet list. If there are N segments there must be N-1 connector objects with the link information. The connector information contains the angle between polymer directors, and the azimuthal rotation between the directors of each segment, as well as the distance from the last monomer of the first polymer and the first monomer of the second polymer, in the direction of the first polymer's labdirector. The first two segments are always contained in the zx plane, and the first polymer is always aligned with the z axis. Setting all the azimuthal rotations to zero thus keeps all subsequent segments in the zx plane which is useful for 2D figures, and for defining a zero plane to put side brushes in later on.''' # intialise the first labdirector, TNB frame and start point labDirector = np.array([0.0, 0.0, 1.0]) labBidirector = np.array([1.0, 0.0, 0.0]) # initial bi-director along the x axis. labNormDirector = np.cross(labDirector, labBidirector) zeroPoint = np.array([0.0, 0.0, 0.0]) # initialise the output arrays with data from the first segment. backboneXYZ = backboneSet[0].blockXYZVals backboneNames = backboneSet[0].blockAtomNames backboneIndexList = [len(backboneXYZ)] backboneTNBList = [ (cp.copy(labDirector), cp.copy(labNormDirector), cp.copy(labBidirector) ) ] # find the principal axis of the first segment blockDirector = coords.axisFromHelix(backboneXYZ) # rotate the xyz vals to the z-axis lab director and translate the first point to the zeroPoint as defined backboneXYZ = coords.transformFromBlockFrameToLabFrame(labDirector, zeroPoint, 0.0, blockDirector, backboneXYZ[0], backboneXYZ) # loop through the connectors and remaining segments orienting them according to the instructions in the connector list for connector, newBackboneSegmentBB in zip(connectorDictList, backboneSet[1:]): # generate unit TNB frame for the last segment TNB = [labDirector, labNormDirector, labBidirector] # compute the lab director for the new block and connector information, which is defined relative to end of last polymer. newLabDirector = coords.generateTNBVecXYZ(TNB, connector['beta']*np.pi/180.0, connector['alpha']*np.pi/180.0) # compute the zero point for the new block to attach to the end of the polymer backbone as it is just now zeroPoint = backboneXYZ[-1] + connector['displacement'] * newLabDirector # find the current principal axis of the new segment blockDirector = coords.axisFromHelix(newBackboneSegmentBB.blockXYZVals) # rotate the xyz vals to the z-axis lab director and translate the first point to the zeroPoint as defined newBackboneXYZ = coords.transformFromBlockFrameToLabFrame(newLabDirector, zeroPoint, 0.0, blockDirector, newBackboneSegmentBB.blockXYZVals[0], newBackboneSegmentBB.blockXYZVals) # generate new TNB frame for the next segment labNormDirector = np.cross(labDirector, newLabDirector) labBidirector = np.cross(labNormDirector, newLabDirector) labDirector = newLabDirector # add the output information to the output arrays backboneXYZ = np.concatenate((backboneXYZ, newBackboneXYZ), 0) backboneNames = np.concatenate((backboneNames, newBackboneSegmentBB.blockAtomNames), 0) backboneIndexList.append(len(newBackboneXYZ)) backboneTNBList.append((cp.copy(newLabDirector), cp.copy(labNormDirector), cp.copy(labBidirector))) # return all the information that describes the backbone necessary for decorating it with side chains return (backboneXYZ, backboneNames, backboneIndexList, backboneTNBList)
def decoratePolymerBackbone(brushDictList, backboneInfo): # unpack input backboneXYZ = backboneInfo[0] backboneNames = backboneInfo[1] backboneSegmentLengths = backboneInfo[2] backboneTNBList = backboneInfo[3] # break the list of XYZ points into list of points for each segment backBoneXYZList = breakListToSubList(backboneXYZ, np.cumsum(np.concatenate((np.array([0]), backboneSegmentLengths), 0))) # set up output polymerXYZ = cp.copy(backboneXYZ) polymerNames = cp.copy(backboneNames) for brush, numBrushes, TNB, points in zip(brushDictList, backboneSegmentLengths, backboneTNBList, backBoneXYZList): if brush['numMonomers']>0: # create the brush generator. Default is polymer. Check to see if we want a peptide brush if 'Peptide' in brush['mode']: brushBBG = PBG(brush['filename']) brushBB = brushBBG.generateBuildingBlock(brush['numMonomers']) # only need to create this once for peptide backbones as they are either alpha or beta. brushBB.blockAtomNames = brush['monomerNames'] else: brushBBG = RPPBBG(brush['filename']) # Set the angle of each brush around the segment director defined relative to the Binormal in the TNB frame for that polymer. # The range of angles is defined in the dictionary. brushPhaseAngles = [ rnd.uniform(0, brush['phaseRange'] * np.pi/180.0) for _ in range(0, numBrushes) ] # if the word Alternate is in the mode than add 180 to every other phase if "Alternate" in brush['mode']: for index in range(0, numBrushes): if index % 2 ==0: brushPhaseAngles[index] += np.pi # if the word "mirror" is in the mode then add 180 randomly to half the brush phases if "Mirror" in brush['mode']: numFlipped = 0 indexList = list(range(0, numBrushes)) while numFlipped < numBrushes/2: index = rnd.choice(indexList) brushPhaseAngles[index] += np.pi numFlipped += 1 indexList.remove(index) # generate directors in direction of phase angles brushDirectors = [ np.cos(angle) * TNB[2] + np.sin(angle) * TNB[1] for angle in brushPhaseAngles] brushDirectorsNorm = [ d/np.linalg.norm(d) for d in brushDirectors] # for each of the points in the backbone generate a brush as defined for point, director in zip(points, brushDirectorsNorm): if "Polymer" in brush['mode'] and not "Peptide" in brush['mode']: # if we're doing a polymer then generate a new polymer with the given polymer parameters for each pass. polyStart = np.array([ 0.0, 0.0, brush['Z1']]) envelopeList = ['frustum ' + str(brush['Z1'] - brush['minDist']) + ' ' + str(brush['R1']) + ' ' + str(brush['Z2']) + ' ' + str(brush['R2'])] brushBB = brushBBG.generateBuildingBlock( brush['numMonomers'], polyStart, brush['alpha1'], brush['alpha2'], brush['beta1'], brush['beta2'], brush['minDist'], brush['bondLength'], envelopeList=envelopeList, visualiseEnvelope=(0, 100, brush['name'] + '_envelope.xyz')) # work out the block director if brush['numMonomers']>2: blockDirector = coords.axisFromHelix(brushBB.blockXYZVals) else: if brush['numMonomers']==2: blockDirector = brushBB.blockXYZVals[1] - brushBB.blockXYZVals[0] blockDirector = blockDirector/np.linalg.norm(blockDirector) if brush['numMonomers']==1: blockDirector = np.array([0.0, 0.0, 1.0]) # rotate the brush and set it at the right place brushXYZVals = coords.transformFromBlockFrameToLabFrame(director, point + brush['bondLength'] * director, 0.0, blockDirector, brushBB.blockXYZVals[0], brushBB.blockXYZVals) # concatenate the brush positions and the brush names to the output arrays polymerXYZ = np.concatenate( (polymerXYZ, brushXYZVals), 0) polymerNames = np.concatenate( (polymerNames, brush['monomerNames']), 0) return polymerXYZ, polymerNames
def GeneralPolymerBrush(brushDict, mode="RandomPolymer"): # unpack the brush dictionary filenameRandom = brushDict['filenameBlock'] filenameBrush = brushDict['filenameBrush'] mode = brushDict['mode'] ABlock = brushDict['ABlock'] num_A = ABlock['num'] alpha1_A = ABlock['alpha1'] alpha2_A = ABlock['alpha2'] beta1_A = ABlock['beta1'] beta2_A = ABlock['beta2'] minDist_A = ABlock['minDist'] bondLength_A = ABlock['bondLength'] Z1_A = ABlock['Z1'] R1_A = ABlock['R1'] Z2_A = ABlock['Z2'] R2_A = ABlock['R2'] BBlock = brushDict['BBlock'] num_B = BBlock['num'] alpha1_B = BBlock['alpha1'] alpha2_B = BBlock['alpha2'] beta1_B = BBlock['beta1'] beta2_B = BBlock['beta2'] minDist_B = BBlock['minDist'] bondLength_B = BBlock['bondLength'] Z1_B = BBlock['Z1'] R1_B = BBlock['R1'] Z2_B = BBlock['Z2'] R2_B = BBlock['R2'] brushBlock = brushDict['brushBlock'] num_brush = brushBlock['num'] alpha1_brush = brushBlock['alpha1'] alpha2_brush = brushBlock['alpha2'] beta1_brush = brushBlock['beta1'] beta2_brush = brushBlock['beta2'] minDist_brush = brushBlock['minDist'] bondLength_brush = brushBlock['bondLength'] Z1_brush = brushBlock['Z1'] R1_brush = brushBlock['R1'] Z2_brush = brushBlock['Z2'] R2_brush = brushBlock['R2'] # Mode word must specify one of Polymer or Peptide. Can also include modifiers Sheet or Random to specify phase of brushes around polymer backbone axis. # defaults to RandomPolymer. Can supply brush polymer parameters via parameter polymer params. These are alpha1, alpha2, beta1, beta2, minDist and bond length. # atom name supplied in filenameBrush. This is a mess. I know. I don't care. # if one of Peptide or Polymer is not in mode then add Polymer to the end of mode. Anything else is dandy. If you specify both peptide and polymer, then Peptide will override. if not ("Peptide" in mode or "Polymer" in mode): mode = mode + "Polymer" # generate the block Copolymer backbone (polymerXyzVals, polymerNames) = MBCP(num_A, num_B, Z1_A, R1_A, Z2_A, R2_A, alpha1_A, alpha2_A, beta1_A, beta2_A, minDist_A, bondLength_A, Z1_B, R1_B, Z2_B, R2_B, alpha1_B, alpha2_B, beta1_B, beta2_B, minDist_B, bondLength_B, filenameRandom) # get axis of block A BlockAAxis = coords.axisFromHelix(polymerXyzVals[0:num_A]) BlockAAxisNorm = BlockAAxis/np.linalg.norm(BlockAAxis) # get an orthogonal vector to BlockAAxis which is random. randDirXYZ = BlockAAxis randXYZIsAxizXYZ = True while randXYZIsAxizXYZ: randVecDir = coords.pickRandomPointOnUnitSphere() randDirXYZ = coords.sphericalPolar2XYZ(np.array([1.0, randVecDir[0], randVecDir[1]])) randXYZIsAxizXYZ = not False in [ np.abs(a - b) < 1e-7 for a, b in zip(randDirXYZ, BlockAAxis) ] OrthVec = randDirXYZ - np.dot(BlockAAxisNorm, randDirXYZ) * BlockAAxisNorm OrthVec1Norm = OrthVec/np.linalg.norm(OrthVec) OrthVec2Norm = np.cross(OrthVec1Norm, BlockAAxisNorm) # now have orthonormal basis for the polymer backbone for the brush part of system # create the brush generator. Default is polymer mode. If peptide is included in mode word then over ride to use peptide instead if "Peptide" in mode: # create a peptide generator using supplied filename to specify parameters of peptide - filename overides polymerParams brushGenerator = PBG(filenameBrush) brushObject = brushGenerator.generateBuildingBlock(num_brush) # only need to create this once for peptides as all are the same else: brushGenerator = RPPBBG(filenameBrush) # choose the phase angles of each brush # initially make the phase angle a little bit random brushPhaseAngles = [ rnd.uniform(0, 0.2 * np.pi) for _ in range(0, num_A) ] # if Random is in mode then make it very random if "Random" in mode: brushPhaseAngles = [ rnd.uniform(0, 2 * np.pi) for _ in range(0, num_A) ] # if sheet is in mode then make phase zero. if "Sheet" in mode: brushPhaseAngles = [ 0.0 for _ in range(0, num_A) ] # generate directors in direction of phase angles brushDirectors = [ np.cos(angle) * OrthVec1Norm + np.sin(angle) * OrthVec2Norm for angle in brushPhaseAngles] brushDirectorsNorm = [ d/np.linalg.norm(d) for d in brushDirectors] # for each of the directors (defined by the length of block A) figure out the final xyz vals for point, labDirector in zip(polymerXyzVals[0:num_A], brushDirectorsNorm): if "Polymer" in mode and not "Peptide" in mode: # if we're doing a polymer then generate a new polymer with the given polymer parameters for each pass. polyStart = np.array([ 0.0, 0.0, Z1_brush]) envelopeList = ['frustum ' + str(Z1_brush - minDist_brush) + ' ' + str(R1_brush) + ' ' + str(Z2_brush) + ' ' + str(R2_brush)] brushObject = brushGenerator.generateBuildingBlock( num_brush, polyStart, alpha1_brush, alpha2_brush, beta1_brush, beta2_brush, minDist_brush, bondLength_brush, envelopeList = envelopeList) newBrushXYZ = coords.transformFromBlockFrameToLabFrame(labDirector, point + minDist_A * labDirector, 0.0, brushObject.blockDirectorHat, brushObject.blockXYZVals[0], brushObject.blockXYZVals) polymerXyzVals = np.concatenate((polymerXyzVals, newBrushXYZ), 0) polymerNames = np.concatenate( (polymerNames, brushObject.blockAtomNames), 0 ) # direction of brush is z-axis. # reference point is the first of the B polymers, which is the numAth entry in the list of xyzVals return (np.array([0.0, 0.0, 1.0]), polymerXyzVals[num_A], polymerXyzVals, polymerNames)