Example #1
0
def makeHSADistribution(num0, num1, num2, num3, num4, num5, x, y, z, minDist):

    # create the cubic distribution
    cubeGen = VPCBBG('VolumePackCuboid.txt')

    # create the NPack object.
    numPoints = num0 + num1 + num2 + num3 + num4 + num5

    # generate the building block
    SpacePackBB = cubeGen.generateBuildingBlock(numPoints, -x / 2, x / 2,
                                                -y / 2, y / 2, -z / 2, z / 2,
                                                minDist)

    # dump the base points to file
    fIO.saveXYZ(SpacePackBB.blockXYZVals, 'CA', 'points.xyz')

    # generate sufficient random orientiation vectors
    thetaPhiArray = [
        coords.pickRandomPointOnUnitSphere() for _ in range(numPoints)
    ]
    directors = [
        coords.sphericalPolar2XYZ(np.array([1.0, angs[0], angs[1]]))
        for angs in thetaPhiArray
    ]

    xyzVals0, names0 = genHsaPoints('hsa_apo.xyz',
                                    SpacePackBB.blockXYZVals[0:num0],
                                    directors[0:num1])
    xyzVals1, names1 = genHsaPoints('hsa_with1.xyz',
                                    SpacePackBB.blockXYZVals[num0:num0 + num1],
                                    directors[num0:num0 + num1])
    xyzVals2, names2 = genHsaPoints(
        'hsa_with2.xyz',
        SpacePackBB.blockXYZVals[num0 + num1:num0 + num1 + num2],
        directors[num0 + num1:num0 + num1 + num2])
    xyzVals3, names3 = genHsaPoints(
        'hsa_with3.xyz',
        SpacePackBB.blockXYZVals[num0 + num1 + num2:num0 + num1 + num2 + num3],
        directors[num0 + num1 + num2:num0 + num1 + num2 + num3])
    xyzVals4, names4 = genHsaPoints(
        'hsa_with4.xyz',
        SpacePackBB.blockXYZVals[num0 + num1 + num2 + num3:num0 + num1 + num2 +
                                 num3 + num4],
        directors[num0 + num1 + num2 + num3:num0 + num1 + num2 + num3 + num4])
    xyzVals5, names5 = genHsaPoints(
        'hsa_with5.xyz',
        SpacePackBB.blockXYZVals[num0 + num1 + num2 + num3 + num4:],
        directors[num0 + num1 + num2 + num3 + num4:])

    fIO.saveXYZList(xyzVals0, names0, 'hsa0All.xyz')
    fIO.saveXYZList(xyzVals1, names1, 'hsa1All.xyz')
    fIO.saveXYZList(xyzVals2, names2, 'hsa2All.xyz')
    fIO.saveXYZList(xyzVals3, names3, 'hsa3All.xyz')
    fIO.saveXYZList(xyzVals4, names4, 'hsa4All.xyz')
    fIO.saveXYZList(xyzVals5, names5, 'hsa5All.xyz')
 def pickFirstPoints(self):
     from Library.VolumePackCuboid import VolumePackCuboidBBG as VPCBBG        
     
     BBGen = VPCBBG(self.paramFilename)
     # generate seedLength number of randomly disributed particles in the void.
     BB = BBGen.generateBuildingBlock(self.seedLength, self.xRange1, self.xRange2, self.yRange1, self.yRange2, self.zRange1, self.zRange2, self.minDist)
     self.nList = BB.blockXYZVals
     #self.nList = [np.array([rnd.uniform(self.xRange1, self.xRange2), rnd.uniform(self.yRange1, self.yRange2), rnd.uniform(self.zRange1, self.zRange2)])]
     self.nAttempts = self.seedLength * [0]
     self.lenListForProximityScore, self.proximityScore = self.computeEntireScore(self.nList, [self.r0])
     return True
    def initialiseParameters(self):
        # ensure parent initialisation takes place and core values are initialised 
        BBG.initialiseParameters(self) 
        
        self.particleName = self.getParam('particleName')

        self.CPPBBG = CPPBBG(self.paramFilename)
        self.VPCBBG = VPCBBG(self.paramFilename)
        
        if self.noLoadErrors == False:            
            print("Critical Parameters are undefined for constrained polymer object")
            sys.exit()
Example #4
0
def makeWormNetwork(numNodesInit, nodeDist, xSize, ySize, zSize, WormRadius,
                    MaxTubeLength, MaxNumAttempts, ValidNodesPerCluster,
                    minDist, packingFraction):

    VolumeBBG = VPCBBG('VolumePackCuboid.txt')

    # generate the XYZVals of the network nodes packed in the volume
    NetworkNodesXYZ = VolumeBBG.generateBuildingBlock(numNodesInit, -xSize / 2,
                                                      xSize / 2, -ySize / 2,
                                                      ySize / 2, -zSize / 2,
                                                      zSize / 2, nodeDist)

    fIO.saveXYZ(NetworkNodesXYZ.blockXYZVals, 'O', 'cubepackedPoints.xyz')

    # create a list of groups of indices which together form a good network
    ListOfNetworkGroups = groupNodesInNetwork(NetworkNodesXYZ.blockXYZVals,
                                              WormRadius, MaxTubeLength,
                                              ValidNodesPerCluster,
                                              MaxNumAttempts)

    # dump output as file
    outputWormNetworkAsAtoms(ListOfNetworkGroups, NetworkNodesXYZ.blockXYZVals,
                             TubeRadius)

    # for each cluster in the network generate a series of points and directors
    networkPoints = populateClusters(ListOfNetworkGroups,
                                     NetworkNodesXYZ.blockXYZVals, WormRadius,
                                     minDist, packingFraction)

    # repack each list
    xyzVals = [points[0] for points in networkPoints]
    directors = [points[1] for points in networkPoints]
    names = [points[2] for points in networkPoints]

    # return flattened lists
    return ([point for minorList in xyzVals for point in minorList],
            [director for minorList in directors for director in minorList
             ], [name for minorList in names for name in minorList])
