def generateBuildingBlockXYZ(self): # create a building block generator using the standard parameter file for this object strandGen = PBG(self.paramFilename) # construct a prototype beta strand building block strandBB = strandGen.generateBuildingBlock(self.strandLength) # generate an array of strand building blocks BBs = [ strandBB.cloneBuildingBlock() for _ in range(0, self.numStrands) ] baseLine = [ self.startPos + n * self.betaStrandSeparation * self.crossStrandDirectorHat for n in range(self.numStrands) ] directors = [self.inStrandDirectorHat for n in range(self.numStrands)] rotation = [0.0 for n in range(self.numStrands)] # modify the strands if we are anti-parallel if not self.parallel: baseLine = [ basePoint if n % 2 == 0 else basePoint + self.offsetXYZ for n, basePoint in enumerate(baseLine) ] directors = [ self.inStrandDirectorHat if n % 2 == 0 else -1 * self.inStrandDirectorHat for n in range(self.numStrands) ] rotation = [ rot if n % 2 == 0 else 0 for n, rot in enumerate(rotation) ] # do the sheet construction: [ BB.transformBBToLabFrame(director, pos, rot) for director, pos, rot, BB in zip( directors, baseLine, rotation, BBs) ] # if we are adding loops then construct the loops if self.numLoopResidues > 0: Loops = self.constructLoops(BBs) # compile the final building block retBB = BBs[0] if self.numLoopResidues > 0: for BB, Loop in zip(BBs[1:], Loops): retBB.append(Loop) retBB.append(BB) else: [retBB.append(BB) for BB in BBs[1:]] return retBB.blockXYZVals
class peptideHairpinGenerator(CPBBG): ''' This class returns a randomly coiled peptide backbone between two sets of points A and B, which is packed into an external envelope. The backbone does not self-intersect and the coil avoids a user supplied set of external points. The class inherits the functionality of the constrainedPolymerClass. The generateSpaceCurve method of that class is overridden to provide an unfolded peptideBackbone. The user supplies three points for pointsA and pointsB. PointsA form the initial seed for the chain. The first point in A is non negotiable and fixed. But the second and third points are movable once the initial connected chain has been built. The main advantage of providing three points is so that derivatives as well as the end points match up, yielding a much smoother link between coils. PointsA and pointsB are supplied in the format m0, m1 and m2 where m0 is the innermost point of a connector. Thus point m2 (the third point in pointsA) is actually the start of the chain. This is important. Throughouth the project connectors are defined as m0, m1 and m2. The energy function is overloaded to compute the energy of two springs between the two atoms at the free end of the chain and the two atoms of the second anchor; Points B is then computed based on the distance between them. Conformations with smaller amounts of energy are found by picking C-CA or N-CA bonds at random and performing dihedral moves of the entire free end of chain from the selected bond onwards. (PS at some time point later this gradient thing was abandoned because it took too long. The code was greyed out in the PE function, but you can put it back in if you want). Each time a move is made the new energy of the two springs is calculated for the new position. If the move results in lower total spring energy than the lowest energy yet found it is accepted. If the spring energy is larger than the current minimum the move is accepted with a probability based on the boltzman factor between the current lowest energy and the new energy. As the distance between the end point and point B shrinks the allowed size range of rotations shrinks rapidly. The naming functions and allowedList functions are also overridden so that only the alpha carbons are allowed to be used in the dihedral twist rotations. This restriction preserves the peptide bond angles in the chain. Once a double anchored space curve is generated, with anchor points at pointsA and B, the procedure for randomising the coil with crankshaft moves and folding the chain into an envelope is the same as for the coiled polymer baseclass. The final structure is exported as a building block class.''' def __init__(self, paramFilename): # initialise the parameter dictionary for the base classes CPBBG.__init__(self, paramFilename) def initialiseParameters(self): # initialise the constrained polymer parent CPBBG.initialiseParameters(self) # load the backbone building object self.PBG = PBG(self.paramFilename) if self.noLoadErrors == False: print "Critical Parameters are undefined for hairpin" sys.exit() def generateBuildingBlock( self, numResidues, pointsA, pointsB, minDist, numCrankMoves, pointsToAvoid=[], visualiseEnvelope=(0,20), envelopeList=['None'], nameCA=False): self.numResidues = numResidues self.numPoints = self.numResidues * 3 bondLength = 0.0 # not used in this game self.nameCA = nameCA self.minDist = minDist # set up the right reference points self.pointA = pointsA[2] self.pointB = pointsB[2] self.pointsA = pointsA[:] self.pointsB = pointsB[:] self.parseEnvelopeList(envelopeList) self.blockRefPoint = self.generateBuildingBlockRefPoint() # prematurely add pointsToAvoid and parse the envelope due to the pointsA and pointsB checks below self.pointsToAvoid = pointsToAvoid # check starting points are legal or it's gonna be a long wait. for pos in self.pointsA: if not self.checkPointInBounds(pos): print "Error Warning: One of pointsA out of bounds" time.sleep(3) for pos in self.pointsB: if not self.checkPointInBounds(pos): print "Error Warning: One of pointsB out of bounds" time.sleep(3) return CPBBG.generateBuildingBlock(self, self.numPoints, pointsA[2], pointsB[2], minDist, bondLength, numCrankMoves, visualiseEnvelope=visualiseEnvelope, pointsToAvoid=pointsToAvoid, envelopeList=envelopeList) def generateAllowedList(self, short=False): # add the first and last points to the allowed list. # This is important to allow the full chain to be able to be mapped into the envelope. allowed = [0, self.numPoints-1] # over ride the naming of the atoms with ca=True. names = self.generateBuildingBlockNames(ca=True) # if the minimisation is already close then only make moves in the last ten CAs closest to the chain if short: [ allowed.insert(-1,i) for i, name in enumerate(names) if name=='CA' and i>(len(names) - 10) ] else: [ allowed.insert(-1,i) for i, name in enumerate(names) if name=='CA' ] return allowed def generateSpaceCurve(self): # Over-rides the generate space curve function of the parent to generate a peptide backbone # and a pseudo energy landscape approach to find an initial chain with the end point fixed # at point B. # create a regular backBone using PointsA as the first residue peptideBackbone = self.PBG.generateBuildingBlock(self.numResidues, seedResidue = [self.pointsA[2], self.pointsA[1], self.pointsA[0]]) return peptideBackbone.getAtomsXYZ() def PE(self, xyzVals): # Over load PE function to include two springs - between the last two points # ensures a smooth connection to other polymers PE = 0.0 # add spring between pointB[2] and end of xyzVals with equilibrium at pointB dist1 = np.linalg.norm(xyzVals[-1] - self.pointsB[2]) PE += 0.5 * 3 * self.springConstant * dist1**2 # add spring between pointsB[1] and end of xyzVals[-2] dist2 = 0.0 # dist2 = np.linalg.norm(xyzVals[-2] - self.pointsB[1]) # PE += 0.5 * 2 * self.springConstant * dist2**2 return PE, np.sqrt(dist1**2 + dist2**2) def generateBuildingBlockNames(self, ca=False): if self.nameCA: names = ['N', 'CA', 'C'] * self.numResidues else: names = ['N', 'C', 'C'] * self.numResidues # override the externally controlled member variable nameCA with a local variable if ca==True: names = ['N', 'CA', 'C'] * self.numResidues return names def generateBuildingBlockConnectors(self): # N connector first, then C Connector return [ [2, 1, 0], [self.numPoints-3, self.numPoints-2, self.numPoints-1] ]
def generateBuildingBlockConnectors(self): # N connector first, then C Connector return [ [2, 1, 0], [self.numPoints-3, self.numPoints-2, self.numPoints-1] ] if __name__ == "__main__": # get the file name from the command line filename = sys.argv[1] # create the generator objects. hairPinGen = peptideHairpinGenerator(filename) backboneGenerator = PBG(filename) # generate 1 residue seed building block seedResidue = backboneGenerator.generateBuildingBlock(1) # generate starting points and move the seed to those points extracting # the xyzVals each time. numResidues = 20 pointA = np.array([10.0, 10.0, 10.0]) pointB = np.array([0.0, 0.0, 0.0]) print "Estimate min num residues: ", np.linalg.norm(pointA-pointB)/3.5 seedResidue.placeAtom(2, pointA) seedResidue.setBlockRefPoint(pointA) seedResidue.orientToDirector(np.array([-1.0, 1.0, 0.0])) pointsA = seedResidue.blockXYZVals[:] seedResidue.placeAtom(2, pointB)
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
# get the file name from the command line filename = sys.argv[1] # create a beta backbone generator object using static file parameters backboneObject = PBG(filename) # generate backbone realtime parameters numPos = 3* 8 startPos = np.array([0.0, 0.0, 0.0]) director = np.array([0.0, 0.0, 1.0]) rotation = 0 * np.pi/180 offset = np.array([4.8, 0.0, 0.0]) polarity = 'CN' backBoneBuildingBlock1 = backboneObject.generateBuildingBlock(numPos, polarity) backBoneBuildingBlock1.transformFromBlockFrameToLabFrame(director, startPos, rotation) backBoneBuildingBlock1.exportBBK("backbone1") backBoneBuildingBlock2 = backboneObject.generateBuildingBlock(numPos, polarity) backBoneBuildingBlock2.transformFromBlockFrameToLabFrame(director, startPos + offset, rotation) backBoneBuildingBlock2.exportBBK("backbone2") startPoints = backBoneBuildingBlock1.xyzVals[3:] endPoints = backBoneBuildingBlock2.xyzVals[3:] # create the NPack object. ConstrainedPolymerPackGBB = ConstrainedPolymerPackNBB(filename) numPoints = 10 pointA = startPoints[-1] pointB = endPoints[-1]
if __name__=="__main__": from Library.peptideBackbone import peptideBackboneGenerator as PBG # set up an alpha helix generator object using the alphaHelix parameters helixGenerator = PBG("alphaHelix.txt") # specify the desired location and orientations of two specific helices numResidues = 16 refPoint1 = np.array([0.0, 0.0, 0.0]) director1 = np.array([0.0, 0.0, 1.0]) refPoint2 = np.array([20.0, 0.0, 0.0]) director2 = np.array([1.0, 0.0, 1.0]) # generate helix1 using the generator function helix1 = helixGenerator.generateBuildingBlock(numResidues, showBlockDirector=False) helix1.transformBBToLabFrame(director1, refPoint1, 0) # generate helix 2 using the generator function helix2 = helixGenerator.generateBuildingBlock(numResidues, showBlockDirector=False) helix2.transformBBToLabFrame(director2, refPoint2, 0) # save the helices to file helix1.exportBBK('alphaHelix1') helix2.exportBBK('alphaHelix2') # set up the angles and displacement between the two building blocks alpha1Inp = -57.0 beta1Inp = 116.0 alpha2Inp = 180.0 beta2Inp = 122.0
SpherePoints = SphereBB.blockXYZVals SphereBB1 = sphereBBG.generateBuildingBlock(int(2.5*numStrands), radius2, theta1, theta2, phi1, phi2, minDist2) SphereBB1.transformBBToLabFrame(director, centerPos, rotation) SphereBB1.exportBBK("sphereBasePoints1") numSpherePoints = 200 volPointBB = VolSphereBBG.generateBuildingBlock(numSpherePoints, radius3, radius3, radius3, minDist3) volPointBB.exportBBK("VolSpherePoints.xyz") peptideStrands = [ peptideBBG.generateBuildingBlock(numResidues) for _ in range(numStrands) ] directors = [ (pos - centerPos)/np.linalg.norm(pos - centerPos) for pos in SpherePoints] vesicleBBS = [ strand.transformBBToLabFrame(director, pos, 0.0) for director, pos, strand in zip(directors, SpherePoints, peptideStrands)] xyzVals = peptideStrands[0].blockXYZVals names = peptideStrands[0].blockAtomNames for strand in peptideStrands[1:]: xyzVals = np.concatenate( (xyzVals, strand.blockXYZVals), 0) names = np.concatenate( (names, strand.blockAtomNames), 0) fIO.saveXYZList(xyzVals, names, "peptideVesicle.xyz") print "example done"
class spidroinProteinGenerator(BBG): # Each spidroin protein consists of two alpha helical termini and a main # body that consists of regions of beta sheet structures linked # by hairpin turns. def __init__(self, filename): BBG.__init__(self, filename) def initialiseParameters(self): BBG.initialiseParameters(self) self.numResiduesAlphaHelix = self.getParam('numResiduesAlphaHelix') self.lengthPerResidueAlpha = self.getParam('lengthPerResidueAlpha') self.lengthPerResidueBeta = self.getParam('lengthPerResidueBeta') self.terminiSeparation = self.getParam('terminiSeparation') self.numBetaSheets = self.getParam('numBetaSheets') self.numBetaStrands = self.getParam('numBetaStrands') self.betaStrandLength = self.getParam('betaStrandLength') self.betaSheetRx = self.getParam('betaSheetRx') self.betaSheetRy = self.getParam('betaSheetRy') self.betaSheetRz = self.getParam('betaSheetRz') self.alphaBetaSeparation = self.getParam('alphaBetaSeparation') self.alphaHelixAtomName = self.getParam('alphaHelixAtomName') self.betaStrandAtomName = self.getParam('betaStrandAtomName') self.coilAtomName = self.getParam('coilAtomName') self.BSG = BSG(self.paramFilename) self.AHG = PBG(self.paramFilename) self.PHG = PHG(self.paramFilename) self.USP = NBB(self.paramFilename) self.SPEBBG = SPEBBG(self.paramFilename) if self.noLoadErrors == False: print "Critical Parameters are undefined for Spidroin Object" sys.exit() def generateBuildingBlock(self, startPos, direction, rotation, alignDirectors=True, showDirector=False, nameByBuildingBlockType=True): self.numPoints = 0 self.spidroinDirector = np.array([0.0, 0.0, 1.0]) self.spidroindRefPoint = np.array([0.0, 0.0, 0.0]) self.nameByBuildingBlockType = nameByBuildingBlockType return BBG.generateBuildingBlock(self, self.numPoints, startPos, direction, rotation, alignDirectors=alignDirectors, showDirector=showDirector) def generateBuildingBlockXYZ(self): # build the components print "Constructing N termninus alpha helix" AH_NTerm_numPoints = self.numResiduesAlphaHelix * 3 AH_NTerm_startPos = np.array([ -self.terminiSeparation / 2, 0.0, self.numResiduesAlphaHelix * self.lengthPerResidueAlpha ]) AH_NTerm_director = 1 * self.spidroinDirector AH_NTerm_rotation = 0 AH_NTerm_polarity = 'NC' # Create the N terminus alpha helix bundle self.NTerminusAlphaHelix = self.AHG.generateBuildingBlock( AH_NTerm_numPoints, AH_NTerm_startPos, AH_NTerm_director, AH_NTerm_rotation, AH_NTerm_polarity, alignDirectors=True, showDirector=False) if self.nameByBuildingBlockType: self.NTerminusAlphaHelix.replaceNames(self.alphaHelixAtomName) print "Constructing C termninus alpha helix" AH_CTerm_numPoints = self.numResiduesAlphaHelix * 3 AH_CTerm_startPos = np.array([self.terminiSeparation / 2, 0.0, 0.0]) AH_CTerm_director = -1 * self.spidroinDirector AH_CTerm_rotation = 0 AH_CTerm_polarity = 'NC' # Create the C terminus alpha helix bundle self.CTerminusAlphaHelix = self.AHG.generateBuildingBlock( AH_CTerm_numPoints, AH_CTerm_startPos, AH_CTerm_director, AH_CTerm_rotation, AH_CTerm_polarity, alignDirectors=True, showDirector=False) if self.nameByBuildingBlockType: self.CTerminusAlphaHelix.replaceNames(self.alphaHelixAtomName) BS_centre = np.array( [0.0, 0.0, -self.betaSheetRz - self.alphaBetaSeparation]) BS_director = self.spidroinDirector BS_rotation = 0 BS_minDist = 0.8 * np.sqrt(2) * max([ 4.8 * self.numBetaStrands, self.betaStrandLength * self.lengthPerResidueBeta ]) print "Constructing Beta Sheet Start Points" # then create the start point for a bundle of beta sheets packed uniformly in space betaSheetStartPointsBB = self.SPEBBG.generateBuildingBlock( self.numBetaSheets, BS_centre, BS_director, BS_rotation, self.betaSheetRx, self.betaSheetRy, self.betaSheetRz, BS_minDist) betaSheetStartPoints = betaSheetStartPointsBB.xyzVals betaSheetDirectors = [] # create beta sheet directors for n in range(0, self.numBetaSheets): theta, phi = coords.pickRandomPointOnUnitSphere() betaSheetDirectors.append( coords.sphericalPolar2XYZ(np.array([1.0, theta, phi]))) inStrandDirector = np.array([0.0, 0.0, 1.0]) crossStrandDirector = np.array([1.0, 0.0, 0.0]) rotation = rnd.uniform(0, 2 * np.pi) offset = np.array([0.0, 0.0, 0.0]) print "Constructing Beta Sheets" # create the betaSheets self.betaSheetBBs = [ self.BSG.generateBuildingBlock(self.numBetaStrands, self.betaStrandLength, startPos, globalDirector, inStrandDirector, crossStrandDirector, rotation, offset, polarity='NC', parallel=True, loopedEnds=False) for startPos, globalDirector in zip(betaSheetStartPoints, betaSheetDirectors) ] if self.nameByBuildingBlockType: [ bsheet.replaceNames(self.betaStrandAtomName) for bsheet in self.betaSheetBBs ] # assemble the components spidroinXYZ = self.NTerminusAlphaHelix.xyzVals for betaSheet in self.betaSheetBBs: spidroinXYZ += betaSheet.xyzVals spidroinXYZ += self.CTerminusAlphaHelix.xyzVals return spidroinXYZ def generateBuildingBlockDirector(self): return np.array([0.0, 0.0, 1.0]) def generateBuildingBlockRefPoint(self): return np.array([0.0, 0.0, 0.0]) def generateBuildingBlockNames(self): spidroinNames = self.NTerminusAlphaHelix.atomNames for betaSheet in self.betaSheetBBs: spidroinNames += betaSheet.atomNames spidroinNames += self.CTerminusAlphaHelix.atomNames return spidroinNames
if __name__ == '__main__': # get the file name from the command line filename = sys.argv[1] # create a beta backbone generator object using static file parameters backboneObject = PBG(filename) # generate backbone realtime parameters numPos = 3* 8 startPos = np.array([0.0, 0.0, 0.0]) director = np.array([0.0, 0.0, 1.0]) rotation = 0 * np.pi/180 offset = np.array([600, 0.0, 0.0]) connectors = [[2, 1, 0], [numPos - 3, numPos - 2, numPos - 1]] backBoneBuildingBlock1 = backboneObject.generateBuildingBlock(numPos, 'CN', connectors) backBoneBuildingBlock1.transformFromBlockFrameToLabFrame(director, startPos, rotation) backBoneBuildingBlock1.exportBBK("backbone1") backBoneBuildingBlock2 = backboneObject.generateBuildingBlock(numPos, 'NC', connectors) backBoneBuildingBlock2.transformFromBlockFrameToLabFrame(director, startPos, rotation) backBoneBuildingBlock2.exportBBK("backbone2") startPoints = backBoneBuildingBlock1.xyzVals[-3:] endPoints = backBoneBuildingBlock2.xyzVals[-3:] # create the generator ConstrainedPolymerPackGBB = ConstrainedPolymerPackBBG(filename) numPoints = 700 alpha1 = -50 alpha2 = -70
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)
def generateBuildingBlockXYZ(self): # create a strand and loop generator strandGen = PBG(self.paramFilename) loopGen = PHG(self.paramFilename) loopPoints = [] sphereCentrePoints = [] dummyConnector = [[2, 1, 0]] # construct the base line for the strand start points baseLine = [ self.startPos + n * self.betaStrandSeparation * self.crossStrandDirectorHat for n in range(self.numStrands) ] # construct a prototype beta strand building Block in the defined polarity strandA_BB = strandGen.generateBuildingBlock(self.numPoints, self.polarity, dummyConnector) # assume loop length needs to be long enough to stretch a whole beta strand. loopLength = int(np.ceil(1.5 * self.strandLength)) # In a not parallel situation construct the antiparallel strand - StrandB # also add the offset to every other strand starting on the first or second strand depending on the polarity # also compute the alternative strand building block if not self.parallel: loopLength = 2 # in a parallel situation the loop is always 2 (residues) long. I just said so. if self.polarity == 'NC': baseLine = [ basePoint + self.offsetXYZ if (n % 2) == 0 else basePoint for n, basePoint in enumerate(baseLine) ] strandB_BB = strandGen.generateBuildingBlock( self.numPoints, 'CN', dummyConnector) else: baseLine = [ basePoint + self.offsetXYZ if (n % 2) == 1 else basePoint for n, basePoint in enumerate(baseLine) ] strandB_BB = strandGen.generateBuildingBlock( self.numPoints, 'NC', dummyConnector) # compute the relative vector from the end of the first chain to the next atom in the hairpin. TNB = coords.constructTNBFrame(strandA_BB.xyzVals[-3], strandA_BB.xyzVals[-2], strandA_BB.xyzVals[-1]) if self.polarity == 'NC': # if polarity is NC then the last three values are an NCC triad. beta = self.angleC alpha = self.phi bondLength = self.CNbondLength else: # if polarity is CN then the last three values are CCN triad. beta = self.angleN alpha = self.psi bondLength = self.CNbondLength # the vector from the end of the first strand to its hairpin is defined as the positive connectorVector. ConnectorVectorPos = bondLength * coords.generateTNBVecXYZ( TNB, beta, alpha) # -ve connector vector is the reciprocal of that ConnectorVectorNeg = -1 * ConnectorVectorPos # for parallel all CVs are always the Positive from end of strand to hairpin # and from hairpin to end of strand connectorVectorStrandToHairpin = ConnectorVectorPos connectorVectorHairpinToStrand = ConnectorVectorPos # for parallel strands are always the same set of xyz vals curStrandXYZ = strandA_BB.xyzVals # now the preparatory stuff is complete begin constructing the beta sheet # copy across the first strand to get the structure going. buildingBlockXYZ = [baseLine[0] + xyz for xyz in strandA_BB.xyzVals] innerRadiusParallel = 0.75 * self.strandLength * 3 * bondLength / 2 outerRadiusParallel = 40 * self.strandLength * 3 * bondLength / 2 innerRadiusAntiParallel = 0.9 * self.betaStrandSeparation / 2 outerRadiusAntiParallel = 40 * self.betaStrandSeparation print "Radii: ", innerRadiusParallel, outerRadiusParallel, innerRadiusAntiParallel, outerRadiusAntiParallel print "Diameter: ", 2 * innerRadiusParallel, 2 * outerRadiusParallel, 2 * innerRadiusAntiParallel, 2 * outerRadiusAntiParallel # contruct the remaining strands following this procedure curStrand = 1 while curStrand < self.numStrands: # if parallel, strands and connectorvectors are always the same so do nothing # to them. # if not parallel work out the connectorVectors for attaching hairpin between # the previous strand and the current strand. if not self.parallel: if (curStrand % 2) == 0: connectorVectorStrandToHairpin = ConnectorVectorNeg connectorVectorHairpinToStrand = ConnectorVectorPos curStrandXYZ = strandA_BB.xyzVals if (curStrand % 2) == 1: connectorVectorStrandToHairpin = ConnectorVectorPos connectorVectorHairpinToStrand = ConnectorVectorNeg curStrandXYZ = list(reversed(strandB_BB.xyzVals)) # loop start is end of building block + connectorvector loopStartPoint = buildingBlockXYZ[ -1] + connectorVectorStrandToHairpin loopPoints.append(loopStartPoint) # compute values for new strand newStrand = [baseLine[curStrand] + xyz for xyz in curStrandXYZ] # loop end point is start of next strand - connector vector. loopEndPoint = newStrand[0] - connectorVectorHairpinToStrand loopPoints.append(loopEndPoint) sphereCentrePoint = (loopEndPoint + loopStartPoint) / 2 sphereCentrePoints.append(sphereCentrePoint) print "loopPointdist: ", np.linalg.norm(loopEndPoint - loopStartPoint) # now we have the length of the loop and the start and end points # makes and attempt to stop the hairpin from entering # a sphere at the mid point of the start and end points if self.loopEnds: if self.parallel: loop = loopGen.generateBuildingBlock( loopLength * 3, loopStartPoint, loopEndPoint, 0, -180, 180, beta * 180 / np.pi - 40, beta * 180 / np.pi + 40, 0.9 * bondLength, bondLength, innerRadiusParallel, outerRadiusParallel, sphereCentrePoint, self.polarity) else: loop = loopGen.generateBuildingBlock( loopLength * 3, loopStartPoint, loopEndPoint, 0, -180, 180, beta * 180 / np.pi - 40, beta * 180 / np.pi + 40, 0.9 * bondLength, bondLength, innerRadiusAntiParallel, outerRadiusAntiParallel, sphereCentrePoint, self.polarity) # append the loop to the array buildingBlockXYZ = buildingBlockXYZ + loop.xyzVals # append each vector to the current output array buildingBlockXYZ = buildingBlockXYZ + newStrand #next strand curStrand += 1 #buildingBlockXYZ = buildingBlockXYZ + loopPoints #buildingBlockXYZ = buildingBlockXYZ + sphereCentrePoints self.numPoints = len(buildingBlockXYZ) return buildingBlockXYZ
class peptideHairpinGenerator(CPBBG): # This class returns a randomly coiled peptide backbone between two sets of points # A and B, which is packed into an external envelope. The backbone does not # self-intersect and the coil avoids a user supplied set of external points. # # The class inherits the functionality of the constrainedPolymerClass. # but overides the generateSpaceCurve method of that class to provide the initial unfolded polymer. # # Instead of generating a geometric shape for the polymer, the function # uses the peptide backbone class to generate a straight peptide backbone model with # appropriate dihedrals, bond angles, bondLengths and number of residues, # with the three points specified in pointsA forming the initial seed for the chain. # The first point in A is non negotiable and fixed. But the second and third points # are movable once the initial chain has been built. # PointsA is supplied in the format m0, m1 and m2 where m0 is the innermost point of a connector. # Thus point m2 (the third point in pointsA) is actually the start of the chain. This is important. # Throughouth the project connectors are defined as m0, m1 and m2. # # # The energy of two springs between the two atoms at the free end of the chain and # the two atoms of the second anchor points B is # then computed based on the distance between them. Conformations with smaller # amounts of energy are found by picking C-CA or N-CA bonds at random and # performing dihedral moves of the entire free end of chain from the selected # bond onwards. # # Each time a move is made the new energy of the two springs is calculated for the # new position. If the move results in lower total spring energy than the lowest energy # yet found it is accepted. If the spring energy is larger than the current minimum # the move is accepted with a probability based on the boltzman factor between the # current lowest energy and the new energy. # # As the distance between the end point and point B shrinks the allowed size # range of rotations shrinks rapidly. # # The naming functions and allowedList functions are also overridden so # that only the alpha carbons are allowed to be used in the dihedral twist # rotations. This restriction preserves the peptide bond angles in the chain. # # Once a double anchored space curve is generated, with anchor points at # pointsA and B, the procedure for randomising the coil with crankshaft moves and # folding the chain into an envelope is the same as for the coiled polymer baseclass. # # The final structure is exported as a building block class. def __init__(self, paramFilename): # initialise the parameter dictionary for the base classes CPBBG.__init__(self, paramFilename) def initialiseParameters(self): # initialise the constrained polymer parent CPBBG.initialiseParameters(self) self.distEpsilon = self.getParam("distEpsilon") self.maxNumConnectingMoves = self.getParam("maxNumConnectingMoves") self.springConstant = self.getParam("springConstant") self.connectingTemp = self.getParam("connectingTemp") # load the backbone building object self.PBG = PBG(self.paramFilename) if self.noLoadErrors == False: print "Critical Parameters are undefined for hairpin" sys.exit() def generateBuildingBlock(self, numResidues, pointsA, pointsB, minDist, numCrankMoves, pointsToAvoid=[], envelopeList=['None'], nameCA=False): self.numResidues = numResidues self.numPoints = self.numResidues * 3 bondLength = 0.0 # not used in this game self.nameCA = nameCA self.minDist = minDist # set up the right reference points self.labPointA = pointsA[2] self.labPointB = pointsB[2] self.labPointsA = pointsA[:] self.labPointsB = pointsB[:] # generate the BuildingBlock reference point earlier than usual because # we need the transformation for the pointsToAvoid input. self.blockRefPoint = self.generateBuildingBlockRefPoint() # generate the BuildingBlock director unit vector earlier than usual because # we need the transformation for the pointsToAvoid input. self.blockDirectorHat = self.generateBuildingBlockDirector() # generate the transformation information from building block to labPointA and labPointB self.labDirector, self.labRefPoint, self.labRotation = self.computeTransform( ) # convert the pointsA and pointsB information from the labFrame to the block frame self.blockPointsA = coords.transformFromLabFrameToBlockFrame( self.labDirector, self.labRefPoint, self.labRotation, self.blockDirectorHat, self.blockRefPoint, pointsA) self.blockPointsB = coords.transformFromLabFrameToBlockFrame( self.labDirector, self.labRefPoint, self.labRotation, self.blockDirectorHat, self.blockRefPoint, pointsB) self.pointsToAvoid = coords.transformFromLabFrameToBlockFrame( self.labDirector, self.labRefPoint, self.labRotation, self.blockDirectorHat, self.blockRefPoint, pointsToAvoid) if self.dumpInterimFiles == 1: fIO.saveXYZList(self.blockPointsA + self.blockPointsB, ['Ca', 'Ca', 'Ca', 'O', 'O', 'O'], "blockPointsAB.xyz") # parse the envelope list if we intend to use it. self.parseEnvelopeList(envelopeList) # check starting points are legal or it's gonna be a long wait. for pos in self.blockPointsA: if not self.checkPointInBounds(pos): print "Error Warning: PointA out of bounds" time.sleep(3) for pos in self.blockPointsB: if not self.checkPointInBounds(pos): print "Error Warning: PointB out of bounds" time.sleep(3) return CPBBG.generateBuildingBlock(self, self.numPoints, pointsA[2], pointsB[2], minDist, bondLength, numCrankMoves, pointsToAvoid=self.pointsToAvoid, envelopeList=envelopeList) def generateAllowedList(self): # add the first and last points to the allowed list. # This is important to allow the full chain to be able to be mapped into the envelope. allowed = [0, self.numPoints - 1] # over ride the naming of the atoms with ca=True. names = self.generateBuildingBlockNames(ca=True) [allowed.insert(-1, i) for i, name in enumerate(names) if name == 'CA'] return allowed def generateSpaceCurve(self): # Over-rides the generate space curve function of the parent to generate a peptide backbone # and a pseudo energy landscape approach to find an initial chain with the end point fixed # at point B. # create a regular b0a14ckBone using block Points A as the first residue peptideBackbone = self.PBG.generateBuildingBlock( self.numResidues, seedResidue=self.blockPointsA) if self.dumpInterimFiles == 1: fIO.saveXYZList(peptideBackbone.blockXYZVals, peptideBackbone.blockAtomNames, 'initialPeptideBackbone.xyz') # extract the xyzValues xyzVals = peptideBackbone.getAtomsXYZ() # perform the energy minimisation that moves the free end to blockPointsB xyzVals = self.minimiseEnergy(xyzVals, self.allowedList) if self.dumpInterimFiles == 1: fIO.saveXYZ(xyzVals, 'K', 'chainConnectedBlockFrame.xyz') return xyzVals def minimiseEnergy(self, xyzVals, allowedList): # Returns a polymer chain which minimises a simple PE function. # Performs random dihedral twists on the free end of a polymer, # starting at a random bond. # Moves resulting in lower energy arrangements are accepted. # Moves resulting in higher energy arrangements are accepted # with probability that is exponentially smaller with increasing energy difference. # Since the primary structure is just a straight line and we are only # doing sparse dihedral twists, with only a single bias towards to the pointB, # there is a low probability of a self-intersection especially for long chains. # This rapidly finds a structure where the point B is within arbitrary distance of pointB. # The step size scales in proportion to the distance from point B. So only tiny steps are taken # near to the only minimum of the entire potential. Converges rapidly even for large N. lowestEnergyMinimum = xyzVals[:] initPE, initDist = self.PE(xyzVals) curPE = initPE minPE = initPE curDist = initDist minDist = initDist maxStepRange = 1.0 numMoves = 0 curMin = 0 while minDist > self.distEpsilon and numMoves < self.maxNumConnectingMoves: # compute new conformation based on a random dihedral twist newXYZ = self.dihedralTwist(xyzVals, maxStepRange) # compute energy and distance of new move newPE, newDist = self.PE(newXYZ) # compute energy difference with current minimum PE deltaPE = newPE - minPE # assume we will accept the move. acceptMove = True # if the currentPE is greater than the minimum then only accept # the move with a probability given by the difference in # energy of the minimum and current states if deltaPE > 0: # pick a random value between 0 and 1 prob = rnd.uniform(0, 1) # if that value is larger than the threshold reject the move. # The threshold decreases with increasing deltaE, so the # higher the energy of the new state relative to the old one # the more likely it is we reject the move if prob > np.exp(-deltaPE / self.connectingTemp): acceptMove = False # if we accept the move then store the new coords # and record the energy of the newest accepted move if acceptMove: xyzVals = newXYZ[:] curPE = newPE curDist = newDist # check the curPE against the minimum energy # if we have a new min energy then retain for the future and dump to file. if curPE < minPE: lowestEnergyMinimum = xyzVals[:] minPE = curPE minDist = curDist maxStepRange = minDist / initDist curMin += 1 self.outline(numMoves, self.maxNumConnectingMoves, minDist, minPE, maxStepRange) numMoves += 1 if numMoves % 100 == 0: self.outline(numMoves, self.maxNumConnectingMoves, minDist, minPE, maxStepRange) return lowestEnergyMinimum def outline(self, n, M, d, E, R): print n, "out of ", M, "minDist:", d, "minEnergy:", E, "maxStepRange:", R def PE(self, xyzVals): PE = 0.0 # add spring between pointB[2] and end of xyzVals with equilibrium at pointB dist1 = np.linalg.norm(xyzVals[-1] - self.blockPointsB[2]) PE += 0.5 * 3 * self.springConstant * dist1**2 # add spring between pointsB[1] and end of xyzVals[-2] dist2 = np.linalg.norm(xyzVals[-2] - self.blockPointsB[1]) PE += 0.5 * 2 * self.springConstant * dist2**2 # add spring between pointsB[0] and end of xyzVals[-3] #dist3 = np.linalg.norm(xyzVals[-3] - self.blockPointsB[0]) #PE += 0.5 * self.springConstant * dist3**2 # made the first spring the stiffest with second and third springs getting weaker. # first spring has largest influence on energy. # add weak spring between each CA and refPoint # and repulsion at close range #for i in allowedList: # posDist = np.linalg.norm(xyzVals[i] - self.blockRefPoint) # PE += +0.5 * 2 * self.springConstant * posDist **2 # #PE += -1.0/posDist**6 # add contribution to PE based on distance between each CA with all the others # only look at ordered pairwise combinations without repetition. # ie. 1-2, 1-3, 1-4, 2-3, 2-4, 3-4 (leaving out 1-1, 2-2, and 3-1, 4-1 etc) #pairWiseCombinations = it.combinations([ pos for n, pos in enumerate(xyzVals) if n in allowedList], 2) # iterate and compare each pair to compute contrib to PE #for pair in pairWiseCombinations: # PE += -self.epsilon0/np.power(np.linalg.norm(pair[1] - pair[0]), 3) # add contribution to PE from every point in pointsToAvoid with # every point in the list #avoidList = it.product(xyzVals, self.pointsToAvoid) # iterate and compare each pair to see if any match #for pair in avoidList: # PE += - self.LJRep(pair[0], pair[1]) return PE, np.sqrt(dist1**2 + dist2**2) def dihedralTwist(self, xyzVals, maxStepRange): #fIO.saveXYZList(xyzVals, self.blockNames, "preTwist.xyz") # can only do this if there are sufficient atoms in the array if len(xyzVals) > 3: # initialise the axisAtom1Index axisAtom1Index = 2 # keep picking a random atom until we get one in the allowed list. while not axisAtom1Index in self.allowedList: axisAtom1Index = rnd.randint(0, len(xyzVals) - 3) # find the relevant points and rotation axis atom1 = xyzVals[axisAtom1Index] atom2 = xyzVals[axisAtom1Index + 1] rotAxis = atom2 - atom1 rotAxisHat = rotAxis / np.linalg.norm(rotAxis) # pick a step size at random from within the current allowed range angle = rnd.uniform(maxStepRange * -np.pi, maxStepRange * np.pi) # rotate all the remaining points about the atom1 axis place at atom1 by angle xyzVals = [ p if n < axisAtom1Index + 2 else cart.rotPAboutAxisAtPoint( p, atom1, rotAxisHat, angle) for n, p in enumerate(xyzVals) ] #fIO.saveXYZ([atom1, atom2], 'O', "atomAxis.xyz") #fIO.saveXYZList(xyzVals, self.blockNames, "postTwist.xyz") return xyzVals def LJRep(self, p1, p2): # add a short range repulsive term based on the distance between # centre of each point return self.epsilon0 / np.power(np.linalg.norm(p1 - p2), 12) def generateBuildingBlockNames(self, ca=False): if self.nameCA: names = ['N', 'CA', 'C'] * self.numResidues else: names = ['N', 'C', 'C'] * self.numResidues # override the externally controlled member variable nameCA with a local variable if ca == True: names = ['N', 'CA', 'C'] * self.numResidues return names def generateBuildingBlockConnectors(self): # N connector first, then C Connector return [[2, 1, 0], [self.numPoints - 3, self.numPoints - 2, self.numPoints - 1]]
# N connector first, then C Connector return [[2, 1, 0], [self.numPoints - 3, self.numPoints - 2, self.numPoints - 1]] if __name__ == "__main__": # get the file name from the command line filename = sys.argv[1] # create the generator objects. hairPinGen = peptideHairpinGenerator(filename) backboneGenerator = PBG(filename) # generate 1 residue seed building block seedResidue = backboneGenerator.generateBuildingBlock(1) # generate starting points and move the seed to those points extracting # the xyzVals each time. numResidues = 20 pointA = np.array([10.0, 10.0, 10.0]) pointB = np.array([-10.0, 10.0, -10.0]) print "Estimate min num residues: ", np.linalg.norm(pointA - pointB) / 3.5 seedResidue.placeAtom(2, pointA) seedResidue.setBlockRefPoint(pointA) seedResidue.orientToDirector(np.array([-1.0, 1.0, 0.0])) pointsA = seedResidue.blockXYZVals[:] seedResidue.placeAtom(2, pointB)