 #### spoon

• • 1134
• Foxy wins ##### cool fun programming stuff
« on: April 27, 2015, 03:20:30 AM »
Code: [Select]
`from __future__ import division from visual import *from random import *#define constants and other parametersnumber = 8 #number of chargesq = 1.6e-19 #1 unit of charge (charge of an electron)k = 9e9 # k (1/4piEnaught)rad = 100 #radius of spherical regionshrink = 1.75 #used to make sure all charges begin within the spherezero = vector(0,0,0) #used for declaration purposeselectrons = 7e11*(1/number) #puts a lot of charge on each charge carrierd = .5 #damping effect the inside of the sphere has on the charges (not used)#generate translucent spherical regionitem1 = sphere(pos = (0,0,0), radius = rad, color = color.white, opacity = .3) #generate axes for clarity in charge position and depth within the spherexaxis = curve(pos = [(-1.5*rad,0,0),(1.5*rad,0,0)], color = color.blue, radius = rad/100)yaxis = curve(pos = [(0,-1.5*rad,0),(0,1.5*rad,0)], color = color.blue, radius = rad/100)zaxis = curve(pos = [(0,0,-1.5*rad),(0,0,1.5*rad)], color = color.blue, radius = rad/200)#label axestext(text = 'x', pos = (1.6*rad,-rad/30,rad/50), height = rad/15, depth= -rad/25, color=color.white)text(text = 'y', pos = (-rad/20,1.6*rad,rad/50), height = rad/10, depth= -rad/25, color=color.white)text(text = 'z', pos = (-rad/20,-rad/20,1.6*rad), height = rad/10, depth= -rad/25, color=color.white)#Create a list and fill it with charge carriers in random locations in the spherical region.charges = []for i in arange(number):    charges.append(sphere(pos = ((randrange(-rad*100,rad*100)/(shrink*100)),(randrange(-rad*100,rad*100)/(shrink*100)),(randrange(-rad*100,rad*100)/(shrink*100))), radius = rad/33, charge = electrons*q, force = zero, velocity = zero, color = color.red))    #This list will be used to make arrows and spheres and what have you.objects = []#This function is used to find the distance between points, which is needed to calculate electric field. a and b are charge carriers from 'charges'. def getDistance(a,b):    distance = mag(charges[a].pos-charges[b].pos)    return distance#This function calculates net force. a = which charge is having calculations done on it. b = the charge whose field is contributing a force to a. c = distance from getDistance function.def getNetForce(a,b,c):    direction = charges[a].pos - charges[b].pos    charges[a].force = (((k*charges[b].charge)/(c**2))*norm(direction))    return charges[a].forcescene.waitfor('click')#This chunk updates force, velocity, and position for each charge carrier.  for i in arange(number):    while mag(charges[i].pos) < 2 * rad:        for a in arange(number): #This for takes a charge carrier and compares it to...            for b in arange(number): #... to a charge carrier in this loop.                if a < b: #This if statement gets rid of overlap so the same calculation is not made twice.                    distance = getDistance(a, b) #get distance between charge carriers                    charges[a].force = getNetForce(a, b, distance) #get force on a due to b...                    charges[b].force = getNetForce(b, a, distance) #... and on b due to a                    charges[a].velocity = charges[a].velocity + charges[a].force #update...                    charges[b].velocity = charges[b].velocity + charges[b].force #...velocity                    sizeA = mag(charges[a].pos) #distance of first charge carrier from center...                    sizeB = mag(charges[b].pos) #... and the second                    #Charges near boundary, take two: Newtonian approach. This makes the charges "bounce off the inside of the wall by using the Rodrigues rotation formula.                    if sizeA >= rad:                        charges[a].pos = .999 * rad * norm(charges[a].pos) #moves charge carrier just inside sphere to avoid the charge getting "stuck"                        posUnit = norm(charges[a].pos) #unit vector in direction of charge position                        projection = proj(charges[a].velocity, charges[a].pos) #projection of velocity vector onto position vector                        charges[a].velocity = -.9 *((charges[a].velocity * cos(pi)) + (cross(posUnit, charges[a].velocity) * sin(pi)) + (projection * (1 - cos(pi)))) #Rodrigues' rotation formula                    if sizeB >= rad:                        charges[b].pos = .999 * rad * norm(charges[b].pos) #moves charge carrier just inside sphere to avoid the charge getting "stuck"                        posUnit = norm(charges[b].pos) #unit vector in direction of charge position                        projection = proj(charges[b].velocity, charges[b].pos) #projection of velocity vector onto position vector                        charges[b].velocity = -.9 *((charges[b].velocity * cos(pi)) + (cross(posUnit, charges[b].velocity) * sin(pi)) + (projection * (1 - cos(pi)))) #Rodrigues' rotation formula                            ## Clunky clode that deals with charges near boundary. Doesn't work well at all, sometimes glitchy. physics inconsistent    ##    ##                if sizeA > rad:    ##                    radialDirectionA = norm(charges[a].pos)    ##                    charges[a].pos = radialDirectionA * rad    ##                    crossA = cross(charges[a].pos, charges[a].force)    ##                    newForceDirA = cross(crossA, charges[a].pos)    ##                    angleA = diff_angle(charges[a].pos,charges[a].force)    ##                    #fixed so it doesn't get glitchy, but doesn't redistribute charges evenly on surface.    ##                    charges[a].force = mag(charges[a].force) * sin(2 * (pi - angleA)) * norm(newForceDirA) * d    ##                    charges[a].velocity = charges[a].force    ##                if sizeB > rad:    ##                    radialDirectionB = norm(charges[b].pos)    ##                    charges[b].pos = radialDirectionB * rad    ##                    crossB = cross(charges[a].pos, charges[a].force)    ##                    newForceDirB = cross(crossB, charges[a].pos)    ##                    angleB = diff_angle(charges[a].pos,charges[a].force)    ##                    #fixed so it doesn't get glitchy, but doesn't redistribute charges evenly on surface.    ##                    charges[b].force = mag(charges[b].force) * sin(2 * (pi - angleB)) * norm(newForceDirB) * d    ##                    charges[b].velocity = charges[b].force    #Optional section that displays force arrow with each update. Change it so old arrows disappear. also traces position with translucent balls.        #for i in arange(number):            #objects.append(arrow(pos=charges[i].pos, axis=charges[i].force, color=color.yellow)                #objects.append(sphere(pos=charges[i].pos, radius = rad / 33, color=color.red, opacity = .1))                    for a in arange(number):            rate(100)            charges[a].pos = charges[a].pos + charges[a].velocity #update position of charges...                    #print mag(charges.force)           #print mag(charges.velocity)`
Above is a program I am writing in vpython to study geometry by simulating the electric force! Placing n like charge carriers in a hollow, neutral, insulating sphere, one would expect the to disperse among the surface of the sphere in such a way that equilibrium is reached.  My supposition was that for charge carriers > 3, equilibrium would only be reached if the number of charges corresponded to the number of vertices in a platonic polyhedral. This supposition was based on intuition and a limited understanding of geometry in 3 space.