Example #5
0
import random as rnd
import copy as cp
from Library.randomPolymer import RandomPolymerPackBBG as RPBBG
from Library.constrainedPolymer import ConstrainedPolymerPackBBG as CPBBG
#from Library.VolumePackSquareBasedPyramid import VolumePackSquareBasedPyramidBBG as VPSBPBBG
from Library.VolumePackCuboid import VolumePackCuboidBBG as VPCBBG
from Projects.spiral  import Spiral
#from Projects.Kagome import computeKagomeLattice as CKL
import Utilities.fileIO as fIO
import Utilities.coordSystems as coords


if __name__=="__main__":

    # create the NPack and polymer objects.
    CuboidPackBBG = VPCBBG("VolumePackCuboid.txt")
    ConstrainedPolyGen = CPBBG("ConstrainedPolymer.txt")
    PolyGen = RPBBG("RandomPolymer.txt")
    
    BoxX = 250
    BoxY = 250
    BoxZ =  70
    
    # Spiral Params
    num3Spirals = 2
    num4Spirals = 2
    num5Spirals = 2
    numStraightPolymers = 4
    numHelicalPolymers = 4
    numParticlesArm = 40
    bondLength = 1.5
class BranchedPolymerPackBBG(BBG):
    '''
    Overloads the building block generator class, and makes use of the constrained polymer class,
    to generate a set of polymer chains between the nodes of arbitary graphs drawn 
    from a set of N points. Each distinct graph is called a cluster. 
    
    The user specifies a set containing the allowed numbers of nodes per cluster, and these 
    are distributed randomly. 
     
    Sets of 2,4 5 and 7 nodes gives rise to a 0, 1 2 or 3 branch points per cluster. 
    Each polymer that connects two nodes of a given graph is drawn using the constrained 
    polymer class. The rest of the network can be passed in as pointsToAvoid to avoid a self crossing.
    But this increases the cost of the calculation.
       
    '''
    def __init__(self, filename):
        BBG.__init__(self, filename)

    def initialiseParameters(self):
        # ensure parent initialisation takes place and core values are initialised 
        BBG.initialiseParameters(self) 
        
        self.particleName = self.getParam('particleName')

        self.CPPBBG = CPPBBG(self.paramFilename)
        self.VPCBBG = VPCBBG(self.paramFilename)
        
        if self.noLoadErrors == False:            
            print("Critical Parameters are undefined for constrained polymer object")
            sys.exit()

    def generateBuildingBlock( self, 
                               numPoints,
                               x1,
                               x2,
                               y1,
                               y2,
                               z1,
                               z2,
                               networkMinDist,
                               neighborRadius,
                               monomerMinDist,
                               bondLength,
                               curlyFactor,
                               coneAngle,
                               minAngle,
                               threshold,
                               maxNeighbours,
                               angularRange=['None'],
                               pointsToAvoid=[],
                               visualiseEnvelope=(0,20),
                               envelopeList=["None"]):

        self.numPoints = numPoints
        self.bondLength = float(bondLength)
        self.networkMinDist = networkMinDist
        self.neighborRadius = neighborRadius
        self.monomerMinDist = monomerMinDist
        self.x1 = x1
        self.x2 = x2
        self.y1 = y1
        self.y2 = y2
        self.z1 = z1
        self.z2 = z2
        self.coneAngle = coneAngle
        self.minAngle = minAngle
        self.threshold = threshold
        self.maxNeighbours = maxNeighbours
        self.curlyFactor = curlyFactor
        self.pointsToAvoid = pointsToAvoid
        self.visualiseEnvelope = visualiseEnvelope
        self.envelopeList = envelopeList
        self.angularRange = angularRange
        
        return BBG.generateBuildingBlock(self, numPoints, networkMinDist, envelopeList=envelopeList, visualiseEnvelope=visualiseEnvelope, pointsToAvoid=pointsToAvoid)

    def generateBuildingBlockDirector(self):
        return  np.array([0.0, 0.0, 1.0])
    
    def generateBuildingBlockRefPoint(self):
        return np.array( [ ( self.x1 + self.x2 ) / 2.0, 
                           ( self.y1 + self.y2 ) / 2.0, 
                           ( self.z1 + self.z2 ) / 2.0 ] )

    def generateBuildingBlockNames(self):
        return [self.particleName] * self.numPoints

    def generateBuildingBlockXYZ(self):
        # pick n random points in the specified envelope within a cubic region of space.
        points = self.VPCBBG.generateBuildingBlock(self.numPoints, 
                                                   self.x1, 
                                                   self.x2, 
                                                   self.y1, 
                                                   self.y2, 
                                                   self.z1, 
                                                   self.z2, 
                                                   self.networkMinDist, 
                                                   envelopeList=self.envelopeList,
                                                   pointsToAvoid=self.envelopeList,
                                                   visualiseEnvelope=self.visualiseEnvelope)

        if self.dumpInterimFiles==1:
            fIO.saveXYZ(points.blockXYZVals, 'C', 'pointsInCube.xyz')

        # generate a random graph of connections between all the points,
        # such that no node has more than 3 connections and every node has at least one edge
        g = self.splitPointsIntoGraphs(points.blockXYZVals)
        
        if self.dumpInterimFiles==1:
            self.visualiseNetwork(g, points.blockXYZVals, self.monomerMinDist)

        return self.connectGraphWithConstrainedPolymer(g, points.blockXYZVals)

    def visualiseNetwork(self, g, points, radius):
        dZ = 0.1 * radius # ten particles per radius 
        xyzVals = []
        for edge in g.edges:
            node1 =  points[edge[0]]
            node2 =  points[edge[1]]
            director = node2 - node1
            length = np.linalg.norm(director)
            director = director/length
            numPoints = int(length/dZ)
            xyzVals += [  node1 + float(zIndex) * dZ * director for zIndex in range(0, numPoints)   ]
                
        fIO.saveXYZ(xyzVals, 'P', 'rawNetwork.xyz')        

    
    def splitPointsIntoGraphs(self, points):
        
        # nodes in graph are numbered 0 to numNodes - 1
        # which correspond to the index in points. 
        numNodes = len(points)
        
        # set up an empty graph
        g = nx.Graph()

        # add the nodes to the graph 
        for nodeNum in range(0, numNodes):
            g.add_node(nodeNum)

        params=[self.neighborRadius, self.coneAngle, self.minAngle, self.threshold, self.maxNeighbours]

        # Add the edges and return
        return self.add_edges(g, points, params, mode="natural")
    
    # switcher between edge adding algorithms    
    def add_edges(self, g, points, params, mode="natural"):
        
        if mode=="deterministic":
            outputGraph = self.add_edges_deterministic(g)
    
        if mode=="natural":
            outputGraph = self.add_edges_natural(g, points, params[0], params[1], params[2], params[3], params[4])

        # find singleton nodes
        nodesToRemove=[]
        for node in outputGraph.nodes():
            if sum(1 for _ in g.neighbors(node))==0:
                nodesToRemove += [node]

        # remove singleton nodes
        for node in nodesToRemove:
            outputGraph.remove_node(node)

        return outputGraph

    # randomly picks two points less than radius apart and uses that as the axis for a cone of interior angle coneangle
    # Then picks point inside that cone, such that no points subtend an angle less than minAngle.
    # keep going until threshold percentage of points have at least one edge
    def add_edges_natural(self, g, points, neighborRadius, coneAngle, minAngle, threshold, maxEdgesPerNode):
    
        numPoints = len(points)

        numFreeNodes = self.countFreeNodes(g)
        minNumFreeNodes = int((1.0 - threshold) * float(numPoints))    
                
        while ( numFreeNodes > minNumFreeNodes):  

            # pick a node at random
            node1 = rnd.randint(0, numPoints-1)
            
            # only pick a node if it has less than maxEdgesPerNode
            if sum(1 for _ in g.neighbors(node1)) < maxEdgesPerNode:
                
                # pick a second node at random
                node2 = rnd.randint(0, numPoints-1)
                
                # compute director between vector if node1 and node 2 are not the same node
                if not node1==node2:
                    director = points[node2] - points[node1]
                    length = np.linalg.norm(director)
                    directorHat = director/length 
                
                # only pick second node if it not node 1, has less than maxEdgesPerNode and is within radius of the first node 
                if (not node1==node2) and (sum(1 for _ in g.neighbors(node2)) < maxEdgesPerNode) and length < neighborRadius:
                    
                    # add edge between the two root nodes
                    g.add_edge(node1, node2)

                    # begin a list of points in the current chain that we are making 
                    xyzList = [ points[node1], points[node2] ]

                    # generate a list of points that are in the allowed cone                    
                    coneList = self.pickPointsInCone(points[node1], coneAngle, directorHat, points, xyzList)

                    # loop through the cone list checking each point in order to see
                    # a) it's less than radius from last point on list
                    # b) forms an angle greater than minAngle with previous 2 points on list
                    # c) has less edges than maxEdgesPerNode
                    lastNode = node2
                    for node in coneList:
                        if (np.linalg.norm(points[node] - xyzList[-1]) < neighborRadius ): 
                            if coords.bondAngle(xyzList[-2], xyzList[-1], points[node]) > minAngle:
                                if (sum(1 for _ in g.neighbors(node2)) < maxEdgesPerNode):
                                    g.add_edge(lastNode, node)
                                    xyzList += points[node]
                                    lastNode = node
            numFreeNodes = self.countFreeNodes(g)

        return g
                    
    # returns a list of indices of points in pointsList that are within a cone
    # defined by apex, director and cone angle.
    # Sorts the list of indices by the distance from the apex point along the cone axis
    def pickPointsInCone(self, apex, coneAngle, director, pointsList, excludedPoints):
        indexList = []
        tanAngle = np.tan(coneAngle/2)
        for index, point in enumerate(pointsList):
            
            if not True in [ np.linalg.norm(point - testPoint)<1e-6 for testPoint in excludedPoints ]: # ignore the two points that were used to define the cone 
                z = np.dot(point - apex, director)
                y = np.linalg.norm(point - apex - z*director)
                if y/z <= tanAngle and z > float(0.0) :
                    indexList += [(index, z)]

        indexList = sorted(indexList, key=lambda a:a[1])
        return [ item[0] for item in indexList]
                    
            
    def countFreeNodes(self, g):
        return sum( 1 for node in g.nodes if sum(1 for _ in g.neighbors(node))==0 )
 
    
    def add_edges_deterministic(self, g):
        # loop through the nodes once and add one two or 3 edges to the node.
        for node in g.nodes():
            
            # randomly choose how many edges this node should have
            numEdgesToAdd = rnd.choice([1,2,3])
            
            # count number of current edges on the node
            numCurrentEdges = sum(1 for _ in g.neighbors(node))
            
            # reduce number of edges 
            numEdgesToAdd  = numEdgesToAdd - numCurrentEdges
            
            # only add edges to this node if we still need to 
            if numEdgesToAdd>0:
            
                # finds, or creates the right number of nodes that can 
                # still be connected to (excluding the current node).
                freeNodes = self.findFreeNodes(g, [node], numEdgesToAdd)
                
                # connect the chosen nodes to the current node 
                for node2 in freeNodes:
                    # add an edge between the node and the selected free nodes
                    g.add_edge(node, node2)
            
        return g

    def findFreeNodes(self, g, protectedNodes, targetNumFreeNodes):

        freeNodes = []

        # make sure the requested number of nodes is smaller than the number of nodes
        # we are allowed to look at (total # of nodes - # of protected nodes)
        if targetNumFreeNodes <= ( len(g) - len(protectedNodes) ): 
            
            # first look for nodes with no neighbours, then 1, then 2.
            for targetNumEdges in [0, 1, 2]:
                # loop through the nodes (backwards!) and look for nodes with targetNumEdges
                # backwards encourages a more broken up network with several components 
                for node in range(len(g)-1, 0, -1):
                    # make sure the current node is not on the protected list 
                    if not node in protectedNodes:
                        # if the current node has the targetNumEdges then add it to the free 
                        # node list.
                        if (sum(1 for _ in g.neighbors(node))==targetNumEdges):
                            freeNodes+=[node]
        
                    # break the inner loop if we already have enough nodes                    
                    if len(freeNodes)==targetNumFreeNodes:
                        break
                # break the outer loop if we already have enough nodes                    
                if len(freeNodes)==targetNumFreeNodes:
                    break
    
            # if we got through the 0, 1 and 2 nodes and there aren't enough free nodes
            # we need to disconnect some edges.   
            # If we are in this state then we know that all the nodes 
            # have 3 edges and there are enough non-protected nodes to generate the free_list
            if (len(freeNodes)<targetNumFreeNodes)==True:
                # loop through unprotected nodes and remove an edge
                # add the current node to the free nodes.
                # The second node is also free, but don't want to do an insertion.
                # might make for a dull networks.So this will leave 3 nodes open - will help
                # with later nodes anyway. 
                for node in g.nodes():
                    if not node in protectedNodes:
                        for node2 in g.neighbours(node):
                            g.remove_edge(node, node2)
                            break # only take the first neighbor
                    if len(freeNodes)<targetNumFreeNodes:
                        freeNodes+=node
                    if len(freeNodes)>=targetNumFreeNodes:
                        break;
        else:
            print("Impossible to find enough free nodes to connect to.")
            exit()

        if len(freeNodes)!=targetNumFreeNodes:
            print("Warning: numfreeNodes not equal to target number but run out of things to try")

        return freeNodes


    # checks to see how many nodes have no neighbours.
    def numZeroNodes(self, g):
        numZeroNodes = 0
        for node in g.nodes():
            if sum(1 for _ in g.neighbors(node))==0:
                numZeroNodes+=1
        return numZeroNodes
        
    def connectGraphWithConstrainedPolymer(self, g, points):
        '''
        # we now traverse the graph generating a polymer for each edge in the network.
        # the nodes are the indices of the A and B points for each network, and the distance between
        # the points determines the minmum length of polymer, with extra length allowed by the curly factor.
        # We supply the growing list of points as points to avoid for subsequent chains.
        # Might make some impossible situations, so choose parameters carefully.
        '''
        
        # create an array to store output
        outputXYZ = []  
        
        # specify number of crank moves to try to fold polymer in to zone
        numCrankMoves = 0
        
        # loop through the edges in g and generate a self avoiding random polymer
        # that connects the specified points in the space
        for edge in g.edges():
            pointA = points[edge[0]]
            pointB = points[edge[1]]
            
            # generate local points to avoid 
            
            # copy externally supplied points
            l_pointsToAvoid = cp.copy(self.pointsToAvoid) 
            
            # add existing output from other polymers providing they are more than minDist away from pointA and pointB
            for point in outputXYZ:
                if np.linalg.norm(point - pointA) > self.minDist and np.linalg.norm(point - pointB) > self.monomerMinDist:   
                    l_pointsToAvoid += [point] 
            
            # add the other node points in the network providing they are far enough away from pointA and pointB
            for point in points:
                if np.linalg.norm(point - pointA) > self.minDist and np.linalg.norm(point - pointB) > self.monomerMinDist:   
                    l_pointsToAvoid += [point] 

            # compute the number of vertices we will need between the two points.        
            numPoints = int(self.curlyFactor * np.linalg.norm(pointA - pointB)/self.bondLength)
        
            polymerBB = self.CPPBBG.generateBuildingBlock(  numPoints, 
                                                            pointA, 
                                                            pointB, 
                                                            self.monomerMinDist, 
                                                            self.bondLength, 
                                                            numCrankMoves, 
                                                            l_pointsToAvoid, 
                                                            visualiseEnvelope=self.visualiseEnvelope, 
                                                            envelopeList=self.envelopeList,
                                                            angularRange=self.angularRange)
            
            # add the newly minted points to the list of output points. 
            outputXYZ += [ point for point in polymerBB.blockXYZVals]

        return outputXYZ
