├── .gitignore ├── test.jpg ├── run.sh ├── data ├── cell_indices.txt ├── network_vertices.txt └── edges.txt ├── parameters.py ├── parser.py ├── relax.py ├── energy.py ├── README.md ├── Polygon.py ├── MD.py ├── plot.py ├── steepest_descent.py ├── geometry.py ├── force.py └── transition.py /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alsignoriello/active_vertex_model/HEAD/test.jpg -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | vertex_file=$1 4 | edge_file=$2 5 | poly_file=$3 6 | folder=$4 7 | eta=$5 8 | 9 | 10 | # Relaxation (steepest descent) 11 | # python relax.py $vertex_file $edge_file $poly_file 12 | # python plot.py $vertex_file $edge_file $poly_file 13 | 14 | 15 | # Molecular Dynamics 16 | python MD.py $vertex_file $edge_file $poly_file $folder $eta 17 | 18 | # later, will add division simulations.. 19 | # python divide.py $vertex_file $edge_file $poly_file 20 | 21 | -------------------------------------------------------------------------------- /data/cell_indices.txt: -------------------------------------------------------------------------------- 1 | 0 1 41 40 47 7 2 | 0 7 6 14 15 8 3 | 0 8 9 10 2 1 4 | 1 2 3 43 42 41 5 | 2 10 11 12 4 3 6 | 3 4 5 45 44 43 7 | 4 12 13 14 6 5 8 | 5 6 7 47 46 45 9 | 8 15 23 16 17 9 10 | 9 17 18 19 11 10 11 | 11 19 20 21 13 12 12 | 13 21 22 23 15 14 13 | 16 23 22 30 31 24 14 | 16 24 25 26 18 17 15 | 18 26 27 28 20 19 16 | 20 28 29 30 22 21 17 | 24 31 39 32 33 25 18 | 25 33 34 35 27 26 19 | 27 35 36 37 29 28 20 | 29 37 38 39 31 30 21 | 32 39 38 46 47 40 22 | 32 40 41 42 34 33 23 | 34 42 43 44 36 35 24 | 36 44 45 46 38 37 -------------------------------------------------------------------------------- /parameters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """ 4 | 5 | Builds a parameter dictionary 6 | 7 | """ 8 | 9 | def get_parameters(lx, ly, ka, gamma, Lambda, eta, xi, lmin, delta_t): 10 | 11 | parameters = {} 12 | 13 | # Side length of box in x direction 14 | parameters['lx'] = lx 15 | 16 | # Side length of box in y directions 17 | parameters['ly'] = ly 18 | 19 | # ka - elastic area coefficient 20 | parameters['ka'] = ka 21 | 22 | # gamma - actin myosin contraction coefficient 23 | parameters['gamma'] = gamma 24 | 25 | # lambda - line tension coefficient 26 | parameters['Lambda'] = Lambda 27 | 28 | # eta - noise scaling coefficient 29 | parameters['eta'] = eta 30 | 31 | # xi - motility coefficient 32 | parameters['xi'] = xi 33 | 34 | # lmin - minimum bond length between two vertices 35 | parameters['lmin'] = lmin 36 | 37 | # delta t - time step 38 | parameters['delta_t'] = delta_t 39 | 40 | 41 | return parameters -------------------------------------------------------------------------------- /data/network_vertices.txt: -------------------------------------------------------------------------------- 1 | 0.930866 0.271744 2 | 0.116862 0.802272 3 | 0.563237 1.233827 4 | 0.349978 1.817110 5 | 0.375702 2.313142 6 | 0.604067 2.999615 7 | 0.299085 3.474244 8 | -0.064818 4.652581 9 | 1.398975 -0.074888 10 | 1.391676 1.049655 11 | 1.160173 1.564420 12 | 1.608240 1.961066 13 | 1.342551 2.416602 14 | 1.562803 3.106261 15 | 1.064323 3.488212 16 | 1.391571 3.776807 17 | 2.141749 0.281557 18 | 2.448764 1.049310 19 | 2.586193 1.480577 20 | 2.184154 1.890135 21 | 2.543788 2.569701 22 | 1.866666 2.730649 23 | 2.487271 3.514713 24 | 1.770058 3.801211 25 | 3.063610 0.284279 26 | 3.555957 0.889567 27 | 2.773178 1.238788 28 | 3.222748 1.948208 29 | 3.511009 2.742986 30 | 3.469543 2.862983 31 | 3.386142 3.557367 32 | 3.241826 4.298552 33 | 4.185830 0.387196 34 | 4.197804 0.781788 35 | 3.907473 1.217865 36 | 4.145059 2.032152 37 | 4.437937 2.698011 38 | 3.992574 3.148598 39 | 4.143524 3.371457 40 | 4.134823 4.588720 41 | 4.703293 0.511607 42 | 5.243425 0.469836 43 | 5.109806 1.133103 44 | 5.229939 1.648571 45 | 4.891341 2.325022 46 | 5.372004 3.201157 47 | 5.047313 3.488913 48 | 4.917538 4.471589 -------------------------------------------------------------------------------- /parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | from Polygon import Polygon 4 | from geometry import rand_angle 5 | 6 | """ 7 | 8 | parser.py - defines functions to read and write relevant data 9 | 10 | 11 | author: Lexi Signoriello 12 | date: 1/22/16 13 | 14 | 15 | 16 | """ 17 | 18 | 19 | def read_poly_indices(file): 20 | # indices = np.loadtxt(file, dtype=int) 21 | indices = [] 22 | f = open(file) 23 | for line in f: 24 | poly_indices = [] 25 | linesplit = line.strip().split("\t") 26 | for i in linesplit: 27 | poly_indices.append(int(i)) 28 | indices.append(poly_indices) 29 | f.close() 30 | return indices 31 | 32 | 33 | def build_polygons(cell_indices, A0): 34 | polys = [] 35 | for i,indices in enumerate(cell_indices): 36 | theta = rand_angle() 37 | poly = Polygon(i, indices, A0, theta) 38 | polys.append(poly) 39 | return polys 40 | 41 | 42 | def read_vertices(file): 43 | vertices = np.loadtxt(file) 44 | return vertices 45 | 46 | def write_vertices(vertices, file): 47 | np.savetxt(file, vertices) 48 | return 49 | 50 | # i1 i2 51 | # indices for edge from v1 to v2 52 | def read_edges(file): 53 | edges = np.loadtxt(file).astype(int) 54 | return edges 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /relax.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import sys 3 | from parameters import get_parameters 4 | from steepest_descent import steepest_descent 5 | from parser import * 6 | 7 | """ 8 | 9 | Relaxes current network using a steepest descent method 10 | 11 | 12 | """ 13 | 14 | # command line arguments for data files 15 | vertex_file = sys.argv[1] 16 | edge_file = sys.argv[2] 17 | poly_file = sys.argv[3] 18 | 19 | 20 | # Parameters 21 | lx = 9 * (2 / (3 * (3**0.5)))**0.5 22 | ly = 4 * (2 / (3**0.5))**0.5 23 | ka = 1. 24 | A0 = 1. # current preferred area for polygon 25 | gamma = 0.04 * ka * A0 # hexagonal network 26 | # gamma = 0.1 * ka * A0 # soft network 27 | Lambda = 0.12 * ka * (A0**(3/2)) # hexagonal network 28 | # Lambda = -0.85 * ka * A0**(3/2) # soft network 29 | lmin = 0.2 30 | delta_t = 0.05 31 | eta = 1. 32 | 33 | 34 | # get parameter dictionary 35 | parameters = get_parameters(lx, ly, ka, gamma, Lambda, eta, lmin, delta_t) 36 | 37 | # get vertices 38 | vertices = read_vertices(vertex_file) 39 | 40 | # get edges 41 | edges = read_edges(edge_file) 42 | 43 | # get polygons 44 | poly_indices = read_poly_indices(poly_file) 45 | polys = build_polygons(poly_indices, A0) 46 | 47 | steepest_descent(vertices, edges, polys, parameters) 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /data/edges.txt: -------------------------------------------------------------------------------- 1 | 0 7 2 | 0 1 3 | 0 8 4 | 1 0 5 | 1 41 6 | 1 2 7 | 2 1 8 | 2 3 9 | 2 10 10 | 3 2 11 | 3 43 12 | 3 4 13 | 4 3 14 | 4 5 15 | 4 12 16 | 5 4 17 | 5 45 18 | 5 6 19 | 6 5 20 | 6 7 21 | 6 14 22 | 7 6 23 | 7 47 24 | 7 0 25 | 8 15 26 | 8 0 27 | 8 9 28 | 9 8 29 | 9 10 30 | 9 17 31 | 10 9 32 | 10 2 33 | 10 11 34 | 11 10 35 | 11 12 36 | 11 19 37 | 12 11 38 | 12 4 39 | 12 13 40 | 13 12 41 | 13 14 42 | 13 21 43 | 14 13 44 | 14 6 45 | 14 15 46 | 15 14 47 | 15 8 48 | 15 23 49 | 16 23 50 | 16 17 51 | 16 24 52 | 17 16 53 | 17 9 54 | 17 18 55 | 18 17 56 | 18 19 57 | 18 26 58 | 19 18 59 | 19 11 60 | 19 20 61 | 20 19 62 | 20 21 63 | 20 28 64 | 21 20 65 | 21 13 66 | 21 22 67 | 22 21 68 | 22 23 69 | 22 30 70 | 23 22 71 | 23 15 72 | 23 16 73 | 24 31 74 | 24 16 75 | 24 25 76 | 25 24 77 | 25 26 78 | 25 33 79 | 26 25 80 | 26 18 81 | 26 27 82 | 27 26 83 | 27 28 84 | 27 35 85 | 28 27 86 | 28 20 87 | 28 29 88 | 29 28 89 | 29 30 90 | 29 37 91 | 30 29 92 | 30 22 93 | 30 31 94 | 31 30 95 | 31 24 96 | 31 39 97 | 32 39 98 | 32 33 99 | 32 40 100 | 33 32 101 | 33 25 102 | 33 34 103 | 34 33 104 | 34 35 105 | 34 42 106 | 35 34 107 | 35 27 108 | 35 36 109 | 36 35 110 | 36 37 111 | 36 44 112 | 37 36 113 | 37 29 114 | 37 38 115 | 38 37 116 | 38 39 117 | 38 46 118 | 39 38 119 | 39 31 120 | 39 32 121 | 40 47 122 | 40 32 123 | 40 41 124 | 41 40 125 | 41 42 126 | 41 1 127 | 42 41 128 | 42 34 129 | 42 43 130 | 43 42 131 | 43 44 132 | 43 3 133 | 44 43 134 | 44 36 135 | 44 45 136 | 45 44 137 | 45 46 138 | 45 5 139 | 46 45 140 | 46 38 141 | 46 47 142 | 47 46 143 | 47 40 144 | 47 7 -------------------------------------------------------------------------------- /energy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | from Polygon import Polygon 4 | from geometry import periodic_diff, euclidean_distance 5 | 6 | """ 7 | 8 | energy.py - contains components to compute the potential energy 9 | in the current configuration of vertex model 10 | 11 | 12 | author: Lexi Signoriello 13 | date: 1/20/16 14 | 15 | 16 | 17 | """ 18 | 19 | 20 | def get_energy(vertices, polys, edges, parameters): 21 | # get necessary parameters 22 | lx = parameters['lx'] 23 | ly = parameters['ly'] 24 | L = np.array([lx,ly]) 25 | ka = parameters['ka'] 26 | Lambda = parameters['Lambda'] 27 | gamma = parameters['gamma'] 28 | 29 | e1 = E_elasticity(vertices, polys, ka, L) 30 | e2 = E_adhesion(vertices, edges, Lambda, L) 31 | # take into account double counting edges 32 | e2 = e2 / 4. 33 | 34 | e3 = E_contraction(vertices, polys, gamma, L) 35 | 36 | return (e1 + e2 + e3) 37 | 38 | 39 | 40 | def E_elasticity(vertices, polys, ka, L): 41 | e = 0. 42 | for poly in polys: 43 | a = poly.get_area(vertices, L) 44 | A0 = poly.A0 45 | e += (ka / 2.) * (a - A0)**2 46 | return e 47 | 48 | 49 | 50 | def E_adhesion(vertices, edges, Lambda , L): 51 | e = 0. 52 | for edge in edges: 53 | i1 = edge[0] 54 | i2 = edge[1] 55 | v1 = vertices[i1] 56 | vertex2 = vertices[i2] 57 | v2 = v1 + periodic_diff(vertex2, v1, L) 58 | dist = euclidean_distance(v1[0], v1[1], v2[0], v2[1]) 59 | e += Lambda * dist 60 | return e 61 | 62 | 63 | def E_contraction(vertices, polys, gamma, L): 64 | e = 0. 65 | for poly in polys: 66 | p = poly.get_perim(vertices, L) 67 | e += ((gamma / 2.) * (p**2)) 68 | return e 69 | 70 | 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vertex Model 2 | 3 | The vertex model uses polygons, composed of vertices and edges, to illustrate the mechanics in formation of polygon sheets. This model has successfully been applied to fly wings and eyes in 2D. 4 | 5 | Simulations compute the energy and forces in the system using the following equations: 6 | 7 | E = 0.5 Σα Kα (A α - Aα)2 + Σ i,j Λi,j l i,j + 0.5 Σ α Γα Pα2 8 | 9 | where A is the area and P is the perimeter. 10 | 11 | ⃗Fi = - ⃗∇ iEi 12 | 13 | 14 | The terms represent cell elasticity, line tension and contraction, respectively. 15 | 16 | 17 | 18 | 19 | Farhadifar, R., Röper, J.-C., Aigouy, B., Eaton, S. & Jülicher, F. The Influence of Cell Mechanics, Cell-Cell Interactions, and Proliferation on Epithelial Packing. Current Biology 17, 2095–2104 (2007). 20 | 21 | 22 | # Parameters 23 | 24 | 25 | |Parameter | Definition | Range | 26 | |----------|------------|-------| 27 | | lx | length of box x-axis | positive float | 28 | | ly | length of box y-axis | positive float | 29 | | kA | elasticity | | 30 | | Λ | line tension | | 31 | | Γ | contraction| | 32 | | lmin | minimum bond length | positive float | 33 | | Δ t| time step | small positive float | 34 | 35 | # Input 36 | 37 | vertices.txt (x,y) coordinates for every vertex in network 38 | 39 | edges.txt (index1, index2) indices for every edge between two vertices in the network 40 | 41 | cells.txt (index0, index1, ... indexN) indices in counter-clockwise order that form every cell in the network, a cell is defined as a polygon, does not assume number of sides 42 | 43 | 44 | # Notes 45 | 46 | - Periodic boundary conditions 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Polygon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | from geometry import * 4 | 5 | """ 6 | 7 | Polygon.py - Class Polygon to define unique characteristics 8 | of every polygons in the network 9 | 10 | author: Lexi Signoriello 11 | date: 1/19/16 12 | 13 | vertices - list of all vertices in the network 14 | [(x0,y0), (x1,y1) .... (xNvertices,yNvertices)] 15 | 16 | indices - list of indices mapping to vertices 17 | for every vertex in current poly 18 | * counter-clockwise order 19 | * list of integers 20 | 21 | n_sides - number of sides in polygon for given cell 22 | 23 | L - length of box 24 | * used to compute periodic boundary conditions 25 | 26 | """ 27 | 28 | 29 | 30 | class Polygon: 31 | 32 | 33 | def __init__(self, id, indices, A0, theta): 34 | self.id = id 35 | self.indices = indices 36 | self.A0 = A0 37 | self.theta = theta 38 | 39 | # return list of vertices 40 | # with periodic boundaries 41 | def get_poly_vertices(self, vertices, L): 42 | indices = self.indices 43 | nsides = len(indices) 44 | 45 | # array of x,y vertices in counter-clockwise order 46 | # moving vertices to maintain periodic boundaries 47 | poly_vertices = [] 48 | 49 | # align everything to previous vertex 50 | x0,y0 = vertices[indices[0]] 51 | v0 = np.array((x0,y0)) 52 | v_last = v0 53 | 54 | for i in indices: 55 | x,y = vertices[i] 56 | v = np.array((x,y)) 57 | v_next = v_last + periodic_diff(v, v_last, L) 58 | x,y = v_next 59 | poly_vertices.append((x,y)) 60 | v_last = np.array((x,y)) 61 | return poly_vertices 62 | 63 | def get_area(self, vertices, L): 64 | poly_vertices = self.get_poly_vertices(vertices, L) 65 | a = area(poly_vertices) 66 | return a 67 | 68 | def get_perim(self, vertices, L): 69 | poly_vertices = self.get_poly_vertices(vertices, L) 70 | p = perimeter(poly_vertices) 71 | return p 72 | 73 | def get_center(self, vertices, L): 74 | x,y = center(self.get_poly_vertices(vertices, L)) 75 | return x,y 76 | 77 | def set_indices(self, indices): 78 | self.indices = indices 79 | 80 | -------------------------------------------------------------------------------- /MD.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | from energy import get_energy 4 | from force import get_forces, move_vertices 5 | from transition import T1_transition 6 | import sys 7 | from parameters import get_parameters 8 | from parser import * 9 | from plot import plot_network 10 | 11 | 12 | def molecular_dynamics(vertices, edges, polys, parameters, T, folder): 13 | 14 | delta_t = parameters['delta_t'] 15 | lx = parameters['lx'] 16 | ly = parameters['ly'] 17 | L = np.array([lx,ly]) 18 | 19 | # time 20 | t = 0 21 | count = 0 22 | 23 | while t < T: 24 | 25 | # get energy for network 26 | energy = get_energy(vertices, polys, edges, parameters) 27 | # print energy 28 | 29 | # get forces for network 30 | forces = get_forces(vertices, polys, edges, parameters) 31 | print t, np.sum(forces**2)**(0.5) 32 | 33 | # move vertices 34 | vertices = move_vertices(vertices, forces, parameters) 35 | 36 | # check for T1 transitions 37 | polys, edges = T1_transition(vertices, polys, edges, parameters) 38 | 39 | # add routine to write vertices, energy, forces at every time step 40 | # can be used for plotting routines later... 41 | # write_vertices(vertices, "%s/%.2f.txt" % (folder,t)) 42 | # write edges? 43 | # write polygons? 44 | # if count % 21 == 0: 45 | plot_network(vertices, polys, L, "%s/%.2f.jpg" % (folder,t)) 46 | 47 | count += 1 48 | t += delta_t 49 | 50 | return 51 | 52 | 53 | 54 | # command line arguments for data files 55 | vertex_file = sys.argv[1] 56 | edge_file = sys.argv[2] 57 | poly_file = sys.argv[3] 58 | folder = sys.argv[4] 59 | eta = float(sys.argv[5]) 60 | 61 | 62 | # Parameters 63 | # lx = 9 * (2 / (3 * (3**0.5)))**0.5 64 | # ly = 4 * (2 / (3**0.5))**0.5 65 | L = np.loadtxt("%s/L" % "data") 66 | lx = L[0] 67 | ly = L[1] 68 | 69 | 70 | ka = 1. 71 | A0 = 1. # current preferred area for polygon 72 | gamma = 0.04 * ka * A0 # hexagonal network 73 | # gamma = 0.1 * ka * A0 # soft network 74 | Lambda = 0.12 * ka * (A0**(3/2)) # hexagonal network 75 | # Lambda = -0.85 * ka * A0**(3/2) # soft network 76 | lmin = 0.2 77 | delta_t = 0.05 78 | # eta = 0.01 79 | xi = 0.2 80 | 81 | # maximum Time 82 | T = 5. 83 | 84 | # get parameter dictionary 85 | parameters = get_parameters(lx, ly, ka, gamma, Lambda, eta, xi, lmin, delta_t) 86 | 87 | # get vertices 88 | vertices = read_vertices(vertex_file) 89 | 90 | # get edges 91 | edges = read_edges(edge_file) 92 | 93 | # get polygons 94 | poly_indices = read_poly_indices(poly_file) 95 | polys = build_polygons(poly_indices, A0) 96 | 97 | molecular_dynamics(vertices, edges, polys, parameters, T, folder) -------------------------------------------------------------------------------- /plot.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import sys 3 | from parser import * 4 | from geometry import periodic_diff 5 | 6 | """ 7 | 8 | plot.py - plots the network for vertex model 9 | 10 | 11 | author: Lexi Signoriello 12 | date: 1/19/16 13 | 14 | [vertices] [edges] 15 | 16 | options: 17 | vertices 18 | 19 | line color 20 | 21 | color by number of neighbors 22 | color by area 23 | 24 | 25 | """ 26 | 27 | 28 | def plot_network(vertices, polys, L, file): 29 | plt.cla() 30 | fig = plt.figure() 31 | ax = fig.add_subplot(1,1,1) 32 | for x,y in vertices: 33 | ax.scatter(x, y, c="m", marker=".", s=50) 34 | 35 | for poly in polys: 36 | indices = poly.indices 37 | for i,index in enumerate(indices): 38 | x1,y1 = vertices[index] 39 | if i == len(indices) - 1: 40 | x2,y2 = vertices[indices[0]] 41 | else: 42 | x2,y2 = vertices[indices[i+1]] 43 | 44 | v1 = np.array((x1,y1)) 45 | v2 = np.array((x2,y2)) 46 | v2 = v1 + periodic_diff(v2, v1, L) 47 | x2,y2 = v2 48 | ax.plot([x1,x2], [y1,y2], c="c") 49 | 50 | v2 = np.array((x2,y2)) 51 | v1 = v2 + periodic_diff(v1, v2, L) 52 | x1,y1 = v1 53 | ax.plot([x1,x2], [y1,y2], c="c") 54 | 55 | 56 | # # plot centers 57 | # x,y = poly.get_center(vertices, L) 58 | # plt.scatter(x,y,color="m", marker="*") 59 | 60 | # remove axis ticks 61 | ax.axes.get_xaxis().set_ticks([]) 62 | ax.axes.get_yaxis().set_ticks([]) 63 | 64 | ax.axis([0,L[0],0,L[1]]) 65 | plt.savefig(file) 66 | plt.close(fig) 67 | return 68 | 69 | def plot_edges(vertices, edges, L): 70 | plt.cla() 71 | for vertex in vertices: 72 | x = vertex[0] 73 | y = vertex[1] 74 | plt.scatter(x, y, c="c") 75 | 76 | for edge in edges: 77 | i1 = edge[0] 78 | i2 = edge[1] 79 | x1,y1 = vertices[i1] 80 | x2,y2 = vertices[i2] 81 | v1 = np.array((x1,y1)) 82 | v2 = np.array((x2,y2)) 83 | v2 = v1 + periodic_diff(v2, v1, L) 84 | x2,y2 = v2 85 | plt.plot([x1,x2],[y1,y2],c="k") 86 | 87 | plt.axis([0,L[0],0,L[1]]) 88 | plt.show() 89 | return 90 | 91 | 92 | 93 | 94 | # vertex_file = sys.argv[1] 95 | # edge_file = sys.argv[2] 96 | # poly_file = sys.argv[3] 97 | 98 | # # lx = 9 * (2 / (3 * (3**0.5)))**0.5 99 | # # ly = 4 * (2 / (3**0.5))**0.5 100 | # L = np.loadtxt("simulation1/L") 101 | 102 | # A0 = 1. 103 | # L = np.array([lx,ly]) 104 | 105 | # # get vertices 106 | # vertices = read_vertices(vertex_file) 107 | 108 | # # get edges 109 | # edges = read_edges(edge_file) 110 | 111 | # # get polygons 112 | # poly_indices = read_poly_indices(poly_file) 113 | # polys = build_polygons(poly_indices, A0) 114 | 115 | # file = "test.jpg" 116 | # plot_network(vertices, polys, L, file) 117 | -------------------------------------------------------------------------------- /steepest_descent.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | from energy import get_energy 4 | from force import get_forces, move_vertices 5 | from transition import T1_transition 6 | 7 | 8 | def steepest_descent(vertices, edges, polys, parameters): 9 | 10 | epsilon = 10**-6 11 | delta_t = parameters['delta_t'] 12 | t = 0. 13 | 14 | count = 0 15 | forces = 10**6 16 | while np.sum(forces**2)**(0.5) > epsilon: 17 | 18 | # get energy for network 19 | energy = get_energy(vertices, polys, edges, parameters) 20 | # print energy 21 | 22 | # get forces for network 23 | forces = get_forces(vertices, polys, edges, parameters) 24 | print np.sum(forces**2)**(0.5) 25 | 26 | 27 | # move vertices 28 | vertices = move_vertices(vertices, forces, parameters) 29 | 30 | # check for T1 transitions 31 | cells, edges = T1_transition(vertices, polys, edges, parameters) 32 | 33 | # add routine to write vertices, energy, forces at every time step 34 | # can be used for plotting routines later... 35 | 36 | t += delta_t 37 | 38 | 39 | return 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | # def steepest_descent(network, vertices, cells, edges, delta_t, epsilon, folder): 74 | 75 | # # keep track of time steps 76 | # time = [] 77 | # t = 0 78 | 79 | # # keep track of energy 80 | # energy = [] 81 | 82 | # # for T1 transition 83 | # min_dist = 0.2 84 | 85 | # L = network.L 86 | 87 | # # while forces are greater than epsilon 88 | # forces = epsilon**0.5 89 | # count = 0 90 | 91 | # # generate random angle vectors 92 | # for cell in cells: 93 | # cell.theta = rand_angle() 94 | 95 | 96 | # os.mkdir("noise/hex/%s" % folder) 97 | # # f = open("energy/energy.txt", "w+") 98 | 99 | # while count < 50: 100 | # # while np.sum(forces**2)**(0.5) > epsilon: 101 | 102 | # # plot_network(vertices, cells, L, "motility/%d.jpg" % count) 103 | 104 | # # if count % 200 == 0: 105 | # # for cell in cells: 106 | # # cell.theta = rand_angle() 107 | 108 | # # # write cell vertices for MSD 109 | 110 | # os.chdir("noise/hex/%s" % folder) 111 | # np.savetxt("%d.txt" % count, vertices) 112 | # os.chdir("..") 113 | # os.chdir("..") 114 | # os.chdir("..") 115 | 116 | # # get energy for network 117 | # energy = network.get_energy(vertices, cells, edges) 118 | 119 | # # get forces for network 120 | # forces = network.get_forces(vertices, cells, edges) 121 | 122 | # # move vertices with forces 123 | # vertices = network.move_vertices(forces, vertices) 124 | 125 | 126 | # ka = network.parameters['ka'] 127 | # A0 = 1. 128 | # print t, energy / (24.*ka*(A0**2)), np.sum(forces**2)**(0.5) 129 | # # norm_energy = np.array(energy / (24.*ka*(A0**2))) 130 | # # f.write("%f\n" % norm_energy) 131 | 132 | 133 | # # new time step 134 | # t += delta_t 135 | 136 | # # # check for T1 transitions 137 | # cells, edges = T1_transition(network, vertices, cells, edges, min_dist) 138 | # count += 1 139 | 140 | # # f.close() 141 | # return vertices -------------------------------------------------------------------------------- /geometry.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | from math import sqrt, pi, sin, cos, acos, atan2, floor 4 | 5 | 6 | """ 7 | 8 | geometry.py - geometrical formulas 9 | 10 | author: Lexi Signoriello 11 | date: 1/20/16 12 | 13 | vertices - list of vertices 14 | (x0, y0), (x1, y1) ... (xN, yN) 15 | 16 | """ 17 | 18 | 19 | # Geometric center of a polygon 20 | def center(vertices): 21 | n = len(vertices) 22 | sumX = 0 23 | sumY = 0 24 | # sum the vectors 25 | for i in range(0,n): 26 | x,y = vertices[i] 27 | sumX += x 28 | sumY += y 29 | 30 | # divide by number of sides 31 | cx = sumX / n 32 | cy = sumY / n 33 | 34 | return cx,cy 35 | 36 | 37 | # Area of a polygon 38 | # http://stackoverflow.com/questions/451426/how-do-i-calculate-the-surface-area-of-a-2d-polygon 39 | def area(vertices): 40 | edges = zip(vertices, vertices[1:] + [vertices[0]]) 41 | cross_product = 0 42 | for ((x0, y0), (x1, y1)) in edges: 43 | cross_product += ((x0 * y1) - (x1 * y0)) 44 | return 0.5 * abs(cross_product) 45 | 46 | 47 | # Perimeter of a polygon 48 | def perimeter(vertices): 49 | n = len(vertices) 50 | perimeter = 0. 51 | for i in range(0,n): 52 | x0,y0 = vertices[i] 53 | if i == n - 1: 54 | x1,y1 = vertices[0] 55 | if i != n - 1: 56 | x1,y1 = vertices[i+1] 57 | dist = euclidean_distance(x0, y0, x1, y1) 58 | perimeter += dist 59 | return perimeter 60 | 61 | 62 | # Euclidean distance between (x,y) coordinates 63 | def euclidean_distance(x0, y0, x1, y1): 64 | return sqrt((x0 - x1)**2 + (y0 - y1)**2) 65 | 66 | 67 | # Difference with respect to periodic boundaries 68 | def periodic_diff(v1, v2, L): 69 | return ((v1 - v2 + L/2.) % L) - L/2. 70 | 71 | 72 | # Unit vector 73 | def unit_vector(v1, v2): 74 | vector = v1 - v2 75 | dist = euclidean_distance(v1[0], v1[1], v2[0],v2[1]) 76 | uv = vector / dist 77 | return uv 78 | 79 | 80 | # assumes 2D 81 | def magnitude(v): 82 | return (v[0]**2 + v[1]**2)**(0.5) 83 | 84 | 85 | # generate random angle theta 86 | def rand_angle(): 87 | # generate random number between -pi - pi 88 | theta = np.random.uniform(-pi,pi) 89 | # generate random number between 0 and 2pi 90 | # theta = np.random.uniform(0,2*pi) 91 | return theta 92 | 93 | # returns unit vector 94 | def angle_2_vector(theta): 95 | x = cos(theta) 96 | y = sin(theta) 97 | 98 | # transform to unit vector 99 | v1 = np.array([x,y]) 100 | v2 = np.array([0,0]) 101 | uv = unit_vector(v1,v2) 102 | 103 | return uv 104 | 105 | def vector_2_angle(x,y): 106 | return atan2(y,x) 107 | 108 | 109 | # in radians [-pi, pi] 110 | # % angle_rad = angle_rad - 2*pi*floor( (angle_rad+pi)/(2*pi) ); 111 | def angle_diff(theta1, theta2): 112 | theta = (theta1 - theta2) 113 | return (theta - 2 * pi * floor((theta + pi) / (2 * pi))) 114 | 115 | 116 | 117 | # get angle assuming vertex is p1 118 | # http://stackoverflow.com/questions/1211212/how-to-calculate-an-angle-from-three-points 119 | def get_angle_points(p1,p2,p3): 120 | radian = 0 121 | p12 = sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2) 122 | p13 = sqrt((p1[0]-p3[0])**2 + (p1[1]-p3[1])**2) 123 | p23 = sqrt((p2[0]-p3[0])**2 + (p2[1]-p3[1])**2) 124 | if p12 != 0 and p13 != 0: 125 | try: 126 | radian = acos( (p12**2 + p13**2 - p23**2)/(2*p12*p13) ) 127 | except ValueError: 128 | print "Domain Error" 129 | pass 130 | return radian 131 | 132 | def get_angle_vectors(v1, v2): 133 | theta = np.dot(v1,v2) 134 | theta = theta / (magnitude(v1) * magnitude(v2)) 135 | return acos(theta) 136 | 137 | 138 | def radian_2_degrees(theta): 139 | return theta * (360. / (2 * pi)) 140 | 141 | # check if counter-clockwise 142 | # change polygon data structure? 143 | def check_counter_clockwise(polygon): 144 | sumEdges = 0 145 | for i,(x,y) in enumerate(polygon): 146 | if i == 0: 147 | x0 = x 148 | y0 = y 149 | if i + 1 != len(polygon): 150 | x2,y2 = polygon[i+1] 151 | if i+1 == len(polygon): 152 | x2 = x0 153 | y2 = y0 154 | 155 | sumEdges += float(x2 - x) / float(y2 + y) 156 | 157 | if sumEdges > 0: 158 | return True 159 | else: 160 | return False 161 | -------------------------------------------------------------------------------- /force.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | from geometry import * 4 | from math import pi 5 | import matplotlib.pyplot as plt 6 | 7 | """ 8 | 9 | force.py - computes forces in the current configuration 10 | of the vertex model 11 | 12 | 13 | author: Lexi Signoriello 14 | date: 1/20/16 15 | 16 | """ 17 | 18 | 19 | def get_forces(vertices, polys, edges, parameters): 20 | # get necessary parameters 21 | lx = parameters['lx'] 22 | ly = parameters['ly'] 23 | L = np.array([lx,ly]) 24 | ka = parameters['ka'] 25 | Lambda = parameters['Lambda'] 26 | gamma = parameters['gamma'] 27 | eta = parameters['eta'] 28 | xi = parameters['xi'] 29 | 30 | f1 = F_elasticity(vertices, polys, ka, L) 31 | f2 = F_adhesion(vertices, edges, Lambda, L) 32 | f3 = F_contraction(vertices, polys, gamma, L) 33 | f4 = F_motility(vertices, polys, eta, xi) 34 | 35 | return -(f1 + f2 + f3 + f4) 36 | 37 | 38 | def move_vertices(vertices, forces, parameters): 39 | delta_t = parameters['delta_t'] 40 | lx = parameters['lx'] 41 | ly = parameters['ly'] 42 | 43 | vertices = vertices + delta_t * forces 44 | n_vertices = vertices.shape[0] 45 | 46 | # wrap around periodic boundaries 47 | for i in range(0, n_vertices): 48 | x = vertices[i,0] 49 | y = vertices[i,1] 50 | 51 | if x < 0: 52 | # wrap around to right 53 | vertices[i,0] = x + lx 54 | # print x, vertices[i,0] 55 | 56 | if x > lx: 57 | # wrap around to left 58 | vertices[i,0] = x - lx 59 | # print x, vertices[i,0] 60 | 61 | if y < 0: 62 | # wrap around to top 63 | vertices[i,1] = y + ly 64 | # print y, vertices[i,1] 65 | 66 | if y > ly: 67 | # wrap around to bottom 68 | vertices[i,1] = y - ly 69 | # print y, vertices[i,1] 70 | 71 | 72 | return vertices 73 | 74 | 75 | 76 | def get_clockwise(index, indices, vertices, L): 77 | 78 | # get position of vertex in list 79 | pos = [i for i,x in enumerate(indices) if x == index] 80 | pos = pos[0] 81 | 82 | # clockwise is position to right 83 | # wrap around to 0 if at end of the list 84 | if pos == len(indices) - 1: 85 | pos = 0 86 | else: 87 | pos += 1 88 | 89 | # compute vertex wrt periodic boundaries 90 | v0 = vertices[index] 91 | v = vertices[indices[pos]] 92 | vc = v0 + periodic_diff(v, v0, L) 93 | 94 | return vc 95 | 96 | 97 | 98 | def get_counter_clockwise(index, indices, vertices, L): 99 | 100 | # get position of vertex in list 101 | pos = [i for i,x in enumerate(indices) if x == index] 102 | pos = pos[0] 103 | 104 | # clockwise is position to left 105 | # wrap around to end of list if first value 106 | if pos == 0: 107 | pos = len(indices) - 1 108 | else: 109 | pos -= 1 110 | 111 | v0 = vertices[index] 112 | v = vertices[indices[pos]] 113 | vcc = v0 + periodic_diff(v, v0, L) 114 | 115 | return vcc 116 | 117 | 118 | # Force on vertex due to elasticity 119 | def F_elasticity(vertices, polys, ka, L): 120 | n_vertices = len(vertices) 121 | 122 | # evert vertex has an associated force 123 | forces = np.zeros((n_vertices, 2)) 124 | 125 | # iterate over vertices and get force 126 | for i,vertex in enumerate(vertices): 127 | 128 | # find polys with this vertex 129 | for poly in polys: 130 | 131 | # if this vertex is in current poly 132 | # compute force contributed from this poly 133 | if i in poly.indices: 134 | 135 | # get clockwise vector 136 | vc = get_clockwise(i, poly.indices, vertices, L) 137 | 138 | # get counter-clockwise vector 139 | vcc = get_counter_clockwise(i, poly.indices, vertices, L) 140 | 141 | # get the difference vector 142 | diff = vc - vcc 143 | 144 | # compute perpendicular vector 145 | # assure correct direction (pointing towards vertex) 146 | perp_matrix = np.zeros((2,2)) 147 | perp_matrix[0,1] = 1. 148 | perp_matrix[1,0] = -1. 149 | 150 | f = -0.5 * np.dot(perp_matrix, diff) 151 | 152 | # force contributed from this poly stored in f 153 | coeff = ka * (poly.A0 - poly.get_area(vertices, L)) 154 | 155 | forces[i,:] += coeff * f 156 | 157 | 158 | return forces 159 | 160 | 161 | 162 | def F_contraction(vertices, polys, gamma, L): 163 | 164 | # every vertex has an associated force 165 | n_vertices = len(vertices) 166 | forces = np.zeros((n_vertices, 2)) 167 | 168 | for i,vertex in enumerate(vertices): 169 | 170 | # find polys with this vertex 171 | for poly in polys: 172 | 173 | if i in poly.indices: 174 | 175 | # get clockwise vector 176 | vc = get_clockwise(i, poly.indices, vertices, L) 177 | uvc = unit_vector(vertex, vc) 178 | 179 | # get counter-clockwise vector 180 | vcc = get_counter_clockwise(i, poly.indices, vertices, L) 181 | uvcc = unit_vector(vcc, vertex) 182 | 183 | # get perimeter for this poly 184 | p = poly.get_perim(vertices, L) 185 | 186 | forces[i,:] += (gamma * p) * (uvc - uvcc) 187 | 188 | return forces 189 | 190 | def F_adhesion(vertices, edges, Lambda, L): 191 | 192 | # every vertex has an associated force 193 | n_vertices = len(vertices) 194 | forces = np.zeros((n_vertices, 2)) 195 | 196 | for edge in edges: 197 | i1 = edge[0] 198 | i2 = edge[1] 199 | v1 = vertices[i1] 200 | vertex2 = vertices[i2] 201 | v2 = v1 + periodic_diff(vertex2, v1, L) 202 | uv = unit_vector(v1, v2) 203 | forces[i1,:] += Lambda * uv 204 | 205 | return forces 206 | 207 | # Force to move vertices of polys in particular direction 208 | def F_motility(vertices, polys, eta, xi): 209 | 210 | 211 | n_vertices = len(vertices) 212 | forces = np.zeros((n_vertices, 2)) 213 | 214 | # find neighbors for every poly 215 | # defined as any two polys that share a vertex 216 | avg_angles = np.zeros((len(polys), 2)) 217 | 218 | neighbor_count = np.ones(len(polys)) 219 | 220 | for i,poly in enumerate(polys): 221 | avg_angles[i, :] += angle_2_vector(poly.theta) 222 | for j,poly2 in enumerate(polys): 223 | if i != j: 224 | a = poly.indices 225 | b = poly2.indices 226 | if any(k in a for k in b) == True: 227 | avg_angles[i, :] += angle_2_vector(poly2.theta) 228 | neighbor_count[i] += 1 229 | 230 | for i,poly in enumerate(polys): 231 | 232 | # noise variable 233 | nx = np.random.uniform(-pi,pi) 234 | ny = np.random.uniform(-pi,pi) 235 | n = np.array([nx,ny]) 236 | 237 | # average all of the unit vectors for angles 238 | avg = (avg_angles[i,:] / neighbor_count[i]) 239 | 240 | # add this force direction for every vertex in current poly 241 | for index in poly.indices: 242 | forces[index, :] += xi * (avg + (eta * n)) 243 | 244 | # theta = avg + eta * noise 245 | poly.theta = vector_2_angle(avg[0] + eta * n[0], avg[1] + eta * n[1]) 246 | 247 | return forces 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | # visualization for average vector 262 | 263 | # plt.plot([0, avg_angles[i,0]],[0,avg_angles[i,1]], color="k") 264 | 265 | # v = angle_2_vector(poly2.theta) 266 | # plt.plot([0,v[0]],[0,v[1]],color="k") 267 | # print avg_angles[i,:] / neighbor_count[i] 268 | # print magnitude(avg_angles[i,:] / neighbor_count 269 | 270 | # v_avg = avg_angles[i,:] / neighbor_count[i] 271 | # print magnitude(v_avg) 272 | # print vector_2_angle(v_avg[0], v_avg[1]) 273 | # plt.plot([0,v_avg[0]],[0,v_avg[1]],color="m") 274 | # v_avg_unit = unit_vector(v_avg, np.array([0,0])) 275 | # plt.plot([0,v_avg_unit[0]],[0,v_avg_unit[1]],color="g") 276 | # plt.plot([0,v_avg[0]],[0,v_avg[1]],color="m") 277 | # plt.show() 278 | # eetat() 279 | 280 | 281 | -------------------------------------------------------------------------------- /transition.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import numpy as np 3 | from Polygon import Polygon 4 | from geometry import periodic_diff 5 | from energy import * 6 | import copy 7 | 8 | 9 | """ 10 | 11 | transition.py - implements T1 transition for short bond lengths 12 | 13 | author: Lexi Signoriello 14 | date: 2/12/16 15 | 16 | 4 polys involved in transition are 1-4 counter-clockwise order 17 | 18 | Cells defined such that: 19 | Cell 0: i4, i1, i2, i5 20 | Cell 1: i3, i1, i4 21 | Cell 2: i6, i2, i1, i3 22 | Cell 3: i5, i2, i6 23 | 24 | 25 | Edges defined such that: 26 | Edge 0: i1 - i2 27 | Edge 1: i1 - i3 28 | Edge 2: i1 - i4 29 | Edge 3: i2 - i1 30 | Edge 4: i2 - i5 31 | Edge 5: i2 - i6 32 | Edge 6: i3 - i1 # reverse edges 33 | Edge 7: i4 - i1 34 | Edge 8: i5 - i2 35 | Edge 9: i6 - i2 36 | 37 | 38 | """ 39 | 40 | 41 | 42 | def get_6_indices(polys, i1, i2, poly_ids): 43 | polys_copy = [] 44 | for i in poly_ids: 45 | poly = copy.deepcopy(polys[i]) 46 | polys_copy.append(poly) 47 | 48 | # define polys 49 | poly_0 = polys_copy[0] 50 | poly_1 = polys_copy[1] 51 | poly_2 = polys_copy[2] 52 | poly_3 = polys_copy[3] 53 | 54 | # Find indices wrt Cell 1 55 | pos = int(np.where(poly_1.indices == i1)[0]) 56 | # i3: poly 1 before i1 57 | if pos == 0: 58 | i_left = len(poly_1.indices) - 1 59 | else: 60 | i_left = pos - 1 61 | i3 = poly_1.indices[i_left] 62 | # i4: poly 1 after i1 63 | if pos == len(poly_1.indices) - 1: 64 | i_right = 0 65 | else: 66 | i_right = pos + 1 67 | i4 = poly_1.indices[i_right] 68 | # i5: poly 3 before i2 69 | pos = int(np.where(poly_3.indices == i2)[0]) 70 | if pos == 0: 71 | i_left = len(poly_3.indices) - 1 72 | else: 73 | i_left = pos - 1 74 | i5 = poly_3.indices[i_left] 75 | # i6: poly 3 after i2 76 | if pos == len(poly_3.indices) - 1: 77 | i_right = 0 78 | else: 79 | i_right = pos + 1 80 | i6 = poly_3.indices[i_right] 81 | 82 | indices = [i1,i2,i3,i4,i5,i6] 83 | 84 | return indices 85 | 86 | 87 | 88 | # get polys and edges associated with short bond length 89 | def T1_0(polys, i1, i2, poly_ids, indices): 90 | polys_0 = [] 91 | 92 | for i in poly_ids: 93 | # copy poly so it can be manipulated without changing 94 | # current configuation 95 | poly = copy.deepcopy(polys[i]) 96 | polys_0.append(poly) 97 | 98 | # define polys 99 | poly_0 = polys_0[0] 100 | poly_1 = polys_0[1] 101 | poly_2 = polys_0[2] 102 | poly_3 = polys_0[3] 103 | 104 | i1 = indices[0] 105 | i2 = indices[1] 106 | i3 = indices[2] 107 | i4 = indices[3] 108 | i5 = indices[4] 109 | i6 = indices[5] 110 | 111 | edges_0 = np.zeros((10,2)) 112 | # Edge 0: i1 - i2 113 | edges_0[0,0] = i1 114 | edges_0[0,1] = i2 115 | 116 | # Edge 1: i1 - i3 117 | edges_0[1,0] = i1 118 | edges_0[1,1] = i3 119 | 120 | # Edge 2: i1 - i4 121 | edges_0[2,0] = i1 122 | edges_0[2,1] = i4 123 | 124 | # Edge 3: i2 - i1 125 | edges_0[3,0] = i2 126 | edges_0[3,1] = i1 127 | 128 | # Edge 4: i2 - i5 129 | edges_0[4,0] = i2 130 | edges_0[4,1] = i5 131 | 132 | # Edge 5: i2 - i6 133 | edges_0[5,0] = i2 134 | edges_0[5,1] = i6 135 | 136 | # Edge 6: i3 - i1 # reverse edges 137 | edges_0[6,0] = i3 138 | edges_0[6,1] = i1 139 | 140 | # Edge 7: i4 - i1 141 | edges_0[7,0] = i4 142 | edges_0[7,1] = i1 143 | 144 | # Edge 8: i5 - i2 145 | edges_0[8,0] = i5 146 | edges_0[8,1] = i2 147 | 148 | # Edge 9: i6 - i2 149 | edges_0[9,0] = i6 150 | edges_0[9,1] = i2 151 | 152 | return polys_0, edges_0 153 | 154 | # get polys and edges associated with 155 | def T1_left(polys, i1, i2, poly_ids, indices): 156 | 157 | # Cells 158 | polys_l = [] 159 | # ids in correct order already 160 | for i in poly_ids: 161 | poly = copy.deepcopy(polys[i]) 162 | polys_l.append(poly) 163 | 164 | # define polys 165 | poly_0 = polys_l[0] 166 | poly_1 = polys_l[1] 167 | poly_2 = polys_l[2] 168 | poly_3 = polys_l[3] 169 | 170 | # define indices 171 | i1 = indices[0] 172 | i2 = indices[1] 173 | i3 = indices[2] 174 | i4 = indices[3] 175 | i5 = indices[4] 176 | i6 = indices[5] 177 | 178 | # Cell 0: remove i2 179 | pos = int(np.where(poly_0.indices == i2)[0]) 180 | indices = np.delete(poly_0.indices, pos) 181 | polys_l[0].indices = indices 182 | 183 | 184 | # Cell 1: insert i2 before i1 185 | pos = int(np.where(poly_1.indices == i1)[0]) 186 | left_indices = poly_1.indices[:pos] 187 | right_indices = poly_1.indices[pos:] 188 | indices = np.concatenate((left_indices, [i2], right_indices)) 189 | polys_l[1].indices = indices 190 | 191 | # Cell 2: remove i1 192 | pos = int(np.where(poly_2.indices == i1)[0]) 193 | indices = np.delete(poly_2.indices, pos) 194 | polys_l[2].indices = indices 195 | 196 | # Cell 3: insert i1 before i2 197 | pos = int(np.where(poly_3.indices == i2)[0]) 198 | left_indices = poly_3.indices[:pos] 199 | right_indices = poly_3.indices[pos:] 200 | indices = np.concatenate((left_indices, [i1], right_indices)) 201 | polys_l[3].indices = indices 202 | 203 | 204 | # Edges 205 | edges_l = np.zeros((10,2)) 206 | # Edge 0: i1 - i2 207 | edges_l[0,0] = i1 208 | edges_l[0,1] = i2 209 | 210 | # Edge 1: i2 - i3 211 | edges_l[1,0] = i2 212 | edges_l[1,1] = i3 213 | 214 | # Edge 2: i1 - i4 215 | edges_l[2,0] = i1 216 | edges_l[2,1] = i4 217 | 218 | # Edge 3: i2 - i1 219 | edges_l[3,0] = i2 220 | edges_l[3,1] = i1 221 | 222 | # Edge 4: i1 - i5 223 | edges_l[4,0] = i1 224 | edges_l[4,1] = i5 225 | 226 | # Edge 5: i2 - i6 227 | edges_l[5,0] = i2 228 | edges_l[5,1] = i6 229 | 230 | # Edge 6: i3 - i2 # reverse edges 231 | edges_l[6,0] = i3 232 | edges_l[6,1] = i2 233 | 234 | # Edge 7: i4 - i1 235 | edges_l[7,0] = i4 236 | edges_l[7,1] = i1 237 | 238 | # Edge 8: i5 - i1 239 | edges_l[8,0] = i5 240 | edges_l[8,1] = i1 241 | 242 | # Edge 9: i6 - i2 243 | edges_l[9,0] = i6 244 | edges_l[9,1] = i2 245 | 246 | 247 | return polys_l, edges_l 248 | 249 | def T1_right(polys, i1, i2, poly_ids, indices): 250 | 251 | polys_r = [] 252 | for i in poly_ids: 253 | poly = copy.deepcopy(polys[i]) 254 | polys_r.append(poly) 255 | 256 | # define polys 257 | poly_0 = polys_r[0] 258 | poly_1 = polys_r[1] 259 | poly_2 = polys_r[2] 260 | poly_3 = polys_r[3] 261 | 262 | # define indices 263 | i1 = indices[0] 264 | i2 = indices[1] 265 | i3 = indices[2] 266 | i4 = indices[3] 267 | i5 = indices[4] 268 | i6 = indices[5] 269 | 270 | # Cell 0: remove i1 271 | pos = int(np.where(poly_0.indices == i1)[0]) 272 | indices = np.delete(poly_0.indices, pos) 273 | polys_r[0].indices = indices 274 | 275 | 276 | # Cell 1: insert i2 after i1 277 | pos = int(np.where(poly_1.indices == i1)[0]) 278 | left_indices = poly_1.indices[:pos+1] 279 | right_indices = poly_1.indices[pos+1:] 280 | indices = np.concatenate((left_indices, [i2], right_indices)) 281 | polys_r[1].indices = indices 282 | 283 | # Cell 2: remove i2 284 | pos = int(np.where(poly_2.indices == i2)[0]) 285 | indices = np.delete(poly_2.indices, pos) 286 | polys_r[2].indices = indices 287 | 288 | # Cell 3: insert i1 after i2 289 | pos = int(np.where(poly_3.indices == i2)[0]) 290 | left_indices = poly_3.indices[:pos+1] 291 | right_indices = poly_3.indices[pos+1:] 292 | indices = np.concatenate((left_indices, [i1], right_indices)) 293 | polys_r[3].indices = indices 294 | 295 | # # Edges 296 | edges_r = np.zeros((10,2)) 297 | # Edge 0: i1 - i2 298 | edges_r[0,0] = i1 299 | edges_r[0,1] = i2 300 | 301 | # Edge 1: i1 - i3 302 | edges_r[1,0] = i1 303 | edges_r[1,1] = i3 304 | 305 | # Edge 2: i1 - i6 306 | edges_r[2,0] = i1 307 | edges_r[2,1] = i6 308 | 309 | # Edge 3: i2 - i1 310 | edges_r[3,0] = i2 311 | edges_r[3,1] = i1 312 | 313 | # Edge 4: i2 - i5 314 | edges_r[4,0] = i2 315 | edges_r[4,1] = i5 316 | 317 | # Edge 5: i2 - i4 318 | edges_r[5,0] = i2 319 | edges_r[5,1] = i4 320 | 321 | # Edge 6: i3 - i1 # reverse edges 322 | edges_r[6,0] = i3 323 | edges_r[6,1] = i1 324 | 325 | # Edge 7: i4 - i2 326 | edges_r[7,0] = i4 327 | edges_r[7,1] = i2 328 | 329 | # Edge 8: i5 - i2 330 | edges_r[8,0] = i5 331 | edges_r[8,1] = i2 332 | 333 | # Edge 9: i6 - i1 334 | edges_r[9,0] = i6 335 | edges_r[9,1] = i1 336 | 337 | 338 | return polys_r, edges_r 339 | 340 | 341 | # # find 4 polys involved with 2 vertices 342 | # Labeled polys 0-3 in counter-clockwise order 343 | # Cell 0 and Cell 3 are neighbors 344 | def get_4_polys(polys, i1, i2): 345 | 346 | poly_ids = np.zeros(4).astype(int) 347 | poly_ids.fill(-1) # catch errors later 348 | 349 | for poly in polys: 350 | # Cell 0 or Cell 2 351 | # Current neighboring polys 352 | # Cell 1 should have i1 before i2 in counter-clockwise orde 353 | if i1 in poly.indices and i2 in poly.indices: 354 | pos1 = np.where(poly.indices == i1) 355 | pos2 = np.where(poly.indices == i2) 356 | 357 | if pos1 == len(poly.indices) - 1: 358 | pos1 = -1 359 | if pos2 == len(poly.indices) - 1: 360 | pos2 = -1 361 | 362 | # if Cell 1: i1 is before i2 363 | if pos1 < pos2: 364 | poly_ids[0] = poly.id 365 | # if Cell 3: i2 is before i1 366 | if pos2 < pos1: 367 | poly_ids[2] = poly.id 368 | 369 | # Cell 3 370 | if i2 in poly.indices and i1 not in poly.indices: 371 | poly_ids[3] = poly.id 372 | # Cell 1 373 | if i1 in poly.indices and i2 not in poly.indices: 374 | poly_ids[1] = poly.id 375 | 376 | return poly_ids 377 | 378 | 379 | 380 | def T1_transition(vertices, polys, edges, parameters): 381 | 382 | 383 | lx = parameters['lx'] 384 | ly = parameters['ly'] 385 | L = np.array([lx,ly]) 386 | lmin = parameters['lmin'] 387 | 388 | reverse = [] 389 | 390 | for edge in edges: 391 | i1 = edge[0] 392 | i2 = edge[1] 393 | 394 | v1 = vertices[i1] 395 | vertex2 = vertices[i2] 396 | v2 = v1 + periodic_diff(vertex2, v1, L) 397 | 398 | dist = euclidean_distance(v1[0], v1[1], v2[0], v2[1]) 399 | 400 | if dist < lmin and (i1,i2) not in reverse: 401 | print "T1", i1, i2, dist 402 | poly_ids = get_4_polys(polys, i1, i2) 403 | if -1 in poly_ids: 404 | pass 405 | else: 406 | # find minimum configuration 407 | reverse.append((i2,i1)) 408 | 409 | # 6 indices for vertices involved in transition 410 | indices = get_6_indices(polys, i1, i2, poly_ids) 411 | 412 | # original configuration 413 | polys_0, edges_0 = T1_0(polys, i1, i2, poly_ids, indices) 414 | E0 = get_energy(vertices, polys_0, edges_0, parameters) 415 | 416 | # left T1 transition 417 | polys_l, edges_l = T1_left(polys, i1, i2, poly_ids, indices) 418 | E_left = get_energy(vertices, polys_l, edges_l, parameters) 419 | 420 | # # right T1 transition 421 | polys_r, edges_r = T1_right(polys, i1, i2, poly_ids, indices) 422 | E_right = get_energy(vertices, polys_r, edges_r, parameters) 423 | 424 | 425 | # get minimum 426 | min_energy = np.min((E0, E_left, E_right)) 427 | min_i = np.argmin((E0, E_left, E_right)) 428 | print min_i 429 | 430 | # # do nothing - same configuration 431 | if min_i == 0: 432 | pass 433 | 434 | if min_i == 1: 435 | set_T1_left(polys, polys_l, poly_ids, edges, indices) 436 | 437 | if min_i == 2: 438 | set_T1_right(polys, polys_r, poly_ids, edges, indices) 439 | 440 | return polys, edges 441 | 442 | 443 | def set_T1_left(polys, polys_l, poly_ids, edges, indices): 444 | # set new poly indices 445 | for i,poly in enumerate(polys_l): 446 | polys[poly_ids[i]].indices = poly.indices 447 | 448 | # set new edges 449 | i1 = indices[0] 450 | i2 = indices[1] 451 | i3 = indices[2] 452 | i5 = indices[4] 453 | for i,edge in enumerate(edges): 454 | 455 | # i1 - i3 becomes i2 - i3 456 | if edge[0] == i1 and edge[1] == i3: 457 | edges[i][0] = i2 458 | 459 | # i2 - i5 becomes i1 - i5 460 | if edge[0] == i2 and edge[1] == i5: 461 | edges[i][0] = i1 462 | 463 | # i3 - i1 becomes i3 - i2 464 | if edge[0] == i3 and edge[1] == i1: 465 | edges[i][1] = i2 466 | 467 | # i5 - i2 becomes i5 - i1 468 | if edge[0] == i5 and edge[1] == i2: 469 | edges[i][1] = i1 470 | 471 | return 472 | 473 | 474 | def set_T1_right(polys, polys_r, poly_ids, edges, indices): 475 | 476 | # set new poly indices 477 | for i,poly in enumerate(polys_r): 478 | polys[poly_ids[i]].indices = poly.indices 479 | 480 | 481 | # set new edges 482 | i1 = indices[0] 483 | i2 = indices[1] 484 | i4 = indices[3] 485 | i6 = indices[5] 486 | 487 | for i,edge in enumerate(edges): 488 | 489 | # i1 - i4 becomes i2 - i4 490 | if edge[0] == i1 and edge[1] == i4: 491 | edges[i][0] = i2 492 | 493 | # i2 - i6 becomes i1 - i6 494 | if edge[0] == i2 and edge[1] == i6: 495 | edges[i][0] = i1 496 | 497 | # i4 - i1 becomes i4 - i2 498 | if edge[0] == i4 and edge[1] == i1: 499 | edges[i][1] = i2 500 | 501 | # i6 - i2 becomes i6 - i1 502 | if edge[0] == i6 and edge[1] == i2: 503 | edges[i][1] = i1 504 | 505 | return 506 | 507 | 508 | 509 | 510 | def T2_transition(network, vertices, polys, edges, min_area): 511 | pass 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | --------------------------------------------------------------------------------