Creating a Simulation of Equipotencial Surfaces in Python with numpy/matplotlib - python

I have a 3D array where I added the potential values for each coordinate here:
z = y = x = size # size of each side of the cube
potential = np.zeros((z, y, x))
exemple: I set a potential to a point -> potential[3,3,3] = 10 now this coord has a value of 10. And the surrounding points will have a potential based on the mean of the surrounding points.
such as:
for z in range(1,size):
for y in range(1,size):
for x in range(1,size):
if [z,y,x] in polop:
potential[z,y,x] = Positive # If positive polo, keeps potential
elif [z,y,x] in polon:
potential[z,y,x] = Negative # If negative polo, keeps potential
elif z!=size-1 and y!=size-1 and x!=size-1: # Sets the potential to the mean potential of neighbors
potential[z][y][x] = (potential[z][y][x+1] + potential[z][y][x-1] + potential[z][y+1][x] +
potential[z][y-1][x] + potential[z+1][y][x] + potential[z-1][y][x]) / 6
Then I plotted this array and it looks like this:
Now what I want is to have surfaces instead of scattering points. A surface consisting of the points that have more less the same potential (let's say from 2 in 2, as 10->8, 8->6, 6->4, 4->2, 2->0, 0->-2, etc.)
Is it possible?
Plus, this was supposed to be an ellipsoid instead of a cube, the poles were supposed to be spheres instead of cubes and this needs to be updating like each second.
Please help!

Related

Place points in a normal distribution

I need to place points distributed normally between 0 and an r max which is 0.00174.
I want to have the points x coordinate to be smaller at the beginning and larger at the end.
My code makes circles that have different radii till rmax and places them evenly (in this case 10 points per circle) around the origin. The r values are linear right now but I want the radii to increase exponentially where the points are closer together in beginning and farther apart at the end.
points = 120
rmax = 0.001745
ppa = 10 #number of points per annulie
valuesr = np.linspace(0.00001,rmax,int(points/ppa))
pirange = np.linspace(0.00001,2*np.pi,int(ppa))
x = np.array([])
y = np.array([])
for r in valuesr:
for theta in pirange:
x=np.append(x,r*np.cos(theta))
y=np.append(y,r*np.sin(theta))
plt.scatter(x,y,marker=".",lw=0.01)
If I understood correctly, you can do this by using np.logspace; So, by changing valuesr to:
valuesr = np.logspace(np.log(0.00001), np.log(rmax), int(points / ppa), base=np.exp(1))
The resulted image will be as:

random numer generation except inside of the circle in python

I want to generate 750 random number in 2D data x (0,1) and y (0,1)
X1 = np.random.random((750,2))
However, I want to make sure I don't have any value in the circular region such as
I can remove the value, but I want to fix the number of the random number to be 750. What would be the best way to generate such list?
import random
points = []
radius = 1
num_points=1500
index = 0
max_coord = 750
while index < num_points:
x=random.random()*max_coord
y=random.random()*max_coord
if x**2 + y**2 > radius**2:
points = points + [[x,y]]
index = index + 1
print(points)
Change max_coor to obtain bigger numbers. This only gives you 1500 points outside the circle of the given radius=1. Each coordinate varies between 0 and 750
you can just apply pythagorean's theorem. Here is my code to do that
import numpy as np
import matplotlib.pyplot as plt
dist = 0.2
X1 = np.random.random((750,2))
X2 = [x for x in X1 if (x[0]-0.5)**2 + (x[1]-0.5)**2 > dist**2] # filter values that are to close to center
# for plotting
x = [x[0] for x in X2]
y = [y[1] for y in X2]
plt.scatter(x,y)
plt.show()
You can do this with a well-known technique called rejection sampling. Generate candidate values and see if they meet your constraints. If so, accept them, otherwise reject and repeat. With 2-d sampling, the probability of acceptance on a given trial is P{Accept} = Area(acceptance region) / Area(generating region). Repetitions due to rejection are independent, so the number of trials has a geometric distribution with parameter p = P{Accept}
and Expected # trials = 1 / P{Accept}. For example, if the rejection region is a circle with radius 1/2 centered in a unit square, P{Accept} = (4 - Pi)/4 andon average it will take 4/(4 - Pi) (approximately 4.66) attempts per value generated. Obviously this won't work if the radius is >= sqrt(2)/2, and gets expensive as you approach that limit.
Note that the rejection circle does not need to be centered in the square, you just reject any candidate that is less than radius away from the circle's center.
Here's the code:
import numpy as np
def generate_pt(exclusion_radius):
while True:
candidate = np.random.sample(2)
# The following assumes the rejection circle is centered at (0.5, 0.5).
# Adjust by subtracting individual x/y coordinates for the center of
# the circle if you don't want it centered. Acceptance/rejection
# distance is determined by Pythagorean theorem.
if np.sum(np.square(candidate - 0.5)) >= exclusion_radius * exclusion_radius:
return candidate
# Generate 750 points outside the centered circle of radius 0.3.
data = np.array([generate_pt(0.3) for _ in range(750)])

How do I create a binary mask on a square grid from a 2D cloud of points in Python?

I have a the X and Y coordinates of a 2D cloud of points that I want to map onto a 2D uniform grid with a resolution of imageResolution of initially all zeros. I want all pixels in the grid which overlay the 2D cloud of points to contain ones, to produce a binary image.
Please note, there are a very large number of points both in my 2D cloud of points and in the uniform grid, and so loops are not an effective solution here.
I have looked at convex hulls but my points are not necessarily in a convex set.
I have tried this following code, but its not giving me the correct binary map, since its only assigning 1s to the nearest grid points closest to the points in the point cloud (see image below):
X = points[:,0] #1D array of X coordinates
Y = points[:,1] #1D array of Y coordinates
imageResolution = 256
xVec = np.linspace(0,800,imageResolution)
yVec = xVec
def find_index(x,y):
xi=np.searchsorted(xVec,x)
yi=np.searchsorted(yVec,y)
return xi,yi
xIndex, yIndex = find_index(X,Y)
binaryMap = np.zeros((imageResolution,imageResolution))
binaryMap[xIndex,yIndex] = 1
fig = plt.figure(1)
plt.imshow(binaryMap, cmap='jet')
plt.colorbar()
Please see this image which shows my 2D cloud of points, the desired binary map I want, and the current binary map I am getting from the code above. Please note the red pixels are difficult to see in the last image.
How do I create a binary mask on a square grid from a 2D cloud of points in Python?
Thank you
DISCLAIMER :: Untested suggestion
If I've understood correctly, rather than mark an individual pixel with 1, you should be marking a neighborhood of pixels.
You could try inserting the following lines just before binaryMap[xIndex,yIndex] = 1:
DELTA_UPPER=2 # Param. Needs fine-tuning
delta = np.arange(DELTA_UPPER).reshape(-1,1)
xIndex = xIndex + delta
xIndex [xIndex >= imageResolution] = imageResolution-1
yIndex = yIndex + delta
yIndex [yIndex >= imageResolution] = imageResolution-1
x_many, y_many = np.broadcast_arrays (xIndex[:,None], yIndex)
xIndex = x_many.reshape(-1)
yIndex = y_many.reshape(-1)
Note:
DELTA_UPPER is a parameter that you will have to fine-tune by playing around with. (Maybe start with DELTA_UPPER=3)
UNTESTED CODE
Based on further clarifications, posting this second answer, to better index the binaryMap, given that points contains floats.
imageResoluton = 256
MAX_X = # Fill in here the max x value ever possible in `points`
MIN_X = # Fill in here the min x value ever possible in `points`
MAX_Y = # Fill in here the max y value ever possible in `points`
MIN_Y = # Fill in here the min y value ever possible in `points`
SCALE_FAC = imageResolution / max(MAX_X-MIN_X, MAX_Y-MIN_Y)
X = np.around(SCALE_FAC * points[:,0]).astype(np.int64)
Y = np.around(SCALE_FAC * points[:,1]).astype(np.int64)
X [X >= imageResolution] = imageResolution-1
Y [Y >= imageResolution] = imageResolution-1
binaryMap[X, Y] = 1
(There's no need for find_index())

Volume of 3-Space Object using 3-Space co-ordinates

I am rather new to Python and I think I'm trying to attempt something quite complicated? I have repeatedly tried to search for this and think I'm missing some base knowledge as I truly don't understand what I've read.
Effectively I have 3 arrays, which contain x points, y points, and z points. They create an approximately hemispherical shape. I have these points from taking a contour of a 2D axisymmetrical semicircular shape, extracting the x points and y points from it into separate arrays, and creating a "z points" array of zeros of the same length as the previous two arrays.
From this, I then rotate the y points into the z-domain using angles to create a 3D approximation of the 2D contour.
This 3D shape is completely bounded (in that it creates both the bottom and the top), as linked below:
3d Approximation
I've butchered the following code from my much longer program, it's the only part that is pertinent to my question however:
c, contours, _ = cv2.findContours(WB, mode = cv2.RETR_EXTERNAL, method = cv2.CHAIN_APPROX_NONE) # Find new contours in recently bisected mask
contours = sorted (contours, key = cv2.contourArea, reverse = True) # # Sort contours (there is only 2 contours left, plinth/background, and droplet)
contour = contours[1] # Second longest contour is the desired contour
xpointsList = [xpoints[0][0] for xpoints in contour] # Create new array of all x co-ordinates
ypointsList = [ypoints[0][1] for ypoints in contour] # Create new array of all y co-ordinates
zpointsList = np.zeros(len(xpointsList)) # # Create an array of 0 values to represent the z domain. True contour sits at 0 on all points in the z-domain
angles = np.arange(0,180,5, dtype = int) # # Creating an array of angles between to form a full drop in degrees of 5
i = 0
b = 0
d = 0
qx = np.zeros(len(xpointsList)*len(angles)) # Setting variable to equal the length of x points times the length of iterative angle calculations
qy = np.zeros(len(ypointsList)*len(angles)) # Setting variable to equal the length of x points times the length of iterative angle calculations
qz = np.zeros(len(zpointsList)*len(angles)) # Setting variable to equal the length of x points times the length of iterative angle calculations
ax = plt.axes(projection='3d')
for b in range(0,len(angles)):
angle = angles[b]
for i in range(0,len(ypointsList)):
qx[i+d] = xpointsList[i]-origin[0] # Setting the x-axis to equal it's current values around the origin
qz[i+d] = origin[0] + math.cos(math.radians(angle)) * (zpointsList[i]) - math.sin(math.radians(angle)) * (ypointsList[i] - origin[1]) # creating a z value based on 10 degree rotations of the y values around the x axis
qy[i+d] = origin[1] + math.sin(math.radians(angle)) * (zpointsList[i]) + math.cos(math.radians(angle)) * (ypointsList[i] - origin[1]) # adapting the y value based on the creation of the z values.
i = i + 1
b = b + 1
d = d + len (xpointsList)
ax.plot3D (qy, qz, qx, color = 'blue', linewidth = 0.1)
Effectively my question is, how do I use this structure to somehow find the volume using the co-ordinate arrays? I think I need to use some kind of scipy.spatial ConvexHull to fill in the areas between my rotations so that it has a surface and work from there?
I don't understand your approach (I'm not that good in Python) so can't help you fix it, but there was a similar question with much simpler solution here:
https://stackoverflow.com/a/48114604/1479630
Your situation is even easier because what you basically have is a distorted sphere, and you have a regular grid on its surface. You can iterate over all surface triangles and for each, compute volume of tetrahedron made of this triangle and center of object. If you sum up those volumes you'll get volume of entire object.

Assigning colour to data depending on sign of gradient (Python)

I have two columns of data, x and y. The y data takes the shape of the triangle wave below. As you can see, the triangle has 2 sections of positive gradient and 1 longer section with a negative gradient.
I would like to write a program that:
Queries whether the current entry in an vertical array has a positive or negative gradient with respect to the successive entry in the array.
Then, plots the y data against x, where y values with a positive gradient (and its respective x value) are plotted using one colour, and the negative points in another colour.
How is this best done in Python?
filen = 'filename.txt'
x = loadtxt(fn,unpack=True,usecols=[0])
y = loadtxt(fn,unpack=True,usecols=[1])
n = ma.masked_where(gradient(y) < 0, y)
p = ma.masked_where(gradient(y) > 0, y)
pylab.plot(x,n,'r',x,p,'g')
Does the trick for me!

Categories

Resources