coordinates of 3D contours in python - python

Thanks to the knowledge acquired on this website. I am able to write a simple script that prints the coordinate (x and y) of a contour plot.
Here is an example:
from numpy import *
from pylab import *
# generate a set of random points
npts = 500
phi = random(npts)*2*pi
theta = random(npts)*pi
u = random(npts)
x = u * sin( theta) * cos( phi )
y = u * sin( theta) * sin( phi )
z = u * cos( theta )
bins = linspace(-1,1,100)
H = histogram2d(x,y,bins=(bins,bins))
fig=figure()
ax1 = fig.add_subplot(1,1,1)
levels = [1,2,3]
CS = ax1.contour(H[0].T,levels=levels)
show()
# print contours coordinates
for kk in range(len(levels)):
print CS.allsegs[kk][0][:,0],CS.allsegs[kk][0][:,1]
I would like to do the same, but in 3D but I'm struggling.
The first step to do is to bin the data, which I can do with:
H = histogramdd((x,y,z),bins=(bins,bins,bins))
But now, how can I get the coordinates of a contour in 3D? I can't even come up with a simple algorithm for that.

Related

Plotting a 3-dimensional superball shape in matplotlib

I'm trying to plot a 3D superball in python matplotlib, where a superball is defined as a general mathematical shape that can be used to describe rounded cubes using a shape parameter p, where for p = 1 the shape is equal to that of a sphere.
This paper claims that the superball is defined by using modified spherical coordinates with:
x = r*cos(u)**1/p * sin(v)**1/p
y = r*cos(u)**1/p * sin(v)**1/p
z = r*cos(v)**1/p
with u = phi and v = theta.
I managed to get the code running, at least for p = 1 which generates a sphere - exactly as it should do:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
r, p = 1, 1
# Make data
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
u, v = np.meshgrid(u, v)
x = r * np.cos(u)**(1/p) * np.sin(v)**(1/p)
y = r * np.sin(u)**(1/p) * np.sin(v)**(1/p)
z = r * np.cos(v)**(1/p)
# Plot the surface
ax.plot_surface(x, y, z)
plt.show()
This is a 3D plot of the code above for p = 1.
However, as I put in any other value for p, e.g. 2, it's giving me only a partial shape, while it should actually give me a full superball.
This is a 3D plot of the code above for p = 2.
I believe the fix is more of mathematical nature, but how can this be fixed?
When plotting a regular sphere, we transform positive and negative coordinates differently:
Positives: x**0.5
Negatives: -1 * abs(x)**0.5
For the superball variants, apply the same logic using np.sign and np.abs:
power = lambda base, exp: np.sign(base) * np.abs(base)**exp
x = r * power(np.cos(u), 1/p) * power(np.sin(v), 1/p)
y = r * power(np.sin(u), 1/p) * power(np.sin(v), 1/p)
z = r * power(np.cos(v), 1/p)
Full example for p = 4:
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
r, p = 1, 4
# Make the data
u = np.linspace(0, 2 * np.pi)
v = np.linspace(0, np.pi)
u, v = np.meshgrid(u, v)
# Transform the coordinates
# Positives: base**exp
# Negatives: -abs(base)**exp
power = lambda base, exp: np.sign(base) * np.abs(base)**exp
x = r * power(np.cos(u), 1/p) * power(np.sin(v), 1/p)
y = r * power(np.sin(u), 1/p) * power(np.sin(v), 1/p)
z = r * power(np.cos(v), 1/p)
# Plot the surface
ax.plot_surface(x, y, z)
plt.show()

How to Create 3D Torus from Circle Revolved about x=2r, r is the radius of circle (Python or JULIA)

