View difference between Paste ID: un6aaK4w and hN73caUt
SHOW: | | - or go back to the newest paste.
1
import numpy as np 
2
import matplotlib.pyplot as plt 
3
import matplotlib.animation as anim 
4
import colorsys as colsys
5
6
np.set_printoptions(precision=3)
7
plt.rcParams['axes.facecolor'] = '#000000'
8
9
def block_matrix(p0,p_std,pos=False):
10
	"""
11
	generates block matrix (used for assigning 
12
	parameters to groups of particles)
13
	"""
14
	m = np.random.normal(p0,p_std,(groups,groups))
15
	m = m.repeat(particles_per_group,axis=1).repeat(particles_per_group,axis=0)
16
17
	if pos:
18
		return np.abs(m)
19
	else:
20
		return m
21
22
def bound(x,y):
23
	"""
24
	periodic boundaries
25
	"""
26
	x[x>=x_width*lim] -= 2*x_width*lim
27
	x[x<-x_width*lim] += 2*x_width*lim
28
	y[y>=lim] -= 2*lim
29
	y[y<-lim] += 2*lim
30
31
	return x,y
32
33
34
def attract(x,y,vx,vy):
35
	"""
36
	calculate forces between particles 
37
	"""
38
39
	dx = x.reshape(total_particles,1) - x
40
	dy = y.reshape(total_particles,1) - y
41
	dx,dy = bound(dx,dy)
42
43
	r = np.sqrt(dx*dx + dy*dy)
44
	f = np.zeros((total_particles,total_particles))
45
	i0 = np.where(r < separation)
46
	i1 = np.where((r > separation) & (r < (separation+force_radius/2)))
47
	i2 = np.where((r > separation+force_radius/2) & (r < separation+force_radius)) 
48
49
	f[i0] = rep_force[i0]*(separation[i0]-r[i0])
50
	f[i1] = forces[i1] * (r[i1]-separation[i1]) 
51
	f[i2] = -forces[i2] * (r[i2]-(separation[i2]+force_radius[i2]))
52
	
53
	np.fill_diagonal(f,0) # no self-force
54
55
	# increase force at long range
56
	f *= close_range_factor + (dist_range_factor-close_range_factor) * (r/lim)
57
58
	a = np.arctan2(dy,dx)
59
60
	# add contributions from all particles
61
	fx = np.sum(f * np.cos(a), axis=0)
62
	fy = np.sum(f * np.sin(a), axis=0)
63
64
	return fx,fy
65
66
def friction(vx,vy):
67
	"""
68
	friction and thermal forces
69
	"""
70
71
	v = np.sqrt(vx*vx + vy*vy)
72
	a = np.arctan2(vy,vx)
73
	fx = friction_coefficient * v * np.cos(a) + np.random.normal(0,temperature,total_particles)
74
	fy = friction_coefficient * v * np.sin(a) + np.random.normal(0,temperature,total_particles)
75
76
	return fx,fy
77
78
def init():
79
	particles.set_offsets([])
80
	particles_area.set_offsets([])
81
	return particles, particles_area,
82
83
def animate(i):
84
85
	global x,y,vx,vy
86
	
87
	x,y = bound(x,y)
88
	fx,fy = attract(x,y,vx,vy)
89
	ffx,ffy = friction(vx,vy)
90
91
	x += vx * dt
92
	y += vy * dt 
93
	vx += (-fx-ffx) * dt
94
	vy += (-fy-ffy) * dt 
95
96
	xy = np.vstack((x,y)).T
97
98
	particles.set_offsets(xy)
99
	particles_area.set_offsets(xy)
100
101
	return particles, particles_area,
102
103
# global settings
104
dt = 0.01
105
lim = 60
106
x_width = 1
107
groups = 3
108
particles_per_group = 200
109
total_particles = groups * particles_per_group
110
111
# parameters
112
repelling_force = 60 # force active if r < separation radius
113
temperature = 5 # controls random fluctuations in particle velocities
114
friction_coefficient = 90 
115
separation_radius = 10 #mean separation radius
116
interaction_radius = 25 # mean interaction radius
117
force_strength = 10 # inter-particle force strength
118
close_range_factor = 1 # force strength multiplier at r=0
119
dist_range_factor = 5 # force strength multiplier at r=lim
120
deviation = 0.1 # spread in group parameters
121
seed_range = 0.9 # initial position spread
122
123
124
# parameter matrices 
125
force_radius = block_matrix(interaction_radius,interaction_radius*deviation,True)
126
separation = block_matrix(separation_radius,separation_radius*deviation,True)
127
forces = block_matrix(0,force_strength)
128
rep_force = block_matrix(repelling_force,repelling_force*deviation,True)
129
130
131
132
# group properties 
133
cs,ss,sa = [],[],[]
134
ppg = particles_per_group
135
for g in range(groups):
136
137
	fr = np.abs(force_radius[0,g*ppg])/np.max(force_radius)
138
	fs = np.abs(forces[0,g*ppg])/np.max(np.abs(forces))
139
	sep = np.abs(separation[0,g*ppg])/np.max(separation)
140
	rep = np.abs(rep_force[0,g*ppg])/np.max(rep_force)
141
	fd = np.abs(forces[g*ppg,g*ppg])/np.max(np.abs(forces))
142
143
	size = 20 + 50 * fs 
144
145
	fc = (forces[0,g*ppg]-np.min(forces))/(np.max(forces)-np.min(forces))
146
147
	color = colsys.hsv_to_rgb(fc,0.3+0.7*fd,0.5*(1+fs))
148
	for p in range(particles_per_group):
149
		cs.append(color)
150
		ss.append(size)
151
		sa.append(2*fr*15*size)
152
153
# initialization
154
x = np.random.uniform(-lim*seed_range,lim*seed_range,total_particles)
155
y = np.random.uniform(-lim*seed_range,lim*seed_range,total_particles)
156
vx = np.random.uniform(-1,1,total_particles)
157
vy = np.random.uniform(-1,1,total_particles)
158
159
# parameter settings
160
# titlestring = ''
161
# titlestring += f'dt {dt:.2f} | '
162
# titlestring += f'Rep. F {repelling_force:.1f} | '
163
# titlestring += f'Fric. {friction_coefficient:.1f} | '
164
# titlestring += f'Sep. {separation_radius:.1f} | '
165
# titlestring += f'Int. R {interaction_radius:.1f} | '
166
# titlestring += f'F Str. {force_strength:.1f} | '
167
# titlestring += f'Temp. {temperature:.1f} | '
168
169
170
171
fig,ax = plt.subplots(figsize=(14,7),facecolor='k')
172
# plt.title(titlestring)
173
ax.set_aspect('equal')
174
plt.xlim(-x_width*lim,x_width*lim)
175
plt.ylim(-lim,lim)
176
particles = plt.scatter(x, y, marker='.', c=cs, s=ss)
177
particles_area = plt.scatter(x, y, marker='.', c=cs, s=sa, alpha=0.1, edgecolors=None, linewidths=0)
178
179
animation = anim.FuncAnimation(fig, animate, init_func=init, interval=0, blit=True)
180
181
plt.axis('off')
182
plt.tight_layout(pad=0.3)
183
plt.subplots_adjust(hspace=0.01, wspace=0.01)
184
# figManager = plt.get_current_fig_manager()
185
# figManager.window.showMaximized()
186
plt.show()