def panels(self):
     """Create two panels of nodes at the front and back of lattice."""
     front, back = [], []
     for y, z in product(range(1, self.__ydim + 1),
                         range(1, self.__zdim + 1)):
         front.append(tau(self.__xdim, y, z))
         back.append(tau(1, y, z))
     return (back, front)
 def determine_shell(self, node):
     """
     Instantiates a shell of immediately neighbouring points that
     correspond to the fusion connections in a brickwork pattern. This is 
     done by a positive and negative x-direction connection, and then
     alternation each consecutive node in any of three directions, between
     positive and negative connections in y-direction and z-direction.
     """
     shell = []
     nodex = self.__latt.node[node]['X']
     nodey = self.__latt.node[node]['Y']
     nodez = self.__latt.node[node]['Z']
     value = self.__latt.node[node]['value']
     # Factor to determine the alternating brickwork lattice connections.
     factor = (-1)**(nodey + nodez + nodex)
     # The x-dimension is not affected by alternating factor.
     if 1 < nodex < self.__xdim:
         if value in [3, 2]:
             alt_ = 1 if value == 3 else -1
             shell.append(tau(nodex + alt_, nodey, nodez))
             shell.append(tau(nodex - alt_, nodey, nodez))
     elif nodex == self.__xdim:
         if value in [3, 2]:
             shell.append(tau(nodex - 1, nodey, nodez))
     else:
         if value in [3, 2]:
             shell.append(tau(nodex + 1, nodey, nodez))
     # The y-dimension is affected by alternating factor.
     if 1 < nodey < self.__ydim:
         if value in [3, 1]:
             shell.append(tau(nodex, nodey + factor, nodez))
     elif nodey == self.__ydim:
         if factor == -1:
             if value in [3, 1]:
                 shell.append(tau(nodex, nodey - 1, nodez))
     else:
         if factor == +1:
             if value in [3, 1]:
                 shell.append(tau(nodex, nodey + 1, nodez))
     # The z-dimension is affected by alternating factor
     if 1 < nodez < self.__zdim:
         if value in [3, 1]:
             shell.append(tau(nodex, nodey, nodez + factor))
     elif nodez == self.__zdim:
         if factor == -1:
             if value in [3, 1]:
                 shell.append(tau(nodex, nodey, nodez - 1))
     else:
         if factor == 1:
             if value in [3, 1]:
                 shell.append(tau(nodex, nodey, nodez + 1))
     self.__latt.node[node]['shell'] = shell
 def shortest_path(self):
     percolate = self.assess_percolation()
     if percolate:
         for y in range(1, self.__ydim + 1):
             for z in range(1, self.__zdim + 1):
                 self.__latt.add_edge(tau(1, y, z), 'Back')
                 self.__latt.add_edge(tau(self.__xdim, y, z), 'Front')
         length = len(nx.shortest_path(self.__latt, 'Back', 'Front')) - 2
         self.__latt.remove_node('Back')
         self.__latt.remove_node('Front')
         return length
     else:
         return 0
 def wise_chain(self, node, mate):
     """
     Procedure to determine next node in the series of lattice points to
     consider, given the diamond/brickwork connection convention.
     """
     # Considers the neighbouring 'mate' of the current node.
     xm = self.__latt.node[mate]['X']
     ym = self.__latt.node[mate]['Y']
     zm = self.__latt.node[mate]['Z']
     mfactor = (-1)**(xm + ym + zm)
     # There is no next unless convinced otherwise.
     to_continue, next_node = True, None
     if xm == self.__latt.node[node]['X'] + 1:
         # If +ve 1 in the x-direction
         if xm == self.__xdim:
             to_continue = False
         else:
             next_node = tau(xm + 1, ym, zm)
     elif xm == self.__latt.node[node]['X'] - 1:
         # If -ve 1 in the x-direction
         if xm == 1:
             # If hitting the edge of the lattice
             to_continue = False
         else:
             next_node = tau(xm - 1, ym, zm)
     elif int(np.abs(self.__latt.node[node]['Y'] - ym)) == 1:
         # If a positive of negative difference in the y-direction.
         if mfactor == 1:
             if zm == self.__zdim:
                 to_continue = False
             else:
                 next_node = tau(xm, ym, zm + 1)
         elif mfactor == -1:
             if zm == 1:
                 to_continue = False
             else:
                 next_node = tau(xm, ym, zm - 1)
     elif int(np.abs(self.__latt.node[node]['Z'] - zm)) == 1:
         # If a positive of negative difference in the z-direction.
         if mfactor == 1:
             if ym == self.__ydim:
                 to_continue = False
             else:
                 next_node = tau(xm, ym + 1, zm)
         elif mfactor == -1:
             if ym == 1:
                 to_continue = False
             else:
                 next_node = tau(xm, ym - 1, zm)
     return next_node, to_continue
 def __init__(self, xdim, ydim=None, zdim=None, p=0.75):
     """
     Forms microclusters for all points in the graph.
 
     Parameters
     ----------
     p : float
         Probability of fusion of two photons.
     """
     if ydim == None and zdim == None:
         ydim, zdim = xdim, xdim
     elif zdim == None:
         zdim = ydim
     self.__xdim, self.__ydim, self.__zdim = xdim, ydim, zdim
     self.__p = p
     M = nx.Graph()
     M.graph['xdim'], M.graph['ydim'], M.graph['zdim'] = xdim, ydim, zdim
     M.graph['Prob'] = p
     probs = np.array(
         [p**2, p**2 + p * (1 - p), p**2 + 2 * (1 - p) * p, 1.0])
     nvals = [3, 2, 1, 0]
     for x in range(1, xdim + 1):
         for y in range(1, ydim + 1):
             for z in range(1, zdim + 1):
                 M.add_node(tau(x, y, z))
                 M.node[tau(x, y, z)]['X'] = x
                 M.node[tau(x, y, z)]['Y'] = y
                 M.node[tau(x, y, z)]['Z'] = z
                 # Marker for when connecting the nodes so
                 # to avoid double-counting branches.
                 M.node[tau(x, y, z)]['Checked'] = False
                 # Now simulates stochastic microcluster formation success.
                 rand = nr.random()
                 accept = list(rand < probs)
                 try:
                     nv = nvals[accept.index(False)]
                 except ValueError:
                     nv = 0
                 # Returns the first False where rand falls in prob range,
                 # instead of extra lines of if-else statements.
                 M.node[tau(x, y, z)]['value'] = nv
     self.__latt = M
     self.__connected = False