class BranchedPolymerPackBBG(BBG):
    '''
    Overloads the building block generator class, and makes use of the random polymer pack generator,
    to generate a set of interweaving polymer chains. The directors of the first generation of chains can be 
    correlated within a specified deviation to a global director.
    
    The first generation of polymer chains can then be used as a seed points for branching polymer chains 
    which have a specified density and minimum interior branch angle.  Each of the subsequent unimers branches
    may then be used as a seed points for additional unimers up to a specified number of generations of branching. 
    
    Every unimer that is generated will have a envelope which is a cone. The same shape cone will be used 
    for all unimers - they are related molecules afterall - but the orientation will be selected based on the degree
    of correlation selected.  Prior to computing each new unimer any of the over all set of points that are in the cone
    will be isolated as points to Avoid for that unimer. In this way no unimer thread should intersect any other. 
    
    The self avoidance can be turned off. It turns out the polymers miss each other most the time anyway. Can live with 
    the odd clash, if we're only making cartoons.   
    
    A networkx graph of the polymer network is created whose nodes are the indices of the points in output xyzlist
    and whose edges are the points in that list which are connected by a bond. 
    
    There are many additional parameters: 
    the alpha and beta ranges for the internal angles of the polymer.
    the networkMinDist is the distance between the starting points of the polymer chains
    MonomerMinDist is as close as two chains can come. 
    BondLength is the distance between successive unimers along the chain. Bondlength must be longer than minDist
    or the chain self rejects. (sorry).
    Minpoints per unimer is the smallest number of points allowed on a chain. typically the chain length is 
    determined by the distance to the outer box, and then 50 % of the time the polymers are 
    randomly truncated by a randomfactor between half and 1. MaxNumAttempts is a bailout counter for some
    while loops which repeat various unimer constructions. Oftimes the starting point is just in a bad position. 
     
    Uggh. I'm bored. figure out the rest of the params yourself. They're pretty self explanatory.
    
    
    '''
    def __init__(self, filename):
        BBG.__init__(self, filename)

    def initialiseParameters(self):
        # ensure parent initialisation takes place and core values are initialised
        BBG.initialiseParameters(self)

        self.particleName = self.getParam('particleName')

        self.RPPBBG = RPPBBG(self.paramFilename)
        self.VPCBBG = VPCBBG(self.paramFilename)

        if self.noLoadErrors == False:
            print(
                "Critical Parameters are undefined for branched polymer network object"
            )
            sys.exit()

    def generateBuildingBlock(self,
                              numGen1Unimers,
                              x1,
                              x2,
                              y1,
                              y2,
                              z1,
                              z2,
                              networkMinDist,
                              alpha1,
                              alpha2,
                              beta1,
                              beta2,
                              monomerMinDist,
                              bondLength,
                              minNumPointsUnimer,
                              maxNumAttempts,
                              selfAvoid,
                              coneAngle,
                              directorMaxPitch,
                              globalDirector,
                              numBranchGenerations,
                              branchDensity,
                              minBranchAngle,
                              angularRange=['None'],
                              pointsToAvoid=[],
                              visualiseEnvelope=(0, 20),
                              envelopeList=["None"],
                              SpaceCurveTransform=True):

        numPoints = 0  # computed on the fly in this case
        self.networkGraph = nx.Graph()
        self.numGen1Unimers = numGen1Unimers
        self.x1 = x1
        self.x2 = x2
        self.y1 = y1
        self.y2 = y2
        self.z1 = z1
        self.z2 = z2
        self.networkMinDist = networkMinDist
        self.monomerMinDist = monomerMinDist
        self.alpha1 = alpha1
        self.alpha2 = alpha2
        self.beta1 = beta1
        self.beta2 = beta2
        self.bondLength = bondLength
        self.minNumPointsUnimer = minNumPointsUnimer
        self.maxNumAttempts = maxNumAttempts
        self.selfAvoid = selfAvoid
        self.coneAngle = coneAngle * np.pi / 180.0
        self.directorMaxPitch = directorMaxPitch * np.pi / 180.0
        self.globalDirector = globalDirector
        self.numBranchGenerations = numBranchGenerations
        self.branchDensity = branchDensity
        self.minBranchAngle = minBranchAngle * np.pi / 180.0
        self.pointsToAvoid = pointsToAvoid
        self.visualiseEnvelope = visualiseEnvelope
        self.envelopeList = envelopeList + [
            'cuboid ' + str(x1) + ' ' + str(x2) + ' ' + str(y1) + ' ' +
            str(y2) + ' ' + str(z1) + ' ' + str(z2)
        ]
        self.angularRange = angularRange
        self.blockRefPoint = self.generateBuildingBlockRefPoint()
        self.SpaceCurveTransform = SpaceCurveTransform

        return BBG.generateBuildingBlock(self,
                                         numPoints,
                                         networkMinDist,
                                         envelopeList=self.envelopeList,
                                         visualiseEnvelope=visualiseEnvelope,
                                         pointsToAvoid=pointsToAvoid)

    def generateBuildingBlockDirector(self):
        return np.array([0.0, 0.0, 1.0])

    def generateBuildingBlockRefPoint(self):
        return np.array([(self.x1 + self.x2) / 2.0, (self.y1 + self.y2) / 2.0,
                         (self.z1 + self.z2) / 2.0])

    def generateBuildingBlockNames(self):
        return [self.particleName] * self.numPoints

    # over load the final function to get a building block, so that the networkGraph can be passed back as well.
    def getBuildingBlock(self):
        # access function that returns a building block based on the backbone
        return (BuildingBlock(self.buildingBlockXYZ, self.blockNames,
                              self.blockConnectors, self.blockRefPoint,
                              self.blockDirectorHat), self.networkGraph)

    def generateBuildingBlockXYZ(self):
        # first pick n random points in the specified envelope within a cubic region of space.
        # These are the unimer start points. Leave a margin at the edge to try to eliminate dodgy scenarios
        startPoints = self.VPCBBG.generateBuildingBlock(
            self.numGen1Unimers,
            self.x1 + self.minNumPointsUnimer * self.bondLength,
            self.x2 - self.minNumPointsUnimer * self.bondLength,
            self.y1 + self.minNumPointsUnimer * self.bondLength,
            self.y2 - self.minNumPointsUnimer * self.bondLength,
            self.z1 + self.minNumPointsUnimer * self.bondLength,
            self.z2 - self.minNumPointsUnimer * self.bondLength,
            self.networkMinDist,
            envelopeList=self.envelopeList,
            pointsToAvoid=self.pointsToAvoid)

        if self.dumpInterimFiles == 1:
            fIO.saveXYZ(startPoints.blockXYZVals, 'C', 'pointsInCube.xyz')

        # for each point generate a direction relative to the globalDirector in the allowed pitch range.
        # azimuthally can choose anything, but elevation wise we are restrained to be within directorMaxPitch
        # angle of the globalDirector. Choose a direction that yields at least minNumPointsUnimer
        gen1UnimerDirectors = [
            self.genDirector(point, self.minNumPointsUnimer,
                             self.globalDirector, self.directorMaxPitch,
                             self.maxNumAttempts)
            for point in startPoints.blockXYZVals
        ]

        # set up output arrays
        xyzPoints = []
        UnimerIndices = []
        unimersToBranchFrom = []

        currentUnimer = 0
        # Now generate the gen1 Unimers
        for point, director in zip(startPoints.blockXYZVals,
                                   gen1UnimerDirectors):
            print("Generating Unimer: ", currentUnimer + 1, " of ",
                  self.numGen1Unimers)

            # generate the next list of xyz values
            if self.selfAvoid:
                # director[0] is the vector, director[1] is the number of points
                unimerXYZVals = self.generateUnimer(
                    point, director[0], director[1],
                    xyzPoints + self.pointsToAvoid, self.maxNumAttempts)
            else:
                unimerXYZVals = self.generateUnimer(point, director[0],
                                                    director[1],
                                                    self.pointsToAvoid,
                                                    self.maxNumAttempts)

            # make a tuple to note the start and end index of the new unimer in the points array.
            # add that tuple to a list of indices
            UnimerIndices += [(len(xyzPoints),
                               len(xyzPoints) + len(unimerXYZVals))]

            # add the xyz points of the unimer to the growing list of xyzValues
            xyzPoints += cp.copy(unimerXYZVals)

            # make a note of the index of the latest tuple pair added.
            # Each generation only adds to the last worms created
            unimersToBranchFrom += [len(UnimerIndices) - 1]

            currentUnimer += 1

        print("First Generation Unimers Complete.")

        if self.dumpInterimFiles == 1:
            fIO.saveXYZ(xyzPoints, 'Ca', 'FirstGenUnimers.xyz')

        # loop through the number of generations we want to have
        for genNum in range(0, self.numBranchGenerations):
            print("Now creating unimers for generation ", genNum + 1, " of ",
                  self.numBranchGenerations)

            # create a new generation of unimers that will branch off the specified unimers.
            # return a full list of all the points, the indices of each unimer in points
            # and a list of new unimers from which we can branch from on the next generation
            xyzPoints, UnimerIndices, unimersToBranchFrom = self.generateNextUnimerGeneration(
                xyzPoints, UnimerIndices, unimersToBranchFrom)

        # we have a list of tuples which contain the start and end indices of each unimer.
        # Add this information to the graph network to complete the graphs.
        # The nodes that connect the daughter and parent unimers together are already in there.
        self.addNodesAndEdgesToGraphFromUnimerIndices(UnimerIndices)

        return xyzPoints

    # Each entry in unimer indices indicates a connected set of nodes.
    # Add those nodes to the graph and then edges between them.
    def addNodesAndEdgesToGraphFromUnimerIndices(self, UnimerIndices):
        for segment in UnimerIndices:

            # add the first node of the segment if it is not there already
            if not self.networkGraph.has_node(segment[0]):
                self.networkGraph.add_node(segment[0])

            # for each subsequent node, add it and connect it to the previous node in the segment
            for node in range(segment[0] + 1, segment[1]):
                if not self.networkGraph.has_node(node):
                    self.networkGraph.add_node(node)

                # add the edge connecting the previous node to the current node
                self.networkGraph.add_edge(node - 1, node)

    # generates a director for a given point. Makes sure that there is space for at least N bond lengths
    # from the point to the edge of the box for that director. Also ensure the picked director is within
    # angle radians of gDirector.
    def genDirector(self,
                    point,
                    minNumPoints,
                    gDirector,
                    angle,
                    maxNumAttempts,
                    within=True):

        numPoints = 0
        maxNumPoints = 0
        numLoops = 0
        # loop until we exceed the minimum number of points requested or we have tried 10 times
        while numPoints < minNumPoints and numLoops < maxNumAttempts:
            if numLoops > 0:
                print("Finding alternative director. Attempt: ", numLoops + 1,
                      "of ", maxNumAttempts)
            if within:
                # for choosing first generation unimers (must be within angle of gDirector)
                theta, phi = coords.pickRandomPointOnUnitSphereInAngRange(
                    np.pi / 2 - angle, np.pi / 2, -np.pi, np.pi)
            else:
                # for choosing nth generation unimers (must be between angle and 2 angle of gdirector)
                theta, phi = coords.pickRandomPointOnUnitSphereInAngRange(
                    np.pi / 2 - 2 * angle, np.pi / 2 - angle, -np.pi, np.pi)

            # convert theta, phi pair to xyz vectors
            directorHat = coords.sphericalPolar2XYZ(np.array([1.0, theta,
                                                              phi]))

            #  rotate director to take into account orientation of globalDirector
            directorHatRot = coords.transformFromBlockFrameToLabFrame(
                gDirector, np.array([0.0, 0.0, 0.0]), 0.0,
                np.array([0.0, 0.0, 1.0]), np.array([0.0, 0.0, 0.0]),
                [directorHat])[0]

            # calculate distance along director vector to the outer edge of the box.
            dist2Box = self.dist2Box(point, directorHatRot)

            # compute number of points we can fit in there assuming straight line to edge of box
            numPoints = int(dist2Box / self.bondLength)

            # make a note of the direction that gives the largest number of points so far
            if numPoints > maxNumPoints:
                maxDirector = cp.copy(directorHatRot)
                maxNumPoints = numPoints

            if numPoints < minNumPoints:
                print("low num points")

            numLoops += 1

        # if we have exited the loop then check to see if we have succeeded.
        # if we have failed then return the best director we encountered.
        if numPoints < minNumPoints:
            print("Warning: Director choice resulted in short polymer")
            numPoints = maxNumPoints
            directorHatRot = maxDirector

        return (directorHatRot, numPoints)

    def generateNextUnimerGeneration(self, points, indices, unimersToGrowFrom):
        # The points for the entire network are contained in flat list of points.
        # Each unimer is identified by a tuple of indices referring to the start and end points in the array.
        # The list of tuples is stored in indices.
        # Unimers to Grow from is a list of unimers i.e. their position in the index list, from which to grow
        # a new unimer.

        # create an array to keep track of the index pairs of each new unimer we add them.
        # This will form the list to grow from for the next generation.
        newUnimersToGrowFrom = []

        # loop through the unimers to grow from.
        for unimerNum, unimer in enumerate(unimersToGrowFrom):

            print("Generating Unimer: ", unimerNum + 1, " of ",
                  len(unimersToGrowFrom))

            # get the indices of the current unimer from which we are going to branch
            startIndex = indices[unimer][0]
            endIndex = indices[unimer][1]
            length = endIndex - startIndex

            # compute how many branch points to add
            numBranchPoints = int(float(length) * self.branchDensity)

            # generate a list of indexes where the branches will happen
            seedPoints = []
            for _ in range(0, numBranchPoints):
                newSeedPoint = rnd.randint(
                    startIndex + 2, endIndex - 2
                )  # don't use the first few points in a unimer - makes for crappy looking networks.
                while newSeedPoint in seedPoints:  # loop until we don't pick the same index multiple times
                    newSeedPoint = rnd.randint(startIndex + 2, endIndex - 2)
                seedPoints += [newSeedPoint]

            # loop through the selected seed points
            for seedPoint in seedPoints:
                # generate a director that is not within branch angle of the local
                # seed unimer
                director = self.genDirector(points[seedPoint],
                                            self.minNumPointsUnimer,
                                            points[seedPoint + 1] -
                                            points[seedPoint],
                                            self.minBranchAngle,
                                            self.maxNumAttempts,
                                            within=False)

                if self.selfAvoid:
                    newUnimerXYZ = self.generateUnimer(
                        points[seedPoint], director[0], director[1],
                        points + self.pointsToAvoid, self.maxNumAttempts)
                else:
                    newUnimerXYZ = self.generateUnimer(points[seedPoint],
                                                       director[0],
                                                       director[1],
                                                       self.pointsToAvoid,
                                                       self.maxNumAttempts)

                # Add the current new unimer's point indices to the list of unimers start and stop indices - we subtract 1
                # because the seed point is already in the parent unimer. We are only adding the new points.
                indices += [(len(points), len(points) + len(newUnimerXYZ) - 1)]

                # At this point this is the only time in the program we know the index values of
                # both the parent and daughter unimers and where they are connected.
                # so add this information to the graph.

                # add the current seed point as a node if it isn't already there
                if not self.networkGraph.has_node(seedPoint):
                    self.networkGraph.add_node(seedPoint)

                # add the start index of the most recent unimer added to the unimer index list
                if not self.networkGraph.has_node(indices[-1][0]):
                    self.networkGraph.add_node(indices[-1][0])

                # connect those two nodes in the graph
                self.networkGraph.add_edge(seedPoint, indices[-1][0])

                # add the xyz points of the unimer to the growing list of xyzValues
                # only add the new points - don't add the seed point which would be redundant.
                points += cp.copy(newUnimerXYZ[1:])

                # add the index number of the latest unimer to the newUnimersToGrowFrom list for the next gen
                # subtract 1 because it is zero based, where the length of the array is not.
                newUnimersToGrowFrom += [len(indices) - 1]

        return points, indices, newUnimersToGrowFrom

    # generates a single unimer which starts at point point, in direction of director
    # avoiding all the points in xyzList, and uses global params in the class to
    # define the unimer details.
    def generateUnimer(self, point, director, numPoints, pointsToAvoid,
                       maxNumAttempts):

        # half the time, scale the number of points by a random factor between 0.5 and 1.0
        # otherwise all the unimers will leave the box
        if rnd.uniform(0.0, 1.0) > 1.0:
            numPoints *= rnd.uniform(0.5, 1.0)

        # make the numPoints an integer
        numPoints = int(numPoints)

        # develop the frustum definition in the block frame of the unimer
        z1 = -self.bondLength
        r1 = self.bondLength
        z2 = numPoints * self.bondLength
        r2 = z2 * np.tan(self.coneAngle / 2.0)

        localEnvelopeList = [
            "frustum " + str(z1) + ' ' + str(r1) + ' ' + str(z2) + ' ' +
            str(r2)
        ]

        if numPoints == 0:
            print("Zero Length Polymer")
        numPointsOutside = numPoints
        numLoops = 0
        # loop until we create a unimer that satisfies constraints. Can set maxNumAttempts depending on how bothered we are about constraints.
        while numPointsOutside > 0 and numLoops < maxNumAttempts:
            if numLoops > 0:
                print(
                    "Re-generating Unimer - exceeds global envelope. Attempt: ",
                    numLoops + 1, "of ", maxNumAttempts)
            # generate the unimer in it's own block frame starting at origin. Don't worry about outer envelope or other points at this stage
            unimer = self.RPPBBG.generateBuildingBlock(
                numPoints,
                np.array([0.0, 0.0, 0.0]),
                self.alpha1,
                self.alpha2,
                self.beta1,
                self.beta2,
                self.monomerMinDist,
                self.bondLength,
                pointsToAvoid=[],
                envelopeList=localEnvelopeList,
                SpaceCurveTransform=self.SpaceCurveTransform)
            #visualiseEnvelope=(100000,200,'envelopeUnimer.xyz'))

            if self.dumpInterimFiles == 1:
                fIO.saveXYZ(unimer.blockXYZVals, 'Ca', 'preRotUnimer.xyz')

            # now rotate and translate the unimer xyzVals to their final point
            rotXYZVals = coords.transformFromBlockFrameToLabFrame(
                director, point, 0.0, np.array([0.0, 0.0, 1.0]),
                np.array([0.0, 0.0, 0.0]), unimer.blockXYZVals)

            # Now check that the unimer is entirely within the outer box, and any global specified user envelope,
            # Also make sure that the unimer is networkMinDist points away from existing pointsToAvoid.
            # Won't self check the unimer against itself only the points to avoid. Allows to have two minDists.
            # One for the monomer distance along the backbone, and one for the distance between unimers (2 * worm tube radius).
            # enables the unimer bondlength to be small which makes for smoother unimer chains.
            numPointsOutside = len(
                self.checkEnvelope(rotXYZVals,
                                   ignorePTA=False,
                                   pointsToAvoid=pointsToAvoid))

            # increment loop counter
            numLoops += 1

        return rotXYZVals


#   def choosePointsToAvoid(self, points, Z1, R1, Z2, R2):
#       # go through points and keep any points that are inside the frustum
#       outPoints = []
#       for point in points:
#           if coords.checkPointInFrustum(point, Z1, R1, Z2, R2, 0):
#               outPoints += [ point ]
#       return outPoints

    def dist2Box(self, point, director):
        # Find the distance from the point to the outer box in the direction of director

        # compute the line parameter where the given line intersects each of the six planes of the box.
        paramVals = []
        paramVals += [
            cart.distanceOfLineToAPlane(point, director,
                                        np.array([self.x1, 0, 0]),
                                        np.array([1.0, 0.0, 0.0]))
        ]
        paramVals += [
            cart.distanceOfLineToAPlane(point, director,
                                        np.array([self.x2, 0, 0]),
                                        np.array([1.0, 0.0, 0.0]))
        ]
        paramVals += [
            cart.distanceOfLineToAPlane(point, director,
                                        np.array([0, self.y1, 0]),
                                        np.array([0.0, 1.0, 0.0]))
        ]
        paramVals += [
            cart.distanceOfLineToAPlane(point, director,
                                        np.array([0, self.y2, 0]),
                                        np.array([0.0, 1.0, 0.0]))
        ]
        paramVals += [
            cart.distanceOfLineToAPlane(point, director,
                                        np.array([0, 0, self.z1]),
                                        np.array([0.0, 0.0, 1.0]))
        ]
        paramVals += [
            cart.distanceOfLineToAPlane(point, director,
                                        np.array([0, 0, self.z2]),
                                        np.array([0.0, 0.0, 1.0]))
        ]

        # choose the smallest +ve distance. (selects the plane in the director of director)
        # when the line is parallel with the plane we're not bothered about that plane.
        # when the line goes through the corner of the box or two of the planes, it's the same distance
        # so we don't care. Automatically handles these cases.
        return min([a for a in paramVals if a > 0.0])
Example #8
0
            PegXYZ - PEGStrand.blockXYZVals[0]
            for PegXYZ in PEGStrand.blockXYZVals
        ]
        retStrand.addAtoms((newXYZVals, PEGStrand.blockAtomNames))

    retStrand.blockAtomNames = names[:]

    return retStrand


if __name__ == "__main__":

    PolyGen = RPBBG('RandomPolymer.txt')
    SphereBBG = SPSBBG('SurfacePackSphere.txt')
    #PyramidBBG = VPSBPBBG('VolumePackPyramid.txt')
    CubeBBG = VPCBBG('VolumePackCuboid.txt')

    numPolymersPerSphere = 195
    AtomicMinDist = 1.0
    NumPEG = 60
    NumHPMA = 113
    bondLength = 1.5
    FRadius1 = 20.0
    FRadius2 = 2.0
    FZ1 = 150.0
    FZ2 = 10.0

    BaseX = 4000.0
    BaseY = 5000.0
    BaseZ = 1000.0
    Volume = BaseX * BaseY * BaseZ