So basically, I've created a plot in python which models two interacting populations on an island and shows the uses the diffusion equation to model the movement and change in the population in one dimension, and I'm trying to modify it so I have a two dimensional plot instead so it takes into account both the x and y position of the species on the island.
Apologies that the code is quite long, most of it is just defining parameters though- I'm not too sure how I create a two dimensional plot which has the density of the population in each position, this is my attempt below but the plot isn't correct at all. I think my numerical solution should be correct, I'm assuming it's it's my initial/boundary conditions and how I've set up my array for the probability distribution which is incorrect but I'm not sure how to change it. For example, one of the problems I have is that it has the Y position on the x axis - how can I fix this?
r1=0.1 # growth rate B
r2=0.3 # growth rate M
KB = 20 # carrying capacity B
KM = 6000 # carrying capacity M
x=np.arange(0,70) # distance from the coast (100m)
y=np.arange(0,90) # distance from the coast
dx=1 # change in x position
dy=1 # change in y position
dt=1 # time step
m=71 # number of x steps
o=91 # number of y steps
n=21 # time
alpha = 0.00002 # predation rate
beta = 0 # growth rate from predation (assumed to be zero)
B=np.zeros(shape=(m,o,n)) # species B
M=np.zeros(shape=(m,o,n)) # species M
D=0.35 # diffusivity of B
D2=0.05 # diffusivity of M
Alpha=(D*dt)/(dx*dx)
Beta=(D*dt)/(dy*dy)
Alpha2=(D2*dt)/(dx*dx)
Beta2 = (D2*dt)/(dy*dy)
M[0,:,0]=0 #initial conditions
M[m-1,:,0]=0
M[:,0,0]=0
M[:,m-1,0]=0
B[1:26,:,0]=0.96
B[26:44,:,0]=4.6
B[44:m-1,:,0]=0.96
B[:,1:26,0]=0.96
B[:,26:44,0]=4.6
B[:,44:m-1,0]=0.96
M[1:26,:,0]=2500
M[26:44,:,0]=1000
M[44:m-1,:,0]=2500
M[:,1:26,0]=2500
M[:,26:44,0]=1000
M[:,44:m-1,0]=2500
for k in range(0,n-1): # loop for time
B[0,:,k+1]=B[1,:,k+1] # boundary conditions
B[m-1,:,k+1]=B[m-2,:,k+1]
M[0,:,k+1]=0
M[m-1,:,k+1]=0
B[:,0,k+1]=B[:,1,k+1]
B[:,m-1,k+1]=B[:,m-2,k+1]
M[:,0,k+1]=0
M[:,m-1,k+1]=0
for i in range(1,m-1): # loop for x
for j in range(1,o-1): # loop for y
Bdxx=(-2*Alpha)*B[i,j,k]+Alpha*B[i+1,j,k]+Alpha*B[i-1,j,k]
Bdyy=(-2*Beta)*B[i,j,k]+Beta*B[i,j+1,k]+Beta*B[i,j-1,k]
B[i,j,k+1]=B[i,j,k]+Bdxx+Bdyy+r1*B[i,j,k]*(1-B[i,j,k]/KB)-alpha*M[i,j,k]*B[i,j,k] # numerical solution for B
Mdxx=(-2*Alpha2)*M[i,j,k]+Alpha2*M[i+1,j,k]+Alpha2*M[i-1,j,k]
Mdyy=(-2*Beta2)*M[i,j,k]+Beta2*M[i,j+1,k]+Beta2*M[i,j-1,k]
M[i,j,k+1]=M[i,j,k]+Mdxx+Mdyy+r2*M[i,j,k]*(1-M[i,j,k]/KM)+beta*M[i,j,k]*B[i,j,k] # numerical solution for M
plt.pcolormesh(B[:,:,n-1])
plt.colorbar()
plt.xlabel('X Position')
plt.ylabel('Y Position')
Any help is really appreciated, thanks!
edit: I've realised part of my problem is that the initial conditions for the y coordinates are wrong
Related
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)])
I have a signal from a magnetic detector that I'm interested in analyzing, I've made signal decomposition using wavedec()
coeffs = pywt.wavedec(dane_K180_40['CH1[uV]'], 'coif5', level=5)
And I've received decomposition coefficients as follows:
cA1, cD5, cD4, cD3, cD2, cD1 = coeffs
These are ndarrays objects with various lengths.
cD1 is (1519,) cD2 is (774,) and so on. Different length of arrays is my main obstacle.
coefficients
My question:
I have to make DWT Scaleogram and I can't stress it enough that I've tried my best and couldn't do it.
What is the best approach? Using matpllotlib's imshow() as follows:
plt.imshow(np.abs([cD5, cD4, cD3, cD2, cD1]), cmap='bone', interpolation='none', aspect='auto')
gives me an error
TypeError: Image data of dtype object cannot be converted to float
I've tried to google it since I'm not an expert in python and I've tried to change the ndarrays to float.
What is the best for plotting scaleogram, matshow, pcolormesh? ;D
Basically, each cDi array has half the amount of samples as the previous array (this is not the case for every mother wavelet!), so I create a 2D numpy array where the first element is the 'full' amount of samples, and for each subsequent level I repeat the samples 2^level times so that the end result is a rectangular block. You can pick whether you want the Y-axis plotted as a linear or as a logarithmic scale.
# Create signal
xc = np.linspace(0, t_n, num=N)
xd = np.linspace(0, t_n, num=32)
sig = np.sin(2*np.pi * 64 * xc[:32]) * (1 - xd)
composite_signal3 = np.concatenate([np.zeros(32), sig[:32], np.zeros(N-32-32)])
# Use the Daubechies wavelet
w = pywt.Wavelet('db1')
# Perform Wavelet transform up to log2(N) levels
lvls = ceil(log2(N))
coeffs = pywt.wavedec(composite_signal3, w, level=lvls)
# Each level of the WT will split the frequency band in two and apply a
# WT on the highest band. The lower band then gets split into two again,
# and a WT is applied on the higher band of that split. This repeats
# 'lvls' times.
#
# Since the amount of samples in each step decreases, we need to make
# sure that we repeat the samples 2^i times where i is the level so
# that at each level, we have the same amount of transformed samples
# as in the first level. This is only necessary because of plotting.
cc = np.abs(np.array([coeffs[-1]]))
for i in range(lvls - 1):
cc = np.concatenate(np.abs([cc, np.array([np.repeat(coeffs[lvls - 1 - i], pow(2, i + 1))])]))
plt.figure()
plt.xlabel('Time (s)')
plt.ylabel('Frequency (Hz)')
plt.title('Discrete Wavelet Transform')
# X-axis has a linear scale (time)
x = np.linspace(start=0, stop=1, num=N//2)
# Y-axis has a logarithmic scale (frequency)
y = np.logspace(start=lvls-1, stop=0, num=lvls, base=2)
X, Y = np.meshgrid(x, y)
plt.pcolormesh(X, Y, cc)
use_log_scale = False
if use_log_scale:
plt.yscale('log')
else:
yticks = [pow(2, i) for i in range(lvls)]
plt.yticks(yticks)
plt.tight_layout()
plt.show()
I am still fairly new to using the numpy and sympy library. Apologies if I have quite a few prints on there, I just wanted to check if the code was working.
I am trying to solve this 2D heat equation problem, and kind of struggling on understanding how I add the initial conditions (temperature of 30 degrees) and adding the homogeneous dirichlet boundary conditions: temperature to each of the sides of the plate (i.e. top,bottom, and the 2 sides). I have, as an example, tried to add the temperature for the top of the plate to be 1000 degrees, but I got an error "IndexError: index 18 is out of bounds for axis 0 with size 18 ".
I have managed to add the function onto the code and it seems to be outputting ok as well, just a bit lost on adding the conditions on..I have tried looking for similar questions online, but couldn't really understand what was happening..
Here is a link to the original 2D heat equation:https://drive.google.com/file/d/1q3KyfaHD7hZTbdIYc9_e5otusELqYJaD/view?usp=sharing
Here is the link to the final equation for explicit finite differencing: https://drive.google.com/file/d/1unJHXO3b3xig8RhBen5k0eOg40YUfjyv/view?usp=sharing
i;j are locations (node numbers) and k= time (time step number)
I am also trying to find the time to which the temperature reaches the steady state
I have added the whole code I have done so far
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import sympy as sp
from sympy.solvers import solve
from sympy import Function, Symbol
#Length of workpiece
Lx= 0.05 #Length of work piece in the x direction, measured in meters (m)
Nx= 18 #Number of mesh points along the x-direction
Ly= 0.1 #Length of work piece in the y direction, measured in meters (m)
Ny= 9 #Number of mesh points along the y-direction
dy=Lx/Nx #distance between nodes in x
dx=Ly/Ny #distance between nodes in x
#Defining material properties
conduc= 16.26 #Conductivity of the material in (W/m (degrees)C)
rho= 8020 #Density of the material in (kg/m^3)
c=502 #Specific heat of the material (J/kg(degrees)C)
#Calculating the alpha (thermal diffusivity) for the plate, in (m^2/s)
a=((conduc)/(rho*c))
print (a)
t=215;
nt= 215; #number of steps wrt time
dt= t/nt; #timestep
#Define the mesh in space
x= np.linspace(1,Lx, Nx)[np.newaxis] #vector to create mesh
y= np.linspace(1,Ly,Ny)[np.newaxis]
x,y=np.meshgrid(x,y) #mesh grid function return
print (x,y)
T = np.ones(([Nx,Ny]))
#Adding the other conditions
T[Nx,:]= 1000 #Assining the temp for the top
sym= sp.symbols('i, j, k')
function = sp.Function('T(i,j,k)+(dt*a(((T(i+1,j,k)-2*T(i,j,k)+T(i-1,j,k))/(dx**2))+(a*dt(((T(i,j+1,k)-2*T(i,k,k)+T(i,j-1,k))/dy**2)')
print(function)
Update: I have added this to the code and these seems to work. However, I noticed that my T = np.ones(([Nx,Ny])) is an 18 x 9,but the plate is longer on the horizontal side..Would this mean that I would just need to flip my matrix?
#Temperatures for the plate
Ttop=1000 # temp for the top side of the plate
Tbottom = 500 # temnp for the bottom side of the plate
Tright = 500 #temp for the right side of the plate
Tleft = 1000 #temp for the left side of the plate
#Adding the boundaries on the system
T[:, 0] = Ttop #temp for the top
print (T[:,0])
T[:, -1] = Tbottom #temp for bottom
print (T[:, -1])
T[0, :] = Tright #temp for the right
print (T[0, :])
T[-1, :] = Tleft #temp for the left
print (T[-1, :])
I think I have managed to sort it out and have checked the values after adding in the temperatures for each of the side from #chris's recommendation on looking at.https://scipython.com/book/chapter-7-matplotlib/examples/the-two-dimensional-diffusion-equation/
I am working on an assignment that is teaching how to plot and label using matplotlib using Python. Science or math is not my background. I have been given the formula for calculating the geostrophic wind and we are to plot it (on the y-axis) versus the latitude on the x-axis.
I know how to plot give an x and a y. Beyond that, the formula is not making sense to me given my lack of background in the area.
The formula is the geostrophic wind formula. Because all I have is an image and I need 10 rep to post an image, I'll just focus on the greek letters I am given.
For example, I am given
r'$x^{10}$'
r'$R_^{final}$'
r'$alpha^{\eta}$'
The first two are superscript and subscript. That I understand. But how this helps with the formula calculations I do not know.
I am given the values to put into the formula as well. An explanation of the order of operations would help.
g0=9.81 ms-‐2;
ΔZ=60m;
Δn=2x10^5m;
and
f=2Ωsin(φ)
My question is how do I put the values above into the formula and then plot them in matplotlib? is it as easy as x and y?
Example of plotting done so far:
x = arange(1, 100, 1)
y1 = 2.0*np.sqrt(x)
y2 = 3.0*x**(1.0/3.0)
plt.plot(x, y1)
plt.plot(x, y2)
Sorry, I'm new to this.
geostrophic wind formula
The physical explanation in jclark754's answer is good. Look at the wiki page on Geostrophic wind, too.
$\Delta n$ is, I assume, your northward distance. I call it dy below, for clarification. Also, it is a question whether you should take g to be negative (z-axis upward). I do so.
For the code, you need to be aware that np.sin expects radians rather than degrees.
And if you work with NumPy arrays rather than lists, you do not need all those list comprehensions and the formulation gets much simpler and closer to the formula:
import matplotlib.pyplot as plt ; plt.style.use('ggplot')
import numpy as np
# define the parameters
g = -9.81 # m/s^2
dZ = 60 # m
dx = 2e5 # m
omega = 7.2921e-5 # rad/s
phi = np.linspace(10,40) # deg
f = 2 * omega * np.sin(np.radians(phi)) # coriolis frequency, s^-1
# compute geostrophic wind, x-component
u_g = -1. * g/f * dZ/dx
# plot phi vs V_g
fig, ax = plt.subplots()
ax.plot(phi, u_g)
ax.set_xlabel('latitude (degrees)')
ax.set_ylabel('geostrophic wind, y-component (m/s)')
plt.show()
The plot shows the geostrophic wind resulting from a constant geostrophic height gradient (dZ/dx = 60 m / 2e5 m) and the Coriolis effect, at different latitudes.
From physical intuition, I find it strange that the velocity increases as you get closer to the equator, even though the Coriolis effect is strongest towards the poles. But then again, the Coriolis effect is not a force but more a balancing effect, obstructing the release of potential energy contained in the pressure gradient force.
So I believe the equation you're trying to show is the geostrophic wind equation:
Is that it? If so, it's one of the simpler equations in meteorology and I'd be happy to explain!
Vg is the geostrophic wind, it's a theoretical wind that results from a balance between the Coriolis effect and the pressure gradient force. It's an idealized wind that doesn't really exist in nature.
g0 and f are gravity and the Coriolis parameter. The Coriolis parameter is a necessary correction needed to account for the Coriolis force.
grad(h) and Z are just the height gradient per degree of latitude. In your case, you're provided with 60 meters as Z and I'm unsure what Δn is for. Maybe your instructor is saying that the change is 60 meters per 2x10^5 meters? I'll assume that's the case.
So just calculating this in wolfram alpha for Denver, Colorado's latitude (40 deg), I get 31.39 meters per second, which is a reasonable number.
Let's try to plot it:
import matplotlib.pyplot as plt
import numpy as np
# Create a list of latitudes but exclude the equator because sin(0) is 0
lat_list = [i for i in range(-90, 91) if i != 0]
# Create a list of coriolis values
cor_list = [2 * 7.292e-5 * np.sin(i) for i in lat_list]
# Create a list of geostrophic winds
geo_wind = [(9.81 / i) * (60.0 / 200000.0) for i in cor_list]
# Plot the geostrophic winds on a line
# Make a new plot, with lat as x and wind as y. 'r--' is a red dashed line
plt.plot(lat_list, geo_wind, 'r--')
# set the axis range
plt.axis([-90, 90, min(geo_wind), max(geo_wind)])
# show the plot
plt.show()
Would give you the following chart, where latitude is the x-axis and wind speed is the y-axis:
Oddly, the chart (and printing the geo_wind list) show some wind value calculations exceeding 100 m/s and in some cases over 1000 m/s. I'm unsure why that's the case right now...it's a bit late! So the logic is correct, I would just check how python is calculating the wind speed...I'm think it has to do with scientific notation and floating point numbers.
Anyway, I should note that I wrote the above lists as list comprehensions. If that's a bit over your head, it's ok. Check out this link for a good explanation on how they compare to regular lists/for loops. I hope this gets you off to a good start. Happy trails!
I am trying to generate an efficient code for generating a number of random position vectors which I then use to calculate a pair correlation function. I am wondering if there is straightforward way to set a constraint on the minimum distance allowed between any two points placed in my box.
My code currently is as follows:
def pointRun(number, dr):
"""
Compute the 3D pair correlation function
for a random distribution of 'number' particles
placed into a 1.0x1.0x1.0 box.
"""
## Create array of distances over which to calculate.
r = np.arange(0., 1.0+dr, dr)
## Generate list of arrays to define the positions of all points,
## and calculate number density.
a = np.random.rand(number, 3)
numberDensity = len(a)/1.0**3
## Find reference points within desired region to avoid edge effects.
b = [s for s in a if all(s > 0.4) and all(s < 0.6) ]
## Compute pairwise correlation for each reference particle
dist = scipy.spatial.distance.cdist(a, b, 'euclidean')
allDists = dist[(dist < np.sqrt(3))]
## Create histogram to generate radial distribution function, (RDF) or R(r)
Rr, bins = np.histogram(allDists, bins=r, density=False)
## Make empty containers to hold radii and pair density values.
radii = []
rhor = []
## Normalize RDF values by distance and shell volume to get pair density.
for i in range(len(Rr)):
y = (r[i] + r[i+1])/2.
radii.append(y)
x = np.average(Rr[i])/(4./3.*np.pi*(r[i+1]**3 - r[i]**3))
rhor.append(x)
## Generate normalized pair density function, by total number density
gr = np.divide(rhor, numberDensity)
return radii, gr
I have previously tried using a loop that calculated all distances for each point as it was made and then accepted or rejected. This method was very slow if I use a lot of points.
Here is a scalable O(n) solution using numpy. It works by initially specifying an equidistant grid of points and then perturbing the points by some amount keeping the distance between the points at most min_dist.
You'll want to tweak the number of points, box shape and perturbation sensitivity to get the min_dist you want.
Note: If you fix the size of a box and specify a minimum distance between every point, it makes sense that there will be a limit to the number of points you can draw satisfying the minimum distance.
import numpy as np
import matplotlib.pyplot as plt
# specify params
n = 500
shape = np.array([64, 64])
sensitivity = 0.8 # 0 means no movement, 1 means max distance is init_dist
# compute grid shape based on number of points
width_ratio = shape[1] / shape[0]
num_y = np.int32(np.sqrt(n / width_ratio)) + 1
num_x = np.int32(n / num_y) + 1
# create regularly spaced neurons
x = np.linspace(0., shape[1]-1, num_x, dtype=np.float32)
y = np.linspace(0., shape[0]-1, num_y, dtype=np.float32)
coords = np.stack(np.meshgrid(x, y), -1).reshape(-1,2)
# compute spacing
init_dist = np.min((x[1]-x[0], y[1]-y[0]))
min_dist = init_dist * (1 - sensitivity)
assert init_dist >= min_dist
print(min_dist)
# perturb points
max_movement = (init_dist - min_dist)/2
noise = np.random.uniform(
low=-max_movement,
high=max_movement,
size=(len(coords), 2))
coords += noise
# plot
plt.figure(figsize=(10*width_ratio,10))
plt.scatter(coords[:,0], coords[:,1], s=3)
plt.show()
Based on #Samir 's answer, and make it a callable function for your convenience :)
import numpy as np
import matplotlib.pyplot as plt
def generate_points_with_min_distance(n, shape, min_dist):
# compute grid shape based on number of points
width_ratio = shape[1] / shape[0]
num_y = np.int32(np.sqrt(n / width_ratio)) + 1
num_x = np.int32(n / num_y) + 1
# create regularly spaced neurons
x = np.linspace(0., shape[1]-1, num_x, dtype=np.float32)
y = np.linspace(0., shape[0]-1, num_y, dtype=np.float32)
coords = np.stack(np.meshgrid(x, y), -1).reshape(-1,2)
# compute spacing
init_dist = np.min((x[1]-x[0], y[1]-y[0]))
# perturb points
max_movement = (init_dist - min_dist)/2
noise = np.random.uniform(low=-max_movement,
high=max_movement,
size=(len(coords), 2))
coords += noise
return coords
coords = generate_points_with_min_distance(n=8, shape=(2448,2448), min_dist=256)
# plot
plt.figure(figsize=(10,10))
plt.scatter(coords[:,0], coords[:,1], s=3)
plt.show()
As I understood, you're looking for an algorithm to create many random points in a box such that no two points are closer than some minimum distance. If this is your problem, then you can take advantage of statistical physics, and solve it using molecular dynamics software. Moreover, you do need molecular dynamics or Monte Carlo to obtain exact solution of this problem.
You place N atoms in a rectangular box, create a repulsive interaction of a fixed radius between them (such as shifted Lennard-Jones interaction), and run simulation for some time (untill you see that the points spread out uniformly throughout the box). By laws of statistical physics you can show that positions of the points would be maximally random given the constraint that points cannot be close than some distance. This would not be true if you use iterative algorithm, such as placing points one-by-one and rejecting them if they overlap
I would estimate a runtime of several seconds for 10000 points, and several minutes for 100k. I use OpenMM for all my moelcular dynamics simulations.
#example of generating 50 points in a square of 4000x4000 and with minimum distance of 400
import numpy as np
import random as rnd
n_points=50
x,y = np.zeros(n_points),np.zeros(n_points)
x[0],y[0]=np.round(rnd.uniform(0,4000)),np.round(rnd.uniform(0,4000))
min_distances=[]
i=1
while i<n_points :
x_temp,y_temp=np.round(rnd.uniform(0,4000)),np.round(rnd.uniform(0,4000))
distances = []
for j in range(0,i):
distances.append(np.sqrt((x_temp-x[j])**2+(y_temp-y[j])**2))
min_distance = np.min(distances)
if min_distance>400 :
min_distances.append(min_distance)
x[i]=x_temp
y[i]=y_temp
i = i+1
print(x,y)