def findCoilPoints(self, s0, s1, s2, displacement_s2_m2, displacement_m2_m1, displacement_m1_m0, alpha_s0_s1_s2_m2, beta_s1_s2_m2, alpha_s1_s2_m2_m1, beta_s2_m2_m1, alpha_s2_m2_m1_m0, beta_m2_m1_m0): # function identifies the positions of the ends of a free coil in terms of the # end point it is connecting to and certain bond information. # Our objective is to define m2, m1 and m0 in terms of this information. # the names s0, s1, s2 and m2, m1, m0 are defined in the building block function # for connecting two blocks together. # m2 atom in terms of s0, s1 and s2 TNB1 = coords.constructTNBFrame(s0, s1, s2) m2 = s2 + displacement_s2_m2 * coords.generateTNBVecXYZ(TNB1, beta_s1_s2_m2, alpha_s0_s1_s2_m2) # m1 atom in terms of s1, s2 and m2 TNB2 = coords.constructTNBFrame(s1, s2, m2) m1 = m2 + displacement_m2_m1 * coords.generateTNBVecXYZ(TNB2, beta_s2_m2_m1, alpha_s1_s2_m2_m1) # m0 atom in terms of s2, m2 and m1 TNB3 = coords.constructTNBFrame(s2, m2, m1) m0 = m1 + displacement_m1_m0 * coords.generateTNBVecXYZ(TNB3, beta_m2_m1_m0, alpha_s2_m2_m1_m0) return [m0, m1, m2]
def residueToAtoms(self, S0, S1, S2, S3, alpha0, b1, beta1, b2, beta2, b3, beta3): # Positioning M0 correctly in the S0, S1, S2 TNB frame # allows for alpha0, b1 and beta1 to be set correctly. TNB_S0_S1_S2 = coords.constructTNBFrame(S0, S1, S2) M0 = S2 + b1 * coords.generateTNBVecXYZ(TNB_S0_S1_S2, beta1, alpha0) TNB_S1_S2_M0 = coords.constructTNBFrame(S1, S2, M0) # Can position M1 such that b2, and beta2 are correct, but that leaves b3 and beta3 and only one # parameter - alpha2 - which is dihedral about s2 to m0 axis. So find the alpha which # minimises the difference between b3 and linalg.norm(M1 - S3). results = minimize(coords.computeDistBetweenM1AndS3, [-60 *np.pi/180], (TNB_S1_S2_M0, beta2, S3, b3)) alpha2 = results['x'] # compute position of M1 with b2, beta2 and alpha2. We don't care what the last angle is. M1 = M0 + b2 * coords.generateTNBVecXYZ(TNB_S1_S2_M0, beta2, alpha2) return M0, M1
def constructLoop(self, BB1, BB2, loopGen, pointsToAvoid): connectorA = BB1.getConnectionAtoms( 1) # 1 is the C terminus of interest connectorB = BB2.getConnectionAtoms( 0) # 0 is the N terminus of interest # generate TNB frames at either connector TNBA = coords.constructTNBFrame(connectorA[0], connectorA[1], connectorA[2]) TNBB = coords.constructTNBFrame(connectorB[0], connectorB[1], connectorB[2]) # C terminus is CCN triad (new atom will be an N). betaA = self.angleC alphaA = self.phi # N Terminus is CNC triad. (new atom will be a C). betaB = self.angleN alphaB = self.psi # compute the coil start and end points pointA = connectorA[2] + self.CNbondLength * coords.generateTNBVecXYZ( TNBA, betaA, alphaA) pointB = connectorB[2] + self.CNbondLength * coords.generateTNBVecXYZ( TNBB, betaB, alphaB) if self.dumpInterimFiles: fIO.saveXYZList([pointA, connectorA[2], pointB, connectorB[2]], ['Ca', 'S', 'O', 'S'], 'connectionDetails.xyz') fIO.saveXYZ(pointsToAvoid, 'K', 'pointsToAvoid.xyz') numCrankMoves = 0 if self.parallel: iSphereR = 0.4 * self.strandLength * 3.5 else: iSphereR = 0.9 * self.betaStrandSeparation / 2.0 # create the loop building Blockss return loopGen.generateBuildingBlock( self.numLoopResidues, pointA, pointB, self.minDist, numCrankMoves, pointsToAvoid=pointsToAvoid, envelopeList=["innersphere " + str(iSphereR)])
def generateResidue(self, prevRes): # given a list of 3 xyz positions for the previous residue # construct the next set of three positions with appropriate bond angles # and dihedral angles. # construct the first TNB from the previous residue TNB1 = coords.constructTNBFrame(prevRes[0], prevRes[1], prevRes[2]) NPos = prevRes[2] + self.CNbondLength * coords.generateTNBVecXYZ( TNB1, self.angleC, self.phi) TNB2 = coords.constructTNBFrame(prevRes[-2], prevRes[-1], NPos) CAPos = NPos + self.CNbondLength * coords.generateTNBVecXYZ( TNB2, self.angleN, self.omega) TNB3 = coords.constructTNBFrame(prevRes[-1], NPos, CAPos) CPos = CAPos + self.CCbondLength * coords.generateTNBVecXYZ( TNB3, self.angleCA, self.psi) return [NPos, CAPos, CPos]
def findLastTwoPointsFit(self, startPoints, endPoints, distanceBetweenPoints): # This function figures out the coordinates of the last # one or two points in a polymer chain such that they are # bondLength distance apart. It stops worrying about bond angles. # This is a many dimensional problem and there # are many ways to solve it. varies all four angles simultaneously # and calculates the resulting distance # between the particles. THe function minimises this distance - bondLength. # construct TNB frames TNBA = coords.constructTNBFrame(startPoints[-3], startPoints[-2], startPoints[-1]) TNBB = coords.constructTNBFrame(endPoints[-3], endPoints[-2], endPoints[-1]) # pick bond angles at random from the acceptable range betaA = rnd.uniform(self.beta1, self.beta2) betaB = rnd.uniform(self.beta1, self.beta2) alphaA = rnd.uniform(self.alpha1, self.alpha2) alphaB = rnd.uniform(self.alpha1, self.alpha2) # optimise the angles for the given function. finalAngles = minimize(coords.computeDistBetweenPoints, [alphaA, alphaB], args=(startPoints[-1], endPoints[-1], TNBA, TNBB, betaA, betaB, self.bondLength, distanceBetweenPoints)) pointA = startPoints[-1] + self.bondLength * coords.generateTNBVecXYZ(TNBA, betaA, finalAngles['x'][0]) pointB = endPoints[-1] + self.bondLength * coords.generateTNBVecXYZ(TNBB, betaB, finalAngles['x'][1]) # true if the optimisation exited successfully (it gave a result!) listsAreGood = finalAngles['success'] return pointA, pointB, listsAreGood
def startResidue(self): # generates the first residue of the backbone # set first NPos to be at origin. NPos = np.array([0.0, 0.0, 0.0]) # Assume CAPos is along director axis from NPos - bad assumption - we'll correct it later CAPos = NPos + self.CNbondLength * self.directorHat # generate a third dummy vector DummyPos = NPos + np.array([1.0, 0.0, 0.0]) # compute the final CPos for the initial residue TNB3 = coords.constructTNBFrame(DummyPos, NPos, CAPos) CPos = CAPos + self.CCbondLength * coords.generateTNBVecXYZ( TNB3, self.angleCA, self.psi) return [NPos, CAPos, CPos]
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 generateGPQUnit(self, prevGPQ): # given a list of 6 xyz positions for the previous G-PQ Unit # construct the next G-PQ unit with appropriate bond angles # and dihedral angles. # construct the first G Unit from the previous G-PQ unit TNB1 = coords.constructTNBFrame(prevGPQ[2], prevGPQ[3], prevGPQ[5]) NPosG = prevGPQ[5] + self.CNbondLength * coords.generateTNBVecXYZ( TNB1, self.angleC, self.PGPsi) TNB2 = coords.constructTNBFrame(prevGPQ[3], prevGPQ[5], NPosG) dirHat = coords.generateTNBVecXYZ(TNB2, self.angleN, self.PGPhi) if self.species == 'SP1': FPosG = NPosG + self.GG1bondLength / 2.0 * dirHat CPosG = FPosG + self.GG1bondLength / 2.0 * dirHat else: FPosG = NPosG + self.GG2bondLength / 2.0 * dirHat CPosG = FPosG + self.GG2bondLength / 2.0 * dirHat TNB3 = coords.constructTNBFrame(prevGPQ[5], NPosG, CPosG) # now construct the P or Q unit if self.species == 'SP1': dirHat = coords.generateTNBVecXYZ(TNB3, self.angleN, self.GPPsi) NPosP = CPosG + self.CNbondLength * dirHat TNB4 = coords.constructTNBFrame(NPosG, CPosG, NPosP) dirHat = coords.generateTNBVecXYZ(TNB4, self.angleN, self.GPPhi) FPosP = NPosP + self.PPbondLength / 2.0 * dirHat CPosP = FPosP + self.PPbondLength / 2.0 * dirHat G_PQUnit = [NPosG, FPosG, CPosG, NPosP, FPosP, CPosP] else: dirHat = coords.generateTNBVecXYZ(TNB3, self.angleC, self.GQPsi) NPosQ = CPosG + self.CNbondLength * dirHat TNB4 = coords.constructTNBFrame(NPosG, CPosG, NPosQ) dirHat = coords.generateTNBVecXYZ(TNB4, self.angleN, self.GQPhi) FPosQ = NPosQ + self.QQbondLength / 2.0 * dirHat CPosQ = FPosQ + self.QQbondLength / 2.0 * dirHat G_PQUnit = [NPosG, FPosG, CPosG, NPosQ, FPosQ, CPosQ] return G_PQUnit
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 startGPQUnit(self): # generates the first G-PQ unit of the spidroin Backbone # A G or PQ unit has three nodes: the NPos, the FPos and the CPos. # The NPos is the N terminal of the unit, The FPos is the force centre and the CPos is the C terminal. # Together these three things define an ellipsoid and a central repulsive/attractive field. if self.species == 'SP2': # First Unit in SP2 is a P-Unit # set up a dummy pos to sort out first dihedral DPos = np.array([1.0, 0.0, 0.0]) # set first NPos to be at origin. NPosP1 = np.array([0.0, 0.0, 0.0]) # Assume FPos is along director axis from NPos - bad assumption - we'll correct it later FPosP1 = NPosP1 + self.PPbondLength / 2.0 * self.directorHat # Make the CPos at the end of the GUnit CPosP1 = NPosP1 + self.PPbondLength / 2.0 * self.directorHat # set up the TNB frame to define the alpha and betas for the first part of the P to G Unit TNB1 = coords.constructTNBFrame(DPos, NPosP1, CPosP1) NPosG = CPosP1 + self.CNbondLength * coords.generateTNBVecXYZ( TNB1, self.angleC, self.PGPsi) # set up the TNB frame to define the alpha and betas for the second part of the P to G Unit TNB2 = coords.constructTNBFrame(NPosP1, CPosP1, NPosG) # set up FPosG and CPosG dirHat = coords.generateTNBVecXYZ(TNB2, self.angleN, self.PGPhi) FPosG = NPosG + self.GG2bondLength / 2.0 * dirHat CPosG = FPosG + self.GG2bondLength / 2.0 * dirHat # now generate NPosP2, FPosP2, CPosP2 # set up the TNB frame to define the alpha and betas for the first part of the G to P Unit TNB3 = coords.constructTNBFrame(CPosP1, NPosG, CPosG) NPosP2 = CPosG + self.CNbondLength * coords.generateTNBVecXYZ( TNB3, self.angleC, self.GPPsi) # set up the TNB frame to define the alpha and betas for the second part of the G to P Unit TNB2 = coords.constructTNBFrame(NPosG, CPosG, NPosP2) dirHat = coords.generateTNBVecXYZ(TNB3, self.angleN, self.GPPhi) # Assume FPos is along director axis from NPos - bad assumption - we'll correct it later FPosP2 = NPosP2 + self.PPbondLength / 2.0 * dirHat # Make the CPos at the end of the GUnit CPosP2 = NPosP1 + self.PPbondLength / 2.0 * dirHat GPQUnit = [ NPosP1, FPosP1, CPosP1, NPosG, FPosG, CPosG, NPosP2, FPosP2, CPosP2 ] # In SP1 we start with a G-Q, in SP2 we start with a P-G-P if self.species == 'SP1': # First unit in SP1 is a G-Q unit. # set up a dummy pos to sort out first dihedral DPos = np.array([1.0, 0.0, 0.0]) # set first NPos to be at origin. NPosG = np.array([0.0, 0.0, 0.0]) # Assume FPos is along director axis from NPos - bad assumption - we'll correct it later FPosG = NPosG + self.GG1bondLength / 2.0 * self.directorHat # Make the CPos at the end of the GUnit CPosG = FPosG + self.GG1bondLength / 2.0 * self.directorHat # set up the TNB frame to define the alpha and betas for the first part of the G to P Unit TNB1 = coords.constructTNBFrame(DPos, NPosG, CPosG) NPosQ = CPosG + self.CNbondLength * coords.generateTNBVecXYZ( TNB1, self.angleC, self.GQPsi) # set up the TNB frame to define the alpha and betas for the second part of the G to Q Unit TNB2 = coords.constructTNBFrame(NPosG, CPosG, NPosQ) # set up FPosPQ and CPosPQ dirHat = coords.generateTNBVecXYZ(TNB2, self.angleN, self.GQPhi) FPosQ = NPosQ + self.QQbondLength / 2.0 * dirHat CPosQ = FPosQ + self.QQbondLength / 2.0 * dirHat GPQUnit = [NPosG, FPosG, CPosG, NPosQ, FPosQ, CPosQ] return GPQUnit
def makeConnection(self, selfConnector, newBB, newBBConnector, displacement, alpha1, beta1, alpha2, beta2, alpha3): # Returns a copy of the newBB that is correctly positioned relative to self, as defined # by the six parameters provided. Six degrees of freedom are necessary to specify the global # position and orientation of the mobile block. These are specified in terms of the dihedral and bond angles # formed by the juxtaposition of the two connectors used to connect the blocks. Input angles are in Radians. # # Six atoms are used to connect the blocks which are specified using 2 arrays of 3 indices that refer to the atoms in # both of the building blocks. There are three atoms from each block: called 0s, 1s, 2s and 0m, 1m, 2m where m stands for # mobile and s for static. # # It is envisaged that 2m and 2s are the two atoms that form the connecting "bond" between the blocks so 2 is # at the surface of a structure and 1, and 0 are increasingly deeper into the structures with m and s specifying which # block they come from. # # The algorithm never moves the static block and proceeds by first establishing the correct position for 2m in the # mobile block, then 1m and then 0m. This is performed by a translation and three rotations. # Alpha1 is the dihedral angle formed by the atoms referenced by connector indices 0s 1s 2s 2m # beta1 is the bond angle formed by the atoms 1s, 2s and 2m. # displacement is the distance between 2s and 2m which form the connector points. # Together displacement, alpha1 and beta1 define where atom 2m goes in the static TNB frame formed # from 0s, 1s and 2s, when this frame is positioned at 2s. # # Thus the first translation places the mobile block so that atom 2m is at the location specified. # # Alpha2, beta2 and alpha3 then define the re-orientation of the entire second block. # # Beta2 is set first and is the the bond angle that should exist between the 2s 2m and 1m atoms. # This angle is set by first measuring the existing bond angle so formed after the translation. # The second block is then rotated by the necessary angle about an axis through 2m which is Normal # to the plane formed by 2s, 2m and 1m. (this is the N vector of the TNBframe define by the points # 2s, 2m, 1m). # # Alpha2 is set next and is the dihedral angle formed by the point 1s 2s 2m 1m. # The existing angle is measured and the mobile block is then # rotated by the appropriate angle about a vector through 2m which is parallel to 2m - 2s. # # point 1m is now positioned correctly. # # THe final dihedral that must be set is the one defined by 2s, 2m, 1m and 0m, which is a rotation about the 2m to 1m axis. # The dihedral is measured and adjusted accordingly. # Only moves point 0m and that means that the second block is now correctly positioned. # first create a copy of the new BB so as not to modify the original building block. # mBB = mobile Building Block mBB = cp.copy(newBB) # extract the connection points for the two building blocks (self and mobile). selfConnectXYZ = self.getConnectionAtoms(selfConnector) mobileConnectXYZ = mBB.getConnectionAtoms(newBBConnector) # construct a TNB frame from 0s, 1s and 2s. TNB1 = coord.constructTNBFrame(selfConnectXYZ[0], selfConnectXYZ[1], selfConnectXYZ[2]) # Compute the displacement of the mobileBlock so that the mobile BB's connection point 2m # is at the right place in the TNB frame of the first connector when it is position with it's origin on the # third point. The vector in this frame is defined by displacement, alpha1 and beta1. displacementVec = selfConnectXYZ[2] + displacement * coord.generateTNBVecXYZ(TNB1, beta1, alpha1) - mobileConnectXYZ[2] # translate the mobile block so it's in the right place mBB.translateBB(displacementVec) ##### Rot 1: Set alpha 2 - dihedral about 2s to 2m axis ###### # get the new repositioned mobile connector block mobileConnectXYZ = mBB.getConnectionAtoms(newBBConnector) # measure the dihedral between s1, s2, m2 and m1, keep order the same to maintain signs. alpha2Meas = coord.Dihedral(selfConnectXYZ[1], selfConnectXYZ[2], mobileConnectXYZ[2], mobileConnectXYZ[1]) if alpha2Meas==None: alpha2Meas = alpha2 # compute the alpha2rotation axis (unti vector from 2s to 2m) alpha2RotAxis = mobileConnectXYZ[2] - selfConnectXYZ[2] alpha2RotAxisHat = alpha2RotAxis/np.linalg.norm(alpha2RotAxis) # rotate the second block by an angle (alpha2 - alpha2Meas) about a vector through the point 2m which is parallel to the 2s 2m axis # Only 1m and 0m should move from the static and mobile connector sets. mBB.rotateBBArbitraryAxis(mobileConnectXYZ[2], alpha2RotAxisHat, alpha2 - alpha2Meas) ##### Rot 2: Set Beta2- bond angle from point m2 ###### # get the new repositioned mobile connector block mobileConnectXYZ = mBB.getConnectionAtoms(newBBConnector) # measure the bondangle between s2, m2 and m1 beta2Meas = coord.bondAngle(selfConnectXYZ[2], mobileConnectXYZ[2], mobileConnectXYZ[1]) # construct a TNB frame from s2, m2 and m1 TNB2 = coord.constructTNBFrame(selfConnectXYZ[2], mobileConnectXYZ[2], mobileConnectXYZ[1]) # rotate mobile connector by angle (beta2 - beta2Meas) about a vector through the point 2m which is parallel with TNB2 frame N-axis # i.e. normal to plane of s2, m2 and m1 - the way normal is defined versus measurements of angle means to put a minus sign in there. mBB.rotateBBArbitraryAxis(mobileConnectXYZ[2], TNB2[1], -(beta2 - beta2Meas)) ###### rot3: set alpha3 - dihedral ab out 2m to 1m axis. # extract the new mobile connectors mobileConnectXYZ = mBB.getConnectionAtoms(newBBConnector) # construct rot3Axis from 2m to 1m. rot3Axis = mobileConnectXYZ[1] - mobileConnectXYZ[2] rot3AxisHat = rot3Axis/np.linalg.norm(rot3Axis) # measure the dihedral betwee s2, m2, m1 and m0. alpha3Meas = coord.Dihedral(selfConnectXYZ[2], mobileConnectXYZ[2], mobileConnectXYZ[1], mobileConnectXYZ[0]) # rotate the second block by an angle (alpha3 - alpha3Meas) about a vector through the point 2m which is parallel to the 2m 1m axis # Only 0m should move to it's final place from the static and mobile connector sets. mBB.rotateBBArbitraryAxis(mobileConnectXYZ[2], rot3AxisHat, alpha3 - alpha3Meas) # return the rotated Building Block return mBB
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