def g_force(self, o): r = self.pos - o.pos if mag2(r) > 0: return -norm(self.pos - o.pos) * g * self.mass * o.mass / mag2(self.pos - o.pos) return vector()
def kinetic_energy(self): return 0.5 * self.mass * mag2(self.v())
def Simulation(): config.Atoms = [] # spheres p = [] # momentums (vectors) apos = [] # positions (vectors) ampl = 0 #амплитуда движения period = 5 k = 1.4E-23 # Boltzmann constant R = 8.3 dt = 1E-5 time = 0 def checkCollisions(Natoms, Ratom): hitlist = [] r2 = 2 * Ratom for i in range(Natoms): for j in range(i): dr = apos[i] - apos[j] if dr.mag < r2: hitlist.append([i, j]) return hitlist def speed(time, piston_mode, period, ampl, temp): if (piston_mode == 0): return 0 if (piston_mode == 1): return ampl / 10 * 3 * sin(time / period * 2 * pi) * sqrt( 3 * config.mass * k * temp) / (5 * config.mass) / period * 100 if (piston_mode == 2): if (time % period < period // 2): return 1.5 * ampl / 10 * sqrt(3 * config.mass * k * temp) / ( 5 * config.mass) / period * 100 else: return -1.5 * ampl / 10 * sqrt(3 * config.mass * k * temp) / ( 5 * config.mass) / period * 100 if (piston_mode == 3): if (time % period < period // 5): return 5 * ampl / 10 * sqrt(3 * config.mass * k * temp) / ( 5 * config.mass) / period * 100 else: return -5 / 4 * ampl / 10 * sqrt( 3 * config.mass * k * temp) / (5 * config.mass) / period * 100 if (piston_mode == 4): if (time % period < 4 * period // 5): return 5 / 4 * ampl / 10 * sqrt(3 * config.mass * k * temp) / ( 5 * config.mass) / period * 100 else: return -5 * ampl / 10 * sqrt(3 * config.mass * k * temp) / ( 5 * config.mass) / period * 100 width, height = config.w.win.GetSize() offset = config.w.dheight deltav = 100 # histogram bar width disp = display( window=config.w, x=offset, y=offset, forward=vector(0, -0.05, -1), range=1, # userspin = False, width=width / 3, height=height) g1 = gdisplay(window=config.w, x=width / 3 + 2 * offset, y=2 * offset, background=color.white, xtitle='t', ytitle='v', foreground=color.black, width=width / 3, height=height / 2 - 2 * offset) g2 = gdisplay(window=config.w, x=width / 3 + 2 * offset, y=height / 2 + offset, background=color.white, foreground=color.black, width=width / 3, height=height / 2 - 2 * offset) # adding empty dots to draw axis graph_average_speed = gcurve(gdisplay=g1, color=color.white) graph_average_speed.plot(pos=(3000, 1500)) graph_temp = gcurve(gdisplay=g2, color=color.white) graph_temp.plot(pos=(3000, config.Natoms * deltav / 1000)) speed_text = wx.StaticText(config.w.panel, pos=(width / 3 + 2 * offset, offset), label="Средняя скорость") graph_text = wx.StaticText(config.w.panel, pos=(width / 3 + 2 * offset, height / 2), label="") L = 1 # container is a cube L on a side d = L / 2 + config.Ratom # half of cylinder's height topborder = d gray = color.gray(0.7) # color of edges of container # cylinder drawing cylindertop = cylinder(pos=(0, d - 0.001, 0), axis=(0, 0.005, 0), radius=d) ringtop = ring(pos=(0, d, 0), axis=(0, -d, 0), radius=d, thickness=0.005) ringbottom = ring(pos=(0, -d, 0), axis=(0, -d, 0), radius=d, thickness=0.005) body = cylinder(pos=(0, -d, 0), axis=(0, 2 * d, 0), radius=d, opacity=0.2) # body_tmp = cylinder(pos = (0, d, 0), axis = (0, 2 * d, 0), radius = d + 0.1, color = (0, 0, 0)) # ceil = box(pos = (0, d, 0), length = 5, height = 0.005, width = 5, color = (0, 0, 0)) # floor = box(pos = (0, -d, 0), length = 100, height = 0.005, width = 100, color = (0, 0, 0)) # left = box(pos = (d + 0.005, 0, 0), axis = (0, 1, 0), length = 100, height = 0.005, width = 100, color = (0, 0, 0)) # right = box(pos = (-d - 0.005, 0, 0), axis = (0, 1, 0), length = 100, height = 0.005, width = 100, color = (0, 0, 0)) # uniform particle distribution for i in range(config.Natoms): qq = 2 * pi * random.random() x = sqrt(random.random()) * L * cos(qq) / 2 y = L * random.random() - L / 2 z = sqrt(random.random()) * L * sin(qq) / 2 if i == 0: # particle with a trace config.Atoms.append( sphere(pos=vector(x, y, z), radius=config.Ratom, color=color.cyan, make_trail=False, retain=100, trail_radius=0.3 * config.Ratom)) else: config.Atoms.append( sphere(pos=vector(x, y, z), radius=config.Ratom, color=gray)) apos.append(vector(x, y, z)) # waiting to start, adjusting everything according to changing variables """WAITING TO START""" last_Natoms = config.Natoms last_Ratom = config.Ratom while config.start == 0: if config.menu_switch == 0: disp.delete() g1.display.delete() g2.display.delete() graph_text.Destroy() speed_text.Destroy() return if config.Natoms > last_Natoms: for i in range(config.Natoms - last_Natoms): qq = 2 * pi * random.random() x = sqrt(random.random()) * L * cos(qq) / 2 y = L * random.random() - L / 2 z = sqrt(random.random()) * L * sin(qq) / 2 if last_Natoms == 0: # particle with a trace config.Atoms.append( sphere(pos=vector(x, y, z), radius=config.Ratom, color=color.cyan, make_trail=False, retain=100, trail_radius=0.3 * config.Ratom)) else: config.Atoms.append( sphere(pos=vector(x, y, z), radius=config.Ratom, color=gray)) apos.append(vector(x, y, z)) last_Natoms = config.Natoms elif config.Natoms < last_Natoms: for i in range(last_Natoms - config.Natoms): config.Atoms.pop().visible = False apos.pop() last_Natoms = config.Natoms if last_Ratom != config.Ratom: for i in range(last_Natoms): config.Atoms[i].radius = config.Ratom last_Ratom = config.Ratom if config.model == 0: if config.piston_mode >= 1: graph_text.SetLabel("Температура") else: graph_text.SetLabel("Распределение скоростей частиц") sleep(0.1) # freezed all variables, ready to start last_T = config.T last_ampl = config.ampl last_period = config.period last_piston_mode = config.piston_mode last_model = config.model pavg = sqrt(3 * config.mass * k * last_T) # average kinetic energy p**2/(2config.mass) = (3/2)kT for i in range(last_Natoms): theta = pi * random.random() phi = 2 * pi * random.random() px = pavg * sin(theta) * cos(phi) py = pavg * sin(theta) * sin(phi) pz = pavg * cos(theta) p.append(vector(px, py, pz)) if last_model == 1: disp.delete() unavail = wx.StaticText( config.w.panel, style=wx.ALIGN_CENTRE_HORIZONTAL, label="Отображение модели недоступно в режиме статистики", pos=(offset, height / 2 - offset)) unavail.Wrap(width / 3) last_period = last_period / 10 last_piston_mode += 1 graph_text.SetLabel("Температура") """ DRAW GRAPHS """ g1.display.delete() g2.display.delete() if last_piston_mode == 0: g1 = gdisplay(window=config.w, x=width / 3 + 2 * offset, y=2 * offset, background=color.white, xtitle='t', ytitle='v', foreground=color.black, width=width / 3, height=height / 2 - 2 * offset, ymin=0.7 * pavg / config.mass, ymax=1.3 * pavg / config.mass) g2 = gdisplay(window=config.w, x=width / 3 + 2 * offset, y=height / 2 + offset, background=color.white, foreground=color.black, xtitle='v', ytitle='Frequency', width=width / 3, height=height / 2 - 2 * offset, xmax=3000 / 300 * last_T, ymax=last_Natoms * deltav / 1000) else: g1 = gdisplay(window=config.w, x=width / 3 + 2 * offset, y=2 * offset, background=color.white, xtitle='t', ytitle='v', foreground=color.black, width=width / 3, height=height / 2 - 2 * offset) g2 = gdisplay(window=config.w, x=width / 3 + 2 * offset, y=height / 2 + offset, background=color.white, xtitle='t', ytitle='T', foreground=color.black, width=width / 3, height=height / 2 - 2 * offset) graph_average_speed = gcurve(gdisplay=g1, color=color.black) if last_piston_mode: graph_temp = gcurve(gdisplay=g2, color=color.black) else: theory_speed = gcurve(gdisplay=g2, color=color.black) dv = 10 for v in range(0, int(3000 / 300 * last_T), dv): theory_speed.plot(pos=(v, (deltav / dv) * last_Natoms * 4 * pi * ((config.mass / (2 * pi * k * last_T))**1.5) * exp(-0.5 * config.mass * (v**2) / (k * last_T)) * (v**2) * dv)) hist_speed = ghistogram(gdisplay=g2, bins=arange(0, int(3000 / 300 * last_T), 100), color=color.red, accumulate=True, average=True) speed_data = [] # histogram data for i in range(last_Natoms): speed_data.append(pavg / config.mass) # speed_data.append(0) """ MAIN CYCLE """ while config.start: while config.pause: sleep(0.1) rate(100) sp = speed(time, last_piston_mode, last_period, last_ampl, 300) cylindertop.pos.y -= sp * dt time += 1 for i in range(last_Natoms): config.Atoms[i].pos = apos[i] = apos[i] + (p[i] / config.mass) * dt if last_piston_mode == 0: speed_data[i] = mag(p[i]) / config.mass total_momentum = 0 v_sum = 0 for i in range(last_Natoms): total_momentum += mag2(p[i]) v_sum += sqrt(mag2(p[i])) / config.mass graph_average_speed.plot(pos=(time, v_sum / last_Natoms)) if last_piston_mode: graph_temp.plot(pos=(time, total_momentum / (3 * k * config.mass) / last_Natoms)) else: hist_speed.plot(data=speed_data) hitlist = checkCollisions(last_Natoms, last_Ratom) for ij in hitlist: i = ij[0] j = ij[1] ptot = p[i] + p[j] posi = apos[i] posj = apos[j] vi = p[i] / config.mass vj = p[j] / config.mass vrel = vj - vi a = vrel.mag2 if a == 0: # exactly same velocities continue rrel = posi - posj if rrel.mag > config.Ratom: # one atom went all the way through another continue # theta is the angle between vrel and rrel: dx = dot(rrel, norm(vrel)) # rrel.mag*cos(theta) dy = cross(rrel, norm(vrel)).mag # rrel.mag*sin(theta) # alpha is the angle of the triangle composed of rrel, path of atom j, and a line # from the center of atom i to the center of atom j where atome j hits atom i: alpha = asin(dy / (2 * config.Ratom)) d = (2 * config.Ratom) * cos( alpha ) - dx # distance traveled into the atom from first contact deltat = d / vrel.mag # time spent moving from first contact to position inside atom posi = posi - vi * deltat # back up to contact configuration posj = posj - vj * deltat mtot = 2 * config.mass pcmi = p[ i] - ptot * config.mass / mtot # transform momenta to cm frame pcmj = p[j] - ptot * config.mass / mtot rrel = norm(rrel) pcmi = pcmi - 2 * pcmi.dot(rrel) * rrel # bounce in cm frame pcmj = pcmj - 2 * pcmj.dot(rrel) * rrel p[i] = pcmi + ptot * config.mass / mtot # transform momenta back to lab frame p[j] = pcmj + ptot * config.mass / mtot apos[i] = posi + ( p[i] / config.mass) * deltat # move forward deltat in time apos[j] = posj + (p[j] / config.mass) * deltat # collisions with walls for i in range(last_Natoms): # проекция радиус-вектора на плоскость loc = vector(apos[i]) loc.y = 0 # вылет за боковую стенку (цилиндр радиуса L / 2 + config.Ratom) if (mag(loc) > L / 2 + 0.01 - last_Ratom + sqrt(p[i].x**2 + p[i].z**2) / config.mass * dt): # проекция импульса на плоскость proj_p = vector(p[i]) proj_p.y = 0 loc = norm(loc) # скалярное произведение нормированного радиус-вектора на импульс (все в проекции на плоскость) dotlp = dot(loc, proj_p) if dotlp > 0: p[i] -= 2 * dotlp * loc # dotlp < 0 - атом улетает от стенки # dotlp = 0 - атом летит вдоль стенки loc = apos[i] # вылет за торцы if loc.y + p[i].y / config.mass * dt < -L / 2 - 0.01 + last_Ratom: p[i].y = abs(p[i].y) if loc.y + p[ i].y / config.mass * dt > cylindertop.pos.y - last_Ratom: v_otn = p[i].y / config.mass + sp if v_otn > 0: p[i].y = (-v_otn - sp) * config.mass # type here if last_model == 0: disp.delete() else: unavail.Destroy() g1.display.delete() g2.display.delete() graph_text.Destroy() speed_text.Destroy()