I am doing a Vpython assignment for my physics 2 class, which is asking to program the electric field of a dipole. I have written the following code:
## constants
oofpez = 9e9 # stands for One Over Four Pi Epsilon-Zero
qe = 1.6e-19 # postive charge value
s = 4e-11 # charge separation
R = 3e-10 # display Enet on a circle of radius R
scalefactor = 3e-20 # for scaling arrows to represent electric field
## objects
## Represent the two charges of the dipole by red and blue spheres:
plus = sphere(pos=vector(s/2,0,0), radius=1e-11, color=color.red)
qplus = qe # charge of positive particle
neg = sphere(pos=vector(-s/2,0,0), radius=1e-11, color=color.blue)
qneg = -qplus # charge of negative particle
## calculations
## You will complete the lines required to make a loop calculate and display the net dipole electric field
## at equally spaced angles on a circle radius R around the dipole. The dipole is centered at the origin.
theta = 0
while theta < 2*pi:
rate(2) # tell computer to go through loop slowly
## Calculate observation location (tail of arrow) using current value of theta:
Earrow = arrow(pos=R*vector(cos(theta),sin(theta),0), axis=vector(1e-10,0,0), color=color.orange)
## assign the name TestLocation to be the observation location on the circle radius R
TestLocation=R*vector(cos(theta),sin(theta),0)
## write instructions below to tell the computer how to calculate the correct
## net electric field Enet at the observation location (the position of Earrow):
rPlus=TestLocation-plus.pos
rPlusMag=((R*cos(theta)-(s/2))^2+(R*sin(theta))^2)^0.5
rPlusHat=rPlus/rPlusMag
Eplus=oofpez*qplus/(rPlusMag)^2*rPlusHat
rNeg=TestLocation-neg.pos
rNegMag=((R*cos(theta)-(-s/2))^2+(R*sin(theta))^2)^0.5
rNegHat=rNeg/rNegMag
Eneg=oofpez*qneg/(rNegMag)^2*rNegHat
Etotal=Eplus+Eneg
Enet=arrow(pos=TestLocation,axis=Etotal*scalefactor, color=color.green)
## change the axis of Earrow to point in the direction of the electric field at that location
## and scale it so it looks reasonable
## Efield = arrow(pos=R*vector(cos(theta),sin(theta),0), axis=Etotal*scalefactor, color=color.blue)
Earrow.axis=Etotal*scalefactor
## Assign a new value to theta
theta = theta + pi/6
The assignment had a pre-made template created with the comments, the proper variables declared and said variables assigned the correct values, so in theory it should run correctly if I input the rest of the code correctly. The code I wrote starts at "rPlus=..." and ends at "Enet=..." However, when I run it (using GlowScript IDE) it gives an error message saying "Error: Property 'axis' must be a vector," which I'm sure means that there's something wrong with the value assigned to Enet.axis within that section of the code. I have looked through the code I've generated and can't seem to find the error.
We are learning python supplementary to our regular course work so I do not have any background in python besides these assignments. I don't need help with finding the electric field, just why the error message is popping up. Any help or hint in the right direction would be appreciated!
Thanks
On line 31 you used ^ instead of ** for exponentiation.
If you use the Chrome browser you can get the line number of an error.
I found the problem by inserting print statements at key locations in the code, which turned up the fact that rPlusMag was 0, not a vector.
Related
I'm trying to solve ODE with odeint in python. I'm working on a physics project.
My goal is to calculate the direction and angle of trajectory in a spherical coordinate when I put the speed of matter, my location, landing point.
for example, when I put parameter - 800m/s, (S50° E135°)(my location), 10km to the south(landing point). then the result says like this - shoot 180°(0° is north), the elevation of 37°.
I can calculate the trajectory when I know the initial value, I can calculate the result and it's trajectory but I can't know what to do to complete my object.
G=6.673*10**(-11) # gravity constant
M=5.972*10**(24) # mass of earth
R=6374916 # radius of earth
w=7.3*10**-5 # angular velocity of earth's ratation
L=np.pi*7/9 # my longitude
l=float(input('Input your latitude(-90~90):'))
m=float(input('Input direction(0~360):'))
d=float(input('How far do you want to fire?(In meter):'))
v=int(input('How fast your matter?(In m/s):'))
dt=l+d*np.sin(m/180*np.pi)/R #target latitude
dp=L+d*np.cos(m/180*np.pi)/R #target longitude
Enter some parameter so far
def nodrag(y,t):
dydt0=y[1]
dydt1=y[0]*(y[3]**2)+y[0]*(y[5]**2)*(np.sin(y[2])**2)-G*M/(y[0]**2)
dydt2=y[3]
dydt3=-2*y[1]/y[0]*y[3]+(y[5]**2)*np.sin(y[2])*np.cos(y[2])
dydt4=y[5]
dydt5=-2*y[1]/y[0]*y[5]-2*y[3]*y[5]*np.cos(y[2])/np.sin(y[2])
return [dydt0,dydt1,dydt2,dydt3,dydt4,dydt5]
rlist=[]
thlist=[]
pilist=[]
ddp=[]
def ODE(azimuth,angle):
yini=np.array([R,v*np.sin(angle),np.pi/2-l*np.pi/180,-v*np.cos(angle)*np.cos(azimuth)/R,np.pi*135/180,w+v*np.cos(angle)*np.sin(azimuth)/R])
t=np.linspace(0,v,v*1000)
result=odeint(nodrag,yini,t)
r=result[:,0] # value of radius
th=result[:,2] # value of theta
pi=result[:,4] # value of pi
rr=list(r)
tt=list(th)
pp=list(pi) # change array to list
for i in range (1,v*1000):
if rr[i]<R:
number=i
break # find time when it reach r=R again
for i in range (number,v*1000):
tt.pop(-1)
pp.pop(-1) # remove extra
thlist.append(tt[-1])
pilist.append(pp[-1]) # save last values (r=R)
move=dp+w*t[number] # displacement of landing point of pi because of rotation of the earth
ddp.append(move)
So far soving ODE, my next part is brute force
min=np.pi*2
for i in range (0,360):
i=i/180*np.pi
for j in range (0,91):
j=j/180*np.pi
ODE(i,j) #calculate all directions
for k in range (0,32760):
compare=np.arccos(np.sin(dt)*np.sin(thlist[k])*np.cos(ddp[k]-pilist[k])+np.cos(dt)*np.cos(thlist[k]))
if compare<=min:
min=compare
azi=k//360
ang=k-azi*360
This code is quite inefficient. It requires about 40minutes
This is an example of my code - when I put some initial value.
but If I put an unknown value in the initial value, odeint does not work. What should I do?
When I get to know how to proceed, I'm going to iterate with the error.
You have a problem of the form that you know posA and posB, and a length constraint v0 for velA. The time to go from A to B is unknown. You have a function derivs (to stay generic, or nodrag in your case) to compute the derivative of combined state of pos and vel from physical principles.
With this you can set up the boundary value solver over the interval [0,1], scaling with the parameter T to get the real interval [0,T] with variable end point. Consequently, if u(s)=y(T*s) the derivative is u'(s)=T*y'(T*s)=T*derivs(T*s,u(s)).
def bvp_ode(s,u,T): return T*np.asarray(derivs(s*T,u))
def bvp_bc(uA,uB,T):
pA, vA=uA[:3],uA[3:] # or uA[0::2],uA[1::2]
pB, vB=uB[:3],uB[3:] # or uB[0::2],uB[1::2]
return np.concatenate(pA-posA, pB-posB, [sum(velA**2)-v0**2])
s_init = [0,1]
u_init = np.asarray([np.concatenate(pos,[0,0,0]) for pos in (posA, posB) ]).T
T = 100 # or dist(posA,posB)/v0
res = solve_bvp(bvp_ode, bvp_bc, s_init, u_init, p=[T])
print(res.message)
T=res.p[0]
velA = res.y[3:,0]
print("T=",T,", velA=",velA)
I'm trying to use Python-Meep package to conduct some FDTD simulations. First, I want to simulate a plane wave traveling through vacuum in 'z' direction. I have problems properly setting the source in three-dimensional case. In 2D case, I can make the source as a line which touches the borders of the computational matrix. In 3D it looks like it's impossible. Below are simple examples.
2D case: In 2D case, the source is a line from (x,y)=(0 , .1e-6) to (x,y)=(15e-6 , .1e-6) (from border to border). Thanks to this, the plane wave is traveling unperturbed to the opposite end of the matrix (where it is reflected).
import meep_mpi as meep
x, y, voxelsize = 15e-6, 15e-6, 50e-9
vol = meep.vol2d(x, y, 1/voxelsize)
class Model(meep.Callback):
def __init__(self):
meep.Callback.__init__(self)
def double_vec(self, r):
return 1
model = Model()
meep.set_EPS_Callback(model.__disown__())
struct = meep.structure(vol, meep.EPS)
f = meep.fields(struct)
f.add_volume_source(meep.Ex,
meep.continuous_src_time(473.755e12/3e8), # 632.8nm
meep.volume(meep.vec(0e-6, .1e-6), meep.vec(15e-6, .1e-6)))
while f.time()/3e8 < 30e-15:
f.step()
meep.del_EPS_Callback()
output = meep.prepareHDF5File("Ex1.h5")
f.output_hdf5(meep.Ex, vol.surroundings(), output)
del(output)
3D case: The source is a plane from (x,y,z)=(0 , 0 , .1e-6) to (x,y,z)=(15e-6 , 15e-6 , .1e-6). This should create a plane from border to border of the matrix. However, for unknown reason, the source does not touch the boundary (there is a small empty space) and whatever I do, I cannot force it to touch it. As a result, I cannot create a plane wave travelling in 'z' direction. Until now I tried: (a) explicitly giving no_pml argument (b) giving pml(0) argument, (c) changing sampling, (d) changing 'z' position of the source. With no luck. I will be grateful for any suggestions.
import meep_mpi as meep
x, y, z, voxelsize = 15e-6, 15e-6, 15e-6, 50e-9
vol = meep.vol3d(x, y, z, 1/voxelsize)
class Model(meep.Callback):
def __init__(self):
meep.Callback.__init__(self)
def double_vec(self, r):
return 1
model = Model()
meep.set_EPS_Callback(model.__disown__())
struct = meep.structure(vol, meep.EPS)
f = meep.fields(struct)
f.add_volume_source(meep.Ex,
meep.continuous_src_time(473.755e12/3e8), # 632.8nm
meep.volume(meep.vec(0, 0, .1e-6), meep.vec(15e-6, 15e-6, .1e-6)))
while f.time()/3e8 < 30e-15:
f.step()
meep.del_EPS_Callback()
output = meep.prepareHDF5File("Ex1.h5")
f.output_hdf5(meep.Ex, vol.surroundings(), output)
del(output)
Your inability to send a homogeneous plane wave with electric field polarised along the X axis indeed manifests at the simulation volume boundaries perpendicular to the Y axis, where the field amplitude drops to zero. This trouble does not occur on the two boundaries perpendicular to X.
This is however fully physical solution; by default, the boundaries behave as perfect electric/magnetic conductor; the electric field component parallel to PEC must be zero in its vicinity. (Good conductors screen the external electric field.)
If you need an exact plane wave, you will have to append another command after the initialisation of field, to define the boundary as periodic:
f.use_bloch(meep.X, 0)
f.use_bloch(meep.Y, 0)
Note that the second parameters doe not have to be zero, enabling the definition of arbitrary inclined wave sources.
For a more advanced (and more convenient) example, see https://github.com/FilipDominec/python-meep-utils/blob/master/scatter.py
correct me if I am wrong but when you use call getArea() from a meshFace node it doesn't return the area relative to the current scene units but rather relative to its own scale.
For example:
If I create a box with the scene units set to Meters, then the box is created with a scale of 1,1,1.
With this box I call:
#Assuming you selected only faces from the scene.
import pymel.core as pm
selected = pm.ls(selection = True, flatten = True)
totalFaceArea = 0
for face in selected:
totalFaceArea = totalFaceArea + face.getArea(space = "world")
print selected
print totalFaceArea
However, if I change the scene units to Centimeters then the scale doesn't change (which makes sense). The problem is that the area seems to be coming from the scale not the amount of space its taking up in the scene so the area doesn't change when you change the scene units.
Do you guys know of a way to compensate for the change in scene units or a way to get the area of a face in the scene units?
Thanks!
Maya uses cm internally, and it's shamefully inconsistent about applying unit settings. Most systems and commands work in you work unit, but not all. Off the top of my head a few of the holdouts are camera settings, polySelectConstraints, and (as in your case) all API based geometry calculations.
for cases like this usually have a corrective function to translate the values into my expected units. The function is trivial:
def scene_unit_linear(val):
_IN_CM = {'m':100, 'in':2.54, 'cm':1, 'ft':30.48}
_scale = _IN_CM[cmds.currentUnit(q=True, l=True)]
return _scale * val
Unfortunately the hard part is knowing when to use it :( Also (and this is important!) you need to use the appropriate power if you're getting a two or three dimensional value. In your case, you'd need to square it since you're getting an area value: 1 square meter is 10,000 square cm, not 100!
def scene_unit_area(val):
_IN_CM = {'m':100, 'in':2.54, 'cm':1, 'ft':30.48}
_scale = _IN_CM[cmds.currentUnit(q=True, l=True)]
return _scale * _scale * val
world_area = scene_unit_area(face.getArea(space = "world"))
I am trying to estimate the value of pi using a monte carlo simulation. I need to use two unit circles that are a user input distance from the origin. I understand how this problem works with a single circle, I just don't understand how I am meant to use two circles. Here is what I have got so far (this is the modified code I used for a previous problem the used one circle with radius 2.
import random
import math
import sys
def main():
numDarts=int(sys.argv[1])
distance=float(sys.argv[2])
print(montePi(numDarts,distance))
def montePi(numDarts,distance):
if distance>=1:
return(0)
inCircle=0
for I in range(numDarts):
x=(2*(random.random()))-2
y=random.random()
d=math.sqrt(x**2+y**2)
if d<=2 and d>=-2:
inCircle=inCircle+1
pi=inCircle/numDarts*4
return pi
main()
I need to change this code to work with 2 unit circles, but I do not understand how to use trigonometry to do this, or am I overthinking the problem? Either way help will be appreciated as I continue trying to figure this out.
What I do know is that I need to change the X coordinate, as well as the equation that determines "d" (d=math.sqrt(x*2+y*2)), im just not sure how.
These are my instructions-
Write a program called mcintersection.py that uses the Monte Carlo method to
estimate the area of this shape (and prints the result). Your program should take
two command-line parameters: distance and numDarts. The distance parameter
specifies how far away the circles are from the origin on the x-axis. So if distance
is 0, then both circles are centered on the origin, and completely overlap. If
distance is 0.5 then one circle is centered at (-0.5, 0) and the other at (0.5, 0). If
distance is 1 or greater, then the circles do not overlap at all! In that last case, your
program can simply output 0. The numDarts parameter should specify the number
of random points to pick in the Monte Carlo process.
In this case, the rectangle should be 2 units tall (with the top at y = 1 and the
bottom at y = -1). You could also safely make the rectangle 2 units wide, but this
will generally be much bigger than necessary. Instead, you should figure out
exactly how wide the shape is, based on the distance parameter. That way you can
use as skinny a rectangle as possible.
If I understand the problem correctly, you have two unit circles centered at (distance, 0) and (-distance, 0) (that is, one is slightly to the right of the origin and one is slightly to the left). You're trying to determine if a given point, (x, y) is within both circles.
The simplest approach might be to simply compute the distance between the point and the center of each of the circles. You've already done this in your previous code, just repeat the computation twice, once with the offset distance inverted, then use and to see if your point is in both circles.
But a more elegant solution would be to notice how your two circles intersect each other exactly on the y-axis. To the right of the axis, the left circle is completely contained within the right one. To the left of the y-axis, the right circle is entirely within the left circle. And since the shape is symmetrical, the two halves are of exactly equal size.
This means you can limit your darts to only hitting on one side of the axis, and then get away with just a single distance test:
def circle_intersection_area(num_darts, distance):
if distance >= 1:
return 0
in_circle = 0
width = 1-distance # this is enough to cover half of the target
for i in range(num_darts):
x = random.random()*width # random value from 0 to 1-distance
y = random.random()*2 - 1 # random value from -1 to 1
d = math.sqrt((x+distance)**2 + y**2) # distance from (-distance, 0)
if d <= 1:
in_circle += 1
sample_area = width * 2
target_area = sample_area * (in_circle / num_darts)
return target_area * 2 # double, since we were only testing half the target
I have created a Python file to generate a Mandelbrot set image. The original maths code was not mine, so I do not understand it - I only heavily modified it to make it about 250x faster (Threads rule!).
Anyway, I was wondering how I could modify the maths part of the code to make it render one specific bit. Here is the maths part:
for y in xrange(size[1]):
coords = (uleft[0] + (x/size[0]) * (xwidth),uleft[1] - (y/size[1]) * (ywidth))
z = complex(coords[0],coords[1])
o = complex(0,0)
dotcolor = 0 # default, convergent
for trials in xrange(n):
if abs(o) <= 2.0:
o = o**2 + z
else:
dotcolor = trials
break # diverged
im.putpixel((x,y),dotcolor)
And the size definitions:
size1 = 500
size2 = 500
n=64
box=((-2,1.25),(0.5,-1.25))
plus = size[1]+size[0]
uleft = box[0]
lright = box[1]
xwidth = lright[0] - uleft[0]
ywidth = uleft[1] - lright[1]
what do I need to modify to make it render a certain section of the set?
The line:
box=((-2,1.25),(0.5,-1.25))
is the bit that defines the area of coordinate space that is being rendered, so you just need to change this line. First coordinate pair is the top-left of the area, the second is the bottom right.
To get a new coordinate from the image should be quite straightforward. You've got two coordinate systems, your "image" system 100x100 pixels in size, origin at (0,0). And your "complex" plane coordinate system defined by "box". For X:
X_complex=X_complex_origin+(X_image/X_image_width)*X_complex_width
The key in understanding how to do this is to understand what the coords = line is doing:
coords = (uleft[0] + (x/size[0]) * (xwidth),uleft[1] - (y/size[1]) * (ywidth))
Effectively, the x and y values you are looping through which correspond to the coordinates of the on-screen pixel are being translated to the corresponding point on the complex plane being looked at. This means that (0,0) screen coordinate will translate to the upper left region being looked at (-2,1.25), and (1,0) will be the same, but moved 1/500 of the distance (assuming a 500 pixel width window) between the -2 and 0.5 x-coordinate.
That's exactly what that line is doing - I'll expand just the X-coordinate bit with more illustrative variable names to indicate this:
mandel_x = mandel_start_x + (screen_x / screen_width) * mandel_width
(The mandel_ variables refer to the coordinates on the complex plane, the screen_ variables refer to the on-screen coordinates of the pixel being plotted.)
If you want then to take a region of the screen to zoom into, you want to do exactly the same: take the screen coordinates of the upper-left and lower-right region, translate them to the complex-plane coordinates, and make those the new uleft and lright variables. ie to zoom in on the box delimited by on-screen coordinates (x1,y1)..(x2,y2), use:
new_uleft = (uleft[0] + (x1/size[0]) * (xwidth), uleft[1] - (y1/size[1]) * (ywidth))
new_lright = (uleft[0] + (x2/size[0]) * (xwidth), uleft[1] - (y2/size[1]) * (ywidth))
(Obviously you'll need to recalculate the size, xwidth, ywidth and other dependent variables based on the new coordinates)
In case you're curious, the maths behind the mandelbrot set isn't that complicated (just complex).
All it is doing is taking a particular coordinate, treating it as a complex number, and then repeatedly squaring it and adding the original number to it.
For some numbers, doing this will cause the result diverge, constantly growing towards infinity as you repeat the process. For others, it will always stay below a certain level (eg. obviously (0.0, 0.0) never gets any bigger under this process. The mandelbrot set (the black region) is those coordinates which don't diverge. Its been shown that if any number gets above the square root of 5, it will diverge - your code is just using 2.0 as its approximation to sqrt(5) (~2.236), but this won't make much noticeable difference.
Usually the regions that diverge get plotted with the number of iterations of the process that it takes for them to exceed this value (the trials variable in your code) which is what produces the coloured regions.