I was pleasantly surprised. For every number of charge carriers I have tried, equilibrium is reached, sometimes in fascinating ways. My favorite is 8. Instead of forming a cube, the charges form a polyhedral with two parallel square bases, rotated such that  it has 8 other faces that are equilateral triangles. Obviously, this is the orientation that results in a net force on each charge of zero, but it also seems correct to say that the same would be true for a cube. Each vertex on a cube is the same average distance from any other vertex.

This is still a work in progress, I need to streamline it and clean it up, but it is functional. Over the next few days, I am going to do some more research into this sort of geometry and see what I can find. In the mean time, feel free to copy/paste this into the most recent version of vpython and give it a whirl! Other cool things to do would be changing the charge on one or more charge carriers. If you have any ideas or insights, let me know.

Also, lol at everything being bold from calling "b" from a list "charges[]"

*thanks parsifal
« Last Edit: April 27, 2015, 03:28:19 AM by spoon »
inb4 Blanko spoons a literally pizza #### xasop

•     • 9746
• Professional computer somebody ##### Re: cool fun programming stuff
« Reply #1 on: April 27, 2015, 03:26:42 AM »
Wrap your code in [code] tags, and that won't happen.
when you try to mock anyone while also running the flat earth society. Lol #### spoon

• • 1134
• Foxy wins ##### Re: cool fun programming stuff
« Reply #2 on: May 04, 2015, 05:30:35 AM »
Code: [Select]
`from __future__ import division from visual import *from random import *from visual.graph import *#define constants and other parametersnumber = 9 #number of chargesq = 1.6e-19 #1 unit of charge (charge of an electron)k = 9e9 # k (1/4piEnaught)rad = 100 #radius of spherical regionshrink = 1.75 #used to make sure all charges begin within the spherezero = vector(0,0,0) #used for declaration purposeselectrons = 7e11*(1/number) #puts a lot of charge on each charge carrierKE = 0 #kinetic energy of the systemt = 0 #timef1 = gcurve(color=color.cyan) # a graphics curve#generate translucent spherical regionitem1 = sphere(pos = (0,0,0), radius = rad, color = color.white, opacity = .3) #generate axes for clarity in charge position and depth within the spherexaxis = curve(pos = [(-1.5*rad,0,0),(1.5*rad,0,0)], color = color.blue, radius = rad/100)yaxis = curve(pos = [(0,-1.5*rad,0),(0,1.5*rad,0)], color = color.blue, radius = rad/100)zaxis = curve(pos = [(0,0,-1.5*rad),(0,0,1.5*rad)], color = color.blue, radius = rad/200)#Create a list and fill it with charge carriers in random locations in the spherical region.charges = []for i in arange(number):    charges.append(sphere(pos = ((randrange(-rad*100,rad*100)/(shrink*100)),(randrange(-rad*100,rad*100)/(shrink*100)),(randrange(-rad*100,rad*100)/(shrink*100))), radius = rad/33, charge = electrons*q, force = zero, velocity = zero, color = color.red))#charges.charge = *electrons*q#This list will be used to make arrows and spheres and what have you.objects = []#This function is used to find the distance between points, which is needed to calculate electric field. a and b are charge carriers from 'charges'. def getDistance(a,b):    distance = mag(charges[a].pos-charges[b].pos)    return distance#This function calculates net force. a = which charge is having calculations done on it. b = the charge whose field is contributing a force to a. c = distance from getDistance function.def getNetForce(a,b,c):    direction = charges[a].pos - charges[b].pos    charges[a].force = (((k*charges[b].charge)/(c**2))*norm(direction))    return charges[a].force#click to start moving chargesscene.waitfor('click')#This chunk updates force, velocity, and position for each charge carrier.for i in arange(number):    while mag(charges[i].pos) < 2 * rad:        t = t + 1 #increment time for kinetiv energy graph        if scene.mouse.clicked: #click to break from loop and draw lines between charges            break        for a in arange(number): #This for takes a charge carrier and compares it to...            for b in arange(number): #... a charge carrier in this loop.                if a < b: #This if statement gets rid of overlap so the same calculation is not made twice.                    distance = getDistance(a, b) #get distance between charge carriers                    charges[a].force = getNetForce(a, b, distance) #get force on a due to b...                    charges[b].force = getNetForce(b, a, distance) #... and on b due to a                    #Is this the best way to update velocity?                    charges[a].velocity = charges[a].velocity + charges[a].force #update...                    charges[b].velocity = charges[b].velocity + charges[b].force #...velocity                    sizeA = mag(charges[a].pos) #distance of first charge carrier from center...                    sizeB = mag(charges[b].pos) #... and the second                            #This makes the charges "bounce off the inside of the wall by using the Rodrigues rotation formula.                    if sizeA >= rad:                        charges[a].pos = .999 * rad * norm(charges[a].pos) #moves charge carrier just inside sphere to avoid the charge getting "stuck"                        posUnit = norm(charges[a].pos) #unit vector in direction of charge position                        projection = proj(charges[a].velocity, charges[a].pos) #projection of velocity vector onto position vector                        charges[a].velocity = -.9 *((charges[a].velocity * cos(pi)) + (cross(posUnit, charges[a].velocity) * sin(pi)) + (projection * (1 - cos(pi)))) #Rodrigues' rotation formula                    if sizeB >= rad:                        charges[b].pos = .999 * rad * norm(charges[b].pos) #moves charge carrier just inside sphere to avoid the charge getting "stuck"                        posUnit = norm(charges[b].pos) #unit vector in direction of charge position                        projection = proj(charges[b].velocity, charges[b].pos) #projection of velocity vector onto position vector                        charges[b].velocity = -.9 *((charges[b].velocity * cos(pi)) + (cross(posUnit, charges[b].velocity) * sin(pi)) + (projection * (1 - cos(pi)))) #Rodrigues' rotation formula                            #displays force arrow with each update. Change it so old arrows disappear. also traces position with dots.         #for i in arange(number):            #objects.append(arrow(pos=charges[i].pos, axis=charges[i].force, color=color.yellow)                #objects.append(sphere(pos=charges[i].pos, radius = rad / 50, color=color.yellow, opacity = .1))        #update position of charges          for a in arange(number):            rate(300)            charges[a].pos = charges[a].pos + charges[a].velocity             for i in arange(number):                KE = KE + mag(charges[i].velocity)**2                f1.plot(pos=(t,KE,0))            KE = 0            #This loop draws lines between each charge carrier after the second click.for a in arange(number):    for b in arange(number):        if a < b:            curve(pos = [charges[a].pos, charges[b].pos])#This loop creates a convex solid out of the charges' positions and makes charges transparent.    scene.waitfor('keydown')a = convex(color = color.white)for i in arange(number):    a.append(pos = charges[i].pos)    charges[i].opacity = 0    #make other objects transparentitem1.opacity = 0xaxis.pos = [zero, zero]yaxis.pos = [zero, zero]zaxis.pos = [zero, zero]`
Basically finished product, with added functionality. Now, after viewing the beautiful symmetry created by the lines between each vertex, you can view the solid created by those vertices. I think it's gorgeous stuff.

With a high number of charge carriers, the solid created begins to resemble a geodisic sphere.

You also get a graph that shows the kinetic energy of the system with respect to time. woo
inb4 Blanko spoons a literally pizza #### Tom Bishop

• Zetetic Council Member
•  • 10493
• Flat Earth Believer ##### Re: cool fun programming stuff
« Reply #3 on: May 09, 2015, 04:06:13 PM »
How do we get the visual module?

PS C:\Users\tbishop\python> python .\spoon.py
Traceback (most recent call last):
File ".\spoon.py", line 2, in <module>
from visual import *
ImportError: No module named visual
PS C:\Users\tbishop\python> #### Tom Bishop

• Zetetic Council Member
•  • 10493
• Flat Earth Believer ##### Re: cool fun programming stuff
« Reply #4 on: May 09, 2015, 04:28:02 PM »
Okay, so I downloaded vpython for win32, and pasted the code into the vpython editor, but can't seem to run it. I selected my base python 27 wpython.exe when the program ran. This is what happens when I press F5: « Last Edit: May 09, 2015, 06:28:00 PM by Tom Bishop » #### Pete Svarrior

• e
• Planar Moderator
•     • 15949
• (◕˽ ◕ ✿) ##### Re: cool fun programming stuff
« Reply #5 on: May 09, 2015, 05:49:05 PM »
This is a bit of a random guess, but: make sure you don't have any .py files in the directory where pythonw.exe is located

If we are not speculating then we must assume #### spoon

• • 1134
• Foxy wins ##### Re: cool fun programming stuff
« Reply #6 on: May 09, 2015, 09:11:21 PM »
Use Vidle, not Idle.
inb4 Blanko spoons a literally pizza