I need help to create a torus out of a circle by revolving it about x=2r, r is the radius of the circle.
I am open to either JULIA code or Python code. Whichever that can solve my problem the most efficient.
I have Julia code to plot circle and the x=2r as the axis of revolution.
using Plots, LaTeXStrings, Plots.PlotMeasures
gr()
θ = 0:0.1:2.1π
x = 0 .+ 2cos.(θ)
y = 0 .+ 2sin.(θ)
plot(x, y, label=L"x^{2} + y^{2} = a^{2}",
framestyle=:zerolines, legend=:outertop)
plot!([4], seriestype="vline", color=:green, label="x=2a")
I want to create a torus out of it, but unable, meanwhile I have solid of revolution Python code like this:
# Calculate the surface area of y = sqrt(r^2 - x^2)
# revolved about the x-axis
import matplotlib.pyplot as plt
import numpy as np
import sympy as sy
x = sy.Symbol("x", nonnegative=True)
r = sy.Symbol("r", nonnegative=True)
def f(x):
return sy.sqrt(r**2 - x**2)
def fd(x):
return sy.simplify(sy.diff(f(x), x))
def f2(x):
return sy.sqrt((1 + (fd(x)**2)))
def vx(x):
return 2*sy.pi*(f(x)*sy.sqrt(1 + (fd(x) ** 2)))
vxi = sy.Integral(vx(x), (x, -r, r))
vxf = vxi.simplify().doit()
vxn = vxf.evalf()
n = 100
fig = plt.figure(figsize=(14, 7))
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222, projection='3d')
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224, projection='3d')
# 1 is the starting point. The first 3 is the end point.
# The last 200 is the number of discretization points.
# help(np.linspace) to read its documentation.
x = np.linspace(1, 3, 200)
# Plot the circle
y = np.sqrt(2 ** 2 - x ** 2)
t = np.linspace(0, np.pi * 2, n)
xn = np.outer(x, np.cos(t))
yn = np.outer(x, np.sin(t))
zn = np.zeros_like(xn)
for i in range(len(x)):
zn[i:i + 1, :] = np.full_like(zn[0, :], y[i])
ax1.plot(x, y)
ax1.set_title("$f(x)$")
ax2.plot_surface(xn, yn, zn)
ax2.set_title("$f(x)$: Revolution around $y$")
# find the inverse of the function
y_inverse = x
x_inverse = np.power(2 ** 2 - y_inverse ** 2, 1 / 2)
xn_inverse = np.outer(x_inverse, np.cos(t))
yn_inverse = np.outer(x_inverse, np.sin(t))
zn_inverse = np.zeros_like(xn_inverse)
for i in range(len(x_inverse)):
zn_inverse[i:i + 1, :] = np.full_like(zn_inverse[0, :], y_inverse[i])
ax3.plot(x_inverse, y_inverse)
ax3.set_title("Inverse of $f(x)$")
ax4.plot_surface(xn_inverse, yn_inverse, zn_inverse)
ax4.set_title("$f(x)$: Revolution around $x$ \n Surface Area = {}".format(vxn))
plt.tight_layout()
plt.show()
Here is a way that actually allows rotating any figure in the XY plane around the Y axis.
"""
Rotation of a figure in the XY plane about the Y axis:
ϕ = angle of rotation
z' = z * cos(ϕ) - x * sin(ϕ)
x' = z * sin(ϕ) + x * cos(ϕ)
y' = y
"""
using Plots
# OP definition of the circle, but we put center at x, y of 4, 0
# for the torus, otherwise we get a bit of a sphere
θ = 0:0.1:2.1π
x = 4 .+ 2cos.(θ) # center at (s, 0, 0)
y = 0 .+ 2sin.(θ)
# add the original z values as 0
z = zeros(length(x))
plot(x, y, z, color=:red)
# add the rotation axis
ϕ = 0:0.1:π/2 # for full torus use 2π at stop of range
xprime, yprime, zprime = Float64[], Float64[], Float64[]
for a in ϕ, i in eachindex(θ)
push!(zprime, z[i] + z[i] * cos(a) - x[i] * sin(a))
push!(xprime, z[i] * sin(a) + x[i] * cos(a))
push!(yprime, y[i])
end
plot!(xprime, yprime, zprime, alpha=0.3, color=:green)
Here is a way using the Meshes package for the construction of the mesh and the MeshViz package for the visualization. You'll just have to translate to fulfill your desiderata.
using Meshes
using MeshViz
using LinearAlgebra
using GLMakie
# revolution of the polygon defined by (x,y) around the z-axis
# x and y have the same length
function revolution(x, y, n)
u_ = LinRange(0, 2*pi, n+1)[1:n]
j_ = 1:(length(x) - 1) # subtract 1 because of periodicity
function f(u, j)
return [x[j] * sin(u), x[j] * cos(u), y[j]]
end
points = [f(u, j) for u in u_ for j in j_]
topo = GridTopology((length(j_), n), (true, true))
return SimpleMesh(Meshes.Point.(points), topo)
end
# define the section to be rotated: a circle
R = 3 # major radius
r = 1 # minor radius
ntheta = 100
theta_ = LinRange(0, 2*pi, ntheta)
x = [R + r*cos(theta) for theta in theta_]
y = [r*sin(theta) for theta in theta_]
# make mesh
mesh = revolution(x, y, 100)
# visualize mesh
viz(mesh)
EDIT: animation
using Meshes
using MeshViz
using LinearAlgebra
using GLMakie
using Makie
using Printf
function revolutionTorus(R, r, alpha; n1=30, n2=90)
theta_ = LinRange(0, 2, n1+1)[1:n1]
x = [R + r*cospi(theta) for theta in theta_]
y = [r*sinpi(theta) for theta in theta_]
full = alpha == 2
u_ = LinRange(0, alpha, n2 + full)[1:n2]
function f(u, j)
return [x[j] * sinpi(u), x[j] * cospi(u), y[j]]
end
points = [f(u, j) for u in u_ for j in 1:n1]
topo = GridTopology((n1, n2 - !full), (true, full))
return SimpleMesh(Meshes.Point.(points), topo)
end
# generates `nframes` meshes for alpha = 0 -> 2 (alpha is a multiple of pi)
R = 3
r = 1
nframes = 10
alpha_ = LinRange(0, 2, nframes+1)[2:(nframes+1)]
meshes = [revolutionTorus(R, r, alpha) for alpha in alpha_]
# draw and save the frames in a loop
for i in 1:nframes
# make a bounding box in order that all frames have the same aspect
fig, ax, plt =
viz(Meshes.Box(Meshes.Point(-4.5, -4.5, -2.5), Meshes.Point(4.5, 4.5, 2.5)); alpha = 0)
ax.show_axis = false
viz!(meshes[i])
scale!(ax.scene, 1.8, 1.8, 1.8)
png = #sprintf "revolutionTorus%02d.png" i
Makie.save(png, fig)
end
# make GIF with ImageMagick
comm = #cmd "convert -delay 1x2 'revolutionTorus*.png' revolutionTorus.gif"
run(comm)

Visualization of isolines in Matplotib on specific shape of mesh

I need some help with visualization in Matplotlib. I'm doing some Computational Fluid Dynamics and I have this kinda mesh, in which you can see the circular shape of floor. I've run some computations and from the data I gathered, I got this image of isolines
of Mach number. As you can see, the curved floor isn't present. How can I plot the isolines in the specified curved shape of the mesh?
This is my code:
mach = np.ndarray((nx, ny))
x = np.empty([nx])
y = np.empty([ny])
for index, cell in np.ndenumerate(mach):
p = (kappa - 1) * (w[index][3] - 0.5 *
(w[index][1] * w[index][1] + w[index][2] * w[index][2])
/ w[index][0])
v = np.sqrt((w[index][1] / w[index][0]) * (w[index][1] / w[index][0]) + (w[index][2] / w[index][0]) * (
w[index][2] / w[index][0]))
a = np.sqrt(kappa * p / w[index][0])
mach[index] = v / a
x[index[0]] = Mesh[index][0]
y[index[1]] = Mesh[index][1]
plt.figure(1)
X, Y = np.meshgrid(x, y, indexing='ij')
matplotlib.pyplot.contour(X, Y, mach, 50, cmap='turbo')
plt.show()
Thank you for your answers!

Draw shaded region within great circle distance from specified point with Matplotlib Basemap

I want to use Python/Matplotlib/Basemap to draw a map and shade a circle that lies within a given distance of a specified point, similar to this (Map generated by the Great Circle Mapper - copyright © Karl L. Swartz.):
I can get the map to generate as follows:
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt
# create new figure, axes instances.
fig,ax = plt.subplots()
# setup Mercator map projection.
m = Basemap(
llcrnrlat=47.0,
llcrnrlon=-126.62,
urcrnrlat=50.60,
urcrnrlon=-119.78,
rsphere=(6378137.00,6356752.3142),
resolution='i',
projection='merc',
lat_0=49.290,
lon_0=-123.117,
)
# Latitudes and longitudes of locations of interest
coords = dict()
coords['SEA'] = [47.450, -122.309]
# Plot markers and labels on map
for key in coords:
lon, lat = coords[key]
x,y = m(lat, lon)
m.plot(x, y, 'bo', markersize=5)
plt.text(x+10000, y+5000, key, color='k')
# Draw in coastlines
m.drawcoastlines()
m.fillcontinents()
m.fillcontinents(color='grey',lake_color='aqua')
m.drawmapboundary(fill_color='aqua')
plt.show()
which generates the map:
Now I would like to create a great circle around a specified point, such as the top map.
My attempt is a function that takes the map object, a center coordinate pair and a distance, and creates two curves and then shade between them, something like:
def shaded_great_circle(map_, lat_0, lon_0, dist=100, alpha=0.2): # dist specified in nautical miles
dist = dist * 1852 # Convert distance to nautical miles
lat = np.linspace(lat_0-dist/2, lat_0+dist/2,50)
lon = # Somehow find these points
# Create curve for longitudes above lon_0
# Create curve for longitudes below lon_0
# Shade region between above two curves
where I have commented what I want to do, but am not sure how to do it.
I have tried a few ways to do this, but what has me confused is that all inputs to the map are coordinates measured in degrees, whereas I want to specify points in length, and have that converted to latitude/longitude points to plot. I think this is related to data as lat/lon in degrees versus map projection coordinates.
Any nudges in the right direction would be appreciated
Thanks
In the end I had to implement this manually.
In short, I used an equation given here to calculate the coordinates given an
initial starting point and a radial to calculate points around 360 degrees, and then plot a line through these points. I don't really need the shading part, so I haven't implemented that yet.
I thought this is a useful feature so here is how I implemented it:
from mpl_toolkits.basemap import Basemap
import numpy as np
import matplotlib.pyplot as plt
def calc_new_coord(lat1, lon1, rad, dist):
"""
Calculate coordinate pair given starting point, radial and distance
Method from: http://www.geomidpoint.com/destination/calculation.html
"""
flat = 298.257223563
a = 2 * 6378137.00
b = 2 * 6356752.3142
# Calculate the destination point using Vincenty's formula
f = 1 / flat
sb = np.sin(rad)
cb = np.cos(rad)
tu1 = (1 - f) * np.tan(lat1)
cu1 = 1 / np.sqrt((1 + tu1*tu1))
su1 = tu1 * cu1
s2 = np.arctan2(tu1, cb)
sa = cu1 * sb
csa = 1 - sa * sa
us = csa * (a * a - b * b) / (b * b)
A = 1 + us / 16384 * (4096 + us * (-768 + us * (320 - 175 * us)))
B = us / 1024 * (256 + us * (-128 + us * (74 - 47 * us)))
s1 = dist / (b * A)
s1p = 2 * np.pi
while (abs(s1 - s1p) > 1e-12):
cs1m = np.cos(2 * s2 + s1)
ss1 = np.sin(s1)
cs1 = np.cos(s1)
ds1 = B * ss1 * (cs1m + B / 4 * (cs1 * (- 1 + 2 * cs1m * cs1m) - B / 6 * \
cs1m * (- 3 + 4 * ss1 * ss1) * (-3 + 4 * cs1m * cs1m)))
s1p = s1
s1 = dist / (b * A) + ds1
t = su1 * ss1 - cu1 * cs1 * cb
lat2 = np.arctan2(su1 * cs1 + cu1 * ss1 * cb, (1 - f) * np.sqrt(sa * sa + t * t))
l2 = np.arctan2(ss1 * sb, cu1 * cs1 - su1 * ss1 * cb)
c = f / 16 * csa * (4 + f * (4 - 3 * csa))
l = l2 - (1 - c) * f * sa * (s1 + c * ss1 * (cs1m + c * cs1 * (-1 + 2 * cs1m * cs1m)))
d = np.arctan2(sa, -t)
finaltc = d + 2 * np.pi
backtc = d + np.pi
lon2 = lon1 + l
return (np.rad2deg(lat2), np.rad2deg(lon2))
def shaded_great_circle(m, lat_0, lon_0, dist=100, alpha=0.2, col='k'): # dist specified in nautical miles
dist = dist * 1852 # Convert distance to nautical miles
theta_arr = np.linspace(0, np.deg2rad(360), 100)
lat_0 = np.deg2rad(lat_0)
lon_0 = np.deg2rad(lon_0)
coords_new = []
for theta in theta_arr:
coords_new.append(calc_new_coord(lat_0, lon_0, theta, dist))
lat = [item[0] for item in coords_new]
lon = [item[1] for item in coords_new]
x, y = m(lon, lat)
m.plot(x, y, col)
# setup Mercator map projection.
m = Basemap(
llcrnrlat=45.0,
llcrnrlon=-126.62,
urcrnrlat=50.60,
urcrnrlon=-119.78,
rsphere=(6378137.00,6356752.3142),
resolution='i',
projection='merc',
lat_0=49.290,
lon_0=-123.117,
)
# Latitudes and longitudes of locations of interest
coords = dict()
coords['SEA'] = [47.450, -122.309]
# Plot markers and labels on map
for key in coords:
lon, lat = coords[key]
x,y = m(lat, lon)
m.plot(x, y, 'bo', markersize=5)
plt.text(x+10000, y+5000, key, color='k')
# Draw in coastlines
m.drawcoastlines()
m.fillcontinents()
m.fillcontinents(color='grey',lake_color='aqua')
m.drawmapboundary(fill_color='aqua')
# Draw great circle
shaded_great_circle(m, 47.450, -122.309, 100, col='k') # Distance specified in nautical miles, i.e. 100 nmi in this case
plt.show()
Running this should give you (with 100 nautical mile circle around Seattle):

Random initial velocities within Plummer model in Python

I want to randomly distribute N particles within a volume such that they satisfy the Plummer potential distribution. I trying to work from "The Art of Computational Science" by Hut, which has a description but I can't seem to implement it. Where I differ from Hut is that I require 3 velocity components for each particle. Here's what I have done so far:
f=0
g=0.1
if g >f*f*(1-f*f)**3.5:
f=np.random.uniform(0,1,N)
g=np.random.uniform(0,0.1,N)
vel_x= f*np.sqrt(2)*(1+x*x)**(-0.25)
vel_y= f*np.sqrt(2)*(1+y*y)**(-0.25)
vel_z= f*np.sqrt(2)*(1+z*z)**(-0.25)
vel = np.zeros((N,3))
vel[:,0]=vel_x
vel[:,1]=vel_y
vel[:,2]=vel_z
However, when I run the energy check described by Hut, such that the kinetic energy ~0.147 in N body units, this code fails. Any advice on where Im going wrong would be greatly appreciated
You are probably misreading the Ruby code in Hut's book since it is also generating 3-dimensional velocity vectors:
x = 0.0
y = 0.1
while y > x*x*(1.0-x*x)**3.5
x = frand(0,1)
y = frand(0,0.1)
end
velocity = x * sqrt(2.0) * ( 1.0 + radius*radius)**(-0.25)
theta = acos(frand(-1, 1))
phi = frand(0, 2*PI)
b.vel[0] = velocity * sin( theta ) * cos( phi )
b.vel[1] = velocity * sin( theta ) * sin( phi )
b.vel[2] = velocity * cos( theta )
The first part generates |v| by rejection sampling from the velocity distribution. The second part generates a random direction in space (in polar coordinates) and the last part of the code transforms from polar to Cartesian coordinates.
Your code does something completely different. You should instead adapt the code fragment shown above in Python, e.g.:
f = 0.0
g = 0.1
while g > f*f*(1.0-f*f)**3.5:
f = np.random.uniform(0,1)
g = np.random.uniform(0,0.1)
velocity = f * np.sqrt(2.0) * (1.0 + radius*radius)**(-0.25)
theta = np.arccos(np.random.uniform(-1, 1))
phi = np.random.uniform(0, 2*np.pi)
vel[n,0] = velocity * np.sin(theta) * np.cos(phi)
vel[n,1] = velocity * np.sin(theta) * np.sin(phi)
vel[n,2] = velocity * np.cos(theta)
The code could possibly be vectorised, but in reality it makes little sense since the rejection sampling is not vectorisable (it might and most likely will take different number of iterations for each sample).

Categories