Plotting a vector field using quiver - python

I'm trying to reproduce a 2D vector map with components
v = 100/a * exp(-1/a^2 * ((x+0.55)^2+y^2))(-y,x) - 100/a * exp(-1/a^2 * ((x-0.55)^2+y^2))(-y,x)
and here are my codes. It did not give the map I want (see attached vector map). Could someone please help me with it?
import numpy as np
import matplotlib.pyplot as plt
import math
grid_resolution = 25
grid_size = 2*grid_resolution+1
a = 0.2
x = np.linspace(-1,1,grid_size)
y = np.linspace(-1,1,grid_size)
X,Y = np.meshgrid(x, y)
vx = np.zeros((grid_size,grid_size))
vy = np.zeros((grid_size,grid_size))
for i in range(0,grid_size):
for j in range(0,grid_size):
x0 = x[j]
y0 = y[i]
xx = (x0 + 0.55) ** 2 + y0 ** 2
yy = (x0 - 0.55) ** 2 + y0 ** 2
expf1 = math.exp(-xx / (a ** 2))
expf2 = math.exp(-yy / (a ** 2))
vx[i,j] = 100 / a * (-expf1 + expf2) * y0
vy[i,j] = 100 / a * (expf1 - expf2) * x0
fig, ax = plt.subplots()
ax.quiver(X, Y, vx, vy)
ax.set_aspect('equal')
plt.show()

In the last passage, when you compute vx[i,j] and vy[i,j], you are computing vector field components in (x0, y0), while you should compute it in the current point, so (x0 ± 0.55, y0). Moreover, you should change the sign of vx and vy in order to draw a vector field like the one you linked.
import numpy as np
import matplotlib.pyplot as plt
import math
grid_resolution = 25
grid_size = 2*grid_resolution + 1
a = 0.2
x = np.linspace(-1, 1, grid_size)
y = np.linspace(-1, 1, grid_size)
X, Y = np.meshgrid(x, y)
vx = np.zeros((grid_size, grid_size))
vy = np.zeros((grid_size, grid_size))
for i in range(0, grid_size):
for j in range(0, grid_size):
x0 = x[j]
y0 = y[i]
xx = (x0 + 0.55)**2 + y0**2
yy = (x0 - 0.55)**2 + y0**2
expf1 = math.exp(-xx/(a**2))
expf2 = math.exp(-yy/(a**2))
vx[i, j] = -100/a*(-expf1 + expf2)*y0
if x0 > 0:
vy[i, j] = -100/a*(expf1 - expf2)*(x0 - 0.55)
else:
vy[i, j] = -100/a*(expf1 - expf2)*(x0 + 0.55)
fig, ax = plt.subplots()
ax.quiver(X,Y,vx,vy)
ax.set_aspect('equal')
plt.show()

Related

Scipy odeint. Gravitational motion

I solve the motion in gravitational field around the sun with scipy and mathplotlib and have a problem. My solution is not correct. It is not like in example. Formulas that I used.
from scipy.integrate import odeint
import scipy.constants as constants
import numpy as np
import matplotlib.pyplot as plt
M = 1.989 * (10 ** 30)
G = constants.G
alpha = 30
alpha0 = (alpha / 180) * np.pi
v00 = 0.7
v0 = v00 * 1000
def dqdt(q, t):
x = q[0]
y = q[2]
ax = - G * M * (x / ((x ** 2 + y ** 2) ** 1.5))
ay = - G * M * (y / ((x ** 2 + y ** 2) ** 1.5))
return [q[1], ax, q[3], ay]
vx0 = v0 * np.cos(alpha0)
vy0 = v0 * np.sin(alpha0)
x0 = -150 * (10 ** 11)
y0 = 0 * (10 ** 11)
q0 = [x0, vx0, y0, vy0]
N = 1000000
t = np.linspace(0.0, 100000000000.0, N)
pos = odeint(dqdt, q0, t)
x1 = pos[:, 0]
y1 = pos[:, 2]
plt.plot(x1, y1, 0, 0, 'ro')
plt.ylabel('y')
plt.xlabel('x')
plt.grid(True)
plt.show()
How can I fix this?
Maybe you can tell me solution with another method for example with Euler's formula or with using other library.
I will be very greatful if you help me.

Fitting curve with conditions

I'm trying to simulate an exoplanet transit and to determine its orbital characteristics with curve fitting. However, the intersection area between two circles needs to distinguish two cases: if the center of the smallest circle is in the biggest or not. This is a problem for scipy with the function curve_fit, calling an array in my function cacl_aire. The function transit simulates the smallest disc's evolution with time.
Here's my code:
import numpy as np
from matplotlib import pyplot as plt
from scipy.optimize import curve_fit
import xlrd
dt = 0.1
Vx = 0.08
Vy = 0
X0 = -5
Y0 = 0
R = 2
r = 0.7
X = X0
Y = Y0
doc = xlrd.open_workbook("transit data.xlsx")
feuille_1 = doc.sheet_by_index(0)
mag = [feuille_1.cell_value(rowx=k, colx=4) for k in range(115)]
T = [feuille_1.cell_value(rowx=k, colx=3) for k in range(115)]
def calc_aire(r, x, y):
D2 = x * x + y * y
if D2 >= (r + R)**2:
return 0
d = (r**2 - R**2 + D2) / (2 * (D2**0.5))
d2 = D2**0.5 - d
if abs(d) >= r:
return min([r * r * np.pi, R * R * np.pi])
H = (r * r - d * d)**0.5
As = np.arccos(d / r) * r * r - d * H
As2 = R * R * np.arccos(d2 / R) - d2 * H
return As + As2
def transit(t, r, X0, Y0, Vx, Vy):
return -calc_aire(r, X0 + Vx * t, Y0 + Vy * t)
best_vals = curve_fit(transit, T, mag)[0]
print('best_vals: {}'.format(best_vals))
plt.figure()
plt.plot(T, mag)
plt.draw()
I have the following error :
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all() with the line 28 :
if D2 >= (r + R)**2:
Here is my database:
https://drive.google.com/file/d/1SP12rrHGjjpHfKBQ0l3nVMJDIRCPlkuf/view?usp=sharing
I don't see any trick to solve my problem.

Hitting a specified target region on a sphere

I am attempting to create a program that will count the number of hits to a specific rectangular area on the surface of a sphere. How the programs is supposed to work, is random lines are generated and if one of those line hits in the target area the count goes up one. My problem is I do not think I am generating the lines correctly and I really have know idea how to correctly set the count parameters. This is the code I have so far and how I think the lines should be generated and what the count parameter might be.
import numpy as np
import random as rand
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_aspect("equal")
#rough model of the earth
theta, phi = np.mgrid[0:2*np.pi : 20j ,0:np.pi : 20j]
r = 6.3
x = r * np.cos(phi)*np.sin(theta)
y = r * np.sin(phi)*np.sin(theta)
z = r * np.cos(theta)
ax.plot_wireframe(x,y,z, color = "k")
#target area
lat1x = 46.49913179 * (2*np.pi/360)
lat2x = 46.4423682 * (2*np.pi/360)
long1y = -119.4049072 * (2*np.pi/360)
long2y = -119.5048141 * (2*np.pi/360)
lat3x = 46.3973998 * (2*np.pi/360)
lat4x = 46.4532495 * (2*np.pi/360)
long3y = -119.4495392 * (2*np.pi/360)
long4y = -119.3492884 * (2*np.pi/360)
def to_cartesian(lat,lon):
x = r * np.cos(lon)*np.cos(lat)
y = r * np.sin(lon)*np.cos(lat)
z = r * np.sin(lat)
return [x,y,z]
p1 = to_cartesian(lat1x,long1y)
p2 = to_cartesian(lat2x,long2y)
p3 = to_cartesian(lat3x,long3y)
p4 = to_cartesian(lat4x,long4y)
X = np.array([p1,p2,p3,p4])
ax.scatter(X[:,0],X[:,1],X[:,2], color = "r")
#random line path
n = 500
x0 = np.zeros(n)
y0 = np.zeros(n)
z0 = np.zeros(n)
x1 = np.zeros(n)
y1 = np.zeros(n)
z1 = np.zeros(n)
for k in range (n):
theta = rand.uniform(0.0, np.pi)
phi = rand.uniform(0, (2 * np.pi))
x0[k] = 100 * np.sin(phi) * np.cos(theta)
y0[k] = 100 * np.sin(phi) * np.sin(theta)
z0[k] = 100 * np.cos(theta)
for j in range (n):
theta = rand.uniform(0.0, np.pi)
phi = rand.uniform(0, (2 * np.pi))
x1[j] = 100 * np.sin(phi) * np.cos(theta)
y1[j] = 100 * np.sin(phi) * np.sin(theta)
z1[j] = 100 * np.cos(theta)
#ax.plot_wireframe([x0[k],x1[j]],[y0[k],y1[j]],[z0[k],z1[j]], color="g")
# count if hit target area
count = 0
for i in range (n):
if np.any([x1[i]<=X[:0]<=x0[i]]) and np.any([y1[i]<=X[:1]<=y0[i]]) and
np.any([z1[i]<=X[:2]<=z0[i]]):
count =+1
print (count)
plt.show()

How to keep axis labels from flip-flopping as azimuth scans past an axis direction (matplotlib)?

The GIF below is assembled from a series of 3D plots that scan from -100 to -80 degrees azimuth. As it passes through -90 the Y and Z axis labels flip-flop between the left and right sides of the plot.
Q: Is there a way to stop this distracting flip-flopping without removing them completely?
I suppose one way would be to force them to remain visible on both sides all the time, and that might be an improvement. I don't know how to do that so I can't make the comparison.
If there is some aesthetically better way to do it than duplicating on both sides all the time, that would be great. Perhaps fading?
note: I made the GIF using ImageJ, which requires the step of conversion of the images to 8-bit color. The light gray shading of the sidewalls disappears at that point. It's not a big concern to me but just mentioning it in case one is surprised to see it go.
def deriv(X, t):
x, y, z, xdot, ydot, zdot = X
r1 = np.sqrt((x + mu)**2 + y**2 + z**2)
r2 = np.sqrt((x - 1. + mu)**2 + y**2 + z**2)
term_1 = x + 2. * ydot
term_2 = -(1.-mu) * (x + mu) / r1**3
term_3 = -mu * (x - 1. + mu) / r2**3
xddot = term_1 + term_2 + term_3
term_1 = -2. * xdot
term_2 = 1. - (1.-mu)/r1**3 - mu/r2**3
yddot = term_1 + y * term_2
term_1 = (1. - mu)/r1**3 + mu/r2**3 # should be plus???
zddot = -z * term_1
return np.array([xdot, ydot, zdot, xddot, yddot, zddot])
class Sat(object):
def __init__(self, X0, T0, nu12):
self.X0 = X0
self.pos0 = X0[:3]
self.v0 = X0[3:]
self.T0 = T0
self.nu1, self.nu2 = nu12
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint as ODEint
from mpl_toolkits.mplot3d import Axes3D
# From "Three-Dimensional, Periodic 'Halo' Orbits,
# Kathleen Connor Howell, Celestial Mechanics 32 (1984) 53-71
pi, twopi = np.pi, 2*np.pi
mu = 0.04
# starting points:
x0 = [0.723268, 0.729988, 0.753700, 0.777413, 0.801125, 0.817724]
y0 = 6*[0.0]
z0 = [0.040000, 0.215589, 0.267595, 0.284268, 0.299382, 0.313788]
xdot0 = 6*[0.0]
ydot0 = [0.198019, 0.397259, 0.399909, 0.361870, 0.312474, 0.271306]
zdot0 = 6*[0.0]
X0s = np.array(zip(x0, y0, z0, xdot0, ydot0, zdot0))
Thalf0s = [1.300177, 1.348532, 1.211253, 1.101099, 1.017241, 0.978653]
T0s = [2.0*x for x in Thalf0s]
nu1s = [1181.69, 51.07839, 4.95816, 1.101843, 0.94834, 1.10361]
nu2s = [ 0.98095, -0.90203, -0.40587, -0.420200, -1.58429, -2.09182]
nu12s = zip(nu1s, nu2s)
n_half = 200
fractional_times = np.linspace(0.0, 1.0, 2*n_half+1)
rtol, atol = 1E-12, 1E-12
sats = []
for X0, T0, nu12 in zip(X0s, T0s, nu12s):
sat = Sat(X0, T0, nu12)
sat.n_half = n_half
sat.t = sat.T0 * fractional_times
sat.rtol, sat.atol = rtol, atol
sats.append(sat)
for sat in sats:
answer, info = ODEint(deriv, sat.X0, sat.t,
rtol=sat.rtol, atol=sat.atol,
full_output = True )
sat.answer = answer
sat.mid = answer[sat.n_half]
sat.info = info
if 1 == 1:
xL2, xL1 = 0.74091, 1.21643 # lazy!
fig = plt.figure(figsize=[10, 8])
ax = fig.add_subplot(1, 1, 1, projection='3d')
for sat in sats:
x, y, z = sat.answer.T[:3]
ax.plot(x, y, z)
ax.plot([0.0-mu], [0], [0], 'ob', markersize=20)
ax.plot([1.0-mu], [0], [0], 'og', markersize=12)
ax.plot([xL2], [0], [0], 'ok', markersize=8)
ax.plot([xL1], [0], [0], 'ok', markersize=8)
ax.set_xlim(0.7, 1.25)
ax.set_ylim(-0.225, 0.225)
ax.set_zlim(-0.15, 0.40)
ax.text(xL1, 0, -0.05, "L1", fontsize=14, horizontalalignment='center')
ax.text(xL2, 0, -0.05, "L2", fontsize=14, horizontalalignment='center')
nplot = 80
thetas = np.linspace(0, twopi, nplot+1)[:-1]
azimuths = -90 + 10.0 * np.cos(thetas)
fnames = []
for i, azim in enumerate(azimuths):
fname = "haloz_3D_" + str(10000+i)[1:]
ax.elev, ax.azim = 0, azim
plt.savefig(fname)
fnames.append(fname)
# tight cropping
for i in range(len(fnames)):
fname_in = "haloz_3D_" + str(10000+i)[1:]
fname_out = "haloz_3D_crop_" + str(10000+i)[1:] + ".png"
img = plt.imread(fname_in + ".png")
plt.imsave(fname_out, img[200:-175, 240:-190])

Plot graph with 4 equations consist of f(x,y, xy)

I'd like to plot a graph which contains 4 functions as shown below:
xy1: - 12.8 x - 0.108 y + xy >= -1.3824
xy2: - 40 x - 5 y + xy >= -200
xy3: - 40 x - 0.108 y + xy <= -4.32
xy4: - 12.8 x - 5 y + xy <= -64
I started off by generating data for x and y
import numpy as np
x = np.linspace(0, 5, 100)
y = np.linspace(0, 40, 100)
But then when I tried to plot the graph, I started to confuse about how should I reformulate the equations so that it is appropriate value for x, y, xy?
import matplotlib.pyplot as plt
plt.plot((-1.3824 + (12.8 * x) + (0.108 * y)), y) <--- this doesn't seems to be right
plt.show()
You can use contour for implicit function plotting. By the way I don't think you can plot multiple functions with inequality on the same graph because you have to show the ranges in the 2D plane, and they overlap each other.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 5, 100)
y = np.linspace(0, 40, 100)
X, Y = np.meshgrid(x, y)
XY1 = (-12.8*X - 0.108*Y + X*Y) >= -1.3824
XY2 = - 40*X - 5*Y + X*Y >= -200
XY3 = - 40*X - 0.108*y + X*Y <= -4.32
XY4 = - 12.8*X - 5*Y + X*Y <= -64
plt.contour(X,Y, XY1)
plt.contour(X,Y, XY2)
plt.contour(X,Y, XY3)
plt.contour(X,Y, XY4)
plt.show()
================================================================
UPDATE:
Seems like sympy's plot_implicit works so much better. You may need to install it.
from sympy import plot_implicit, symbols, And
x, y = symbols('x y')
p1 = plot_implicit(And(-12.8*x - 0.108*y + x*y >= -1.3824), (x,0,5), (y,0,40))
p2 = plot_implicit(And(-40*x - 5*y + x*y >= -200), (x,0,5), (y,0,40))
p3 = plot_implicit(And(-40*x - 0.108*y + x*y <= -4.32), (x,0,5), (y,0,40))
p4 = plot_implicit(And(-12.8*x - 5*y + x*y <= -64), (x,0,5), (y,0,40))
Results:
you can use contourf:
import pylab as pl
import numpy as np
x, y = np.mgrid[-10:10:100j, -10:50:100j]
z1 = - 12.8 * x - 0.108 * y + x * y + 1.3824
z2 = - 40 * x - 5 * y + x * y + 200
z3 = - 40 * x - 0.108 * y + x * y + 4.32
z4 = - 12.8 * x - 5 * y + x * y + 64
fig, axes = pl.subplots(2, 2, figsize=(12, 8))
axes = axes.ravel()
axes[0].contourf(x, y, z1, levels=[0, 1e10], alpha=0.2, colors=["blue"])
axes[1].contourf(x, y, z2, levels=[0, 1e10], alpha=0.2, colors=["green"])
axes[2].contourf(x, y, z3, levels=[-1e10, 0], alpha=0.2, colors=["red"])
axes[3].contourf(x, y, z4, levels=[-1e10, 0], alpha=0.2, colors=["yellow"])
Here is the output:

Categories