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 findLastTwoPoints(self, startPoints, endPoints, pointsToAvoid): TNB = coords.constructTNBFrame(startPoints[-2], startPoints[-1], endPoints[-1]) # see page 173 in book 2 l = np.linalg.norm(startPoints[-1] - endPoints[-1]) tComponent = np.abs(l-self.bondLength)/2.0 bComponent = np.sqrt(self.bondLength**2 - tComponent**2) if l>self.bondLength: newPoint1 = startPoints[-1] - tComponent* TNB[0] - bComponent * TNB[2] else: newPoint1 = startPoints[-1] + tComponent* TNB[0] - bComponent * TNB[2] newPoint2 = newPoint1 + self.bondLength * TNB[0] # whizz check point 1 as if newPoint2 was at the end of endPoints newPoint1, inBounds1 = self.whizzCheck(startPoints, endPoints + [newPoint2], pointsToAvoid, newPoint1) if inBounds1: # Now whizz check point 2 as if the new newPoint1 was at the end of startPoints. newPoint2, inBounds2 = self.whizzCheck(startPoints + [newPoint1], endPoints, pointsToAvoid, newPoint2) listsAreGood = False if inBounds1 and inBounds2: # only return true if both the new points are golden. listsAreGood = True return newPoint1, newPoint2, listsAreGood
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 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 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 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 findLastPoint(self, startPoints, endPoints, pointsToAvoid): # construct a TNB frame TNB = coords.constructTNBFrame(startPoints[-2], startPoints[-1], endPoints[-1]) # Compute position in TB plane of last point TComponent = np.linalg.norm(startPoints[-1] - endPoints[-1])/2 BComponent = np.sqrt(self.bondLength**2 - TComponent**2) newPoint = startPoints[-1] + BComponent*TNB[2] + TComponent*TNB[0] # perform a whizz Check return self.whizzCheck(startPoints, endPoints, pointsToAvoid, newPoint)
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 pickRandomPointInDefinedSpace(self): # Takes the last three points of the nList and picks a new point in XYZ which # is inside the given angular ranges. No need to check the ranges, because the points are only selected inside those zones. # construct the TNB vectors from the last three points of the nList. TNB = coords.constructTNBFrame(self.nList[-3], self.nList[-2], self.nList[-1]) # compute a directional vector in XYZ coords from the last point on the list to the new point, within the given angular range constraints. newCoordXYZRel = coords.pickRandomTNBDirectionInAngRangeXYZ(TNB, self.beta1, self.beta2, self.alpha1, self.alpha2) # should be a unit vector already but just in case newCoordXYZRelHat = newCoordXYZRel/np.linalg.norm(newCoordXYZRel) # scale the vector by the bondlength and add it to the last point in the list to give new position vector return self.nList[-1] + self.bondLength * newCoordXYZRelHat
def correctBondLengths(self, xyzVals): # whip through the xyz vals and subtly adjust the positions of the atoms # so they are all exactly bondlength apart. for i, pos in enumerate(xyzVals): if i > 0 and i < len(xyzVals) - 1: # if the bondLengths b1 = np.linalg.norm(pos - xyzVals[i - 1]) b2 = np.linalg.norm(pos - xyzVals[i + 1]) if (np.abs(b1 - self.bondLength) > 0.01) or (np.abs(b2 - self.bondLength) > 0.01): tnb = coords.constructTNBFrame(pos, xyzVals[i - 1], xyzVals[i + 1]) midPoint = (xyzVals[i - 1] + xyzVals[i + 1]) / 2 l = np.linalg.norm(xyzVals[i - 1] - xyzVals[i + 1]) / 2 r = np.sqrt(self.bondLength**2 - l**2) xyzVals[i] = midPoint + tnb[2] * r return xyzVals
def pickRandomPointInDefinedSpace(self, p1, p2, p3): # Takes the given three points and picks a new point in XYZ which # is bondLength distance from p3, but inside the given angular ranges # as defined by the bond angle p2 - p3 - newPoint and dihedral p1-p2-p3-new point. # No need to check againt angle ranges once point is selected because angles # are picked inside those ranges already. # construct the TNB vectors from the last three points of the nList. TNB = coords.constructTNBFrame(p1, p2, p3) # compute a directional vector in XYZ coords from the last point on the list to the new point, within the given angular range constraints. newCoordXYZRel = coords.pickRandomTNBDirectionInAngRangeXYZ(TNB, self.betaMin, self.betaMax, self.alphaMin, self.alphaMax) # should be a unit vector already but just in case newCoordXYZRelHat = newCoordXYZRel/np.linalg.norm(newCoordXYZRel) # scale the vector by the bondlength and add it to the last point in the list to give new position vector return p3 + self.bondLength * newCoordXYZRelHat
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
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