I'm finally a member of the stackoverflow community, so this is my first post. I'm trying my best to create a good question.
I have a problem with a simple variable declaration in a for-loop. The variable gets declared in an if-statement and is the variable of a class. Basically what the code does is to create a box with points in it, initialise the positions and velocities of the points, edit the velocities and calculate the new positions according to their new velocities. I now want to save the initial positions of the points (means time = 0) outside of the class in a for-loop. I do this by if time = 0 then save position to a variable, but the variable gets updated to the new position in every loop iteration. The actual code is about hydrodynamics and particle interaction, but the basic structure of the code is something like this:
import numpy as np
class box():
def __init__(self, boxsize, num_points, timestep):
"""
boxsize is the size of the quadratic box in x and y direction
num_points is the number of points in the box
timestep is the time after that the positions should be updated
"""
self.boxsize = float(boxsize)
self.num_points = int(num_points)
self.timestep = float(timestep)
self.positions = np.zeros((self.num_points, 2)).astype(float)
self.velocities = np.zeros((self.num_points, 2)).astype(float)
def initialise(self):
"""initialise the positions and velocites of the points in the box, both with x- and y-components"""
self.positions[:, :] = np.random.uniform(0., self.boxsize, size=(self.num_points, 2))
self.velocities[:, :] = np.random.uniform(0., 1., size=(self.num_points, 2))
def update_positions(self):
"""update position according to velocities, x- and y-components"""
self.positions += self.velocities*self.timestep
def new_velocities(self):
""" create new velocities, x- and y-components """
self.velocities[:, :] = np.random.uniform(0., 1., size=(self.num_points, 2))
def connect_steps(self):
"""update the positions according to their velocities and create new velocities"""
self.update_positions()
self.new_velocities()
system = box(10., 1, 0.1) #box is 10 in x and y; 1 point in the box
system.initialise() #initialise the positions and velocities of the box
for i in range(10): #10 timesteps
system.connect_steps()
if i == 0.:
r0 = system.positions
print(r0) #here r0 should always be the same array from i = 0 but isn't
print(r0 == system.positions) #yields True every iteration, so r0 is always the new position
What I want is that r0 is always the position at i = 0 (initial position), but every iteration the variable r0 gets updated according to its new position, although the if-clause and so the variable definition only gets entered once at i = 0.
It is intended to first update the positions and after that generate new velocities although they are first used in the next iteration because the real algorithm behind this velocity-generation needs the structure this way.
Maybe there is just a characteristic or property of classes I don't know.
I hope the question makes sense and anybody can help me out.
Thanks a lot!
You might try np.copy(system.positions) to get a copy that won't continue to mutate as you update.
Reference https://numpy.org/doc/stable/reference/generated/numpy.copy.html
Related
Trying to create a n-body simulation and currently creating a particle class. I initialise the class by inputing a number of variables such as position and velocity. I was wondering if I could somehow combine all the same variable into one array. For instance when I call upon the Particle class it saves all the variables but the variables are all about that particular particle and tied to it. I was wondering if I would be able to find a way to return the velocities for all the particles in one array.
class Particle:
Position = np.full((1, 3), 0, dtype=float)
Velocity = np.full((1, 3), 0, dtype=float)
Acceleration = np.full((1, 3), 0, dtype=float)
Name = np.full((1, 1), 0, dtype=float)
Mass = np.full((1, 1), 0, dtype=float)
"""Initialisng all all the data members for each Particle"""
def __init__(self, Name, Mass, initPosition, initVelocity, initAcceleration):
self.Name = Name
self.Mass = Mass
self.Position = np.array(initPosition)
self.Velocity = np.array(initVelocity)
self.Aceleration = np.array(initAcceleration)
def arrays(self, Positions, Velocities, Accelerations, Names, Masses):
Positions.append(self.Position)
Velocities.append(self.Velocity)
Accelerations.append(self.Acceleration)
Names.append(self.Name)
Masses.append(self.Mass)
my second definition "arrays" is trying to to that but unsuccessfully. The aim is so that I can type Positions and a (N,3) matrix is produced upon which I can perform calculations. Is this possible?
I am not sure what you want to do actually:
If you want to update and return all positions/velocities of one Particle object so you can define:
def arrays(self, Positions, Velocities, Accelerations, Names, Masses):
self.Positions = np.append(self.Position, Position)
self.Velocities = np.append(self.Velocity, Velocity)
self.Accelerations = np.append(self.Acceleration, Acceleration)
self.Names = np.append(self.Name, Name)
self.Masses = np.append(self.Mass, Mass)
and then you can access class properties like:
p1 = Particle(...)
p1.Positions
you can update your particle properties from outside and can access it.
However in your case(i guess at least) you will probably need multiple particle objects. So it is better define a new class that takes particles collection[Particle(*args, **kwargs), Particle(*args, **kwargs), ...] as an input and then you can access all the particles properties and do whatever you want.
Actually, 'numpy.ndarray' object has no attribute 'append'.
Use append of numpy:
a = np.array([1, 2])
a = np.append(a, 3)
And note that the result of the execution must be assigned, otherwise nothing will be added.
def arrays(self, Positions, Velocities, Accelerations, Names, Masses):
self.Positions.append(Position)
self.Velocities.append(Velocity)
self.Accelerations.append(Acceleration)
self.Names.append(Name)
self.Masses.append(Mass)
I am creating a charge smear function. I have a matrix were each row is a particle with a charge and position. I then look at each particles position in a grid, to count how many particles are in each grid-cell, but I need to know which cell each particle is in, so that I may find the average of the positions for every particle in a specific grid-cell. My idea for a fix is to create an list where the number of rows is the amount of grid-cells in my matrix, and let the column be positions in x,y and z direction, but obviously I can't append more then one number to each index, but maybe some variation will work? Sorry for open ended question. Thank you in advance
import matplotlib.pyplot as plt
import random
import numpy as np
###Initalize particle lists
particle_arrayX=[]
particle_arrayY=[]
###The resolution
N = 10
###Number of particles
M = 1000
col=None
row=None
###Size of box
Box_size=100
###gridsize
Grid_size=Box_size/N
###Initalize particles
for i in range(M):
particle_arrayX.append(random.random()*Box_size)
particle_arrayY.append(random.random()*Box_size)
###Intialize matrix
ParticleMatrix_Inital=[[0 for i in range(N)]]*N
###Measure density in each cell
for i in range(M):
col=None
row=None
#The x and y components are diveded by the gridsize
#Then they are converted to integers and then asigned either to a row or column
#If value is float with decimal 0 EX 2.0, then 1 is substracted before converted to int
coln=particle_arrayX[i]/Grid_size
rown=particle_arrayY[i]/Grid_size
if coln.is_integer()==True:
col=int(coln)-1
else:
col=int(coln)
if rown.is_integer()==True:
row=int(rown)-1
else:
row=int(rown)
ParticleMatrix_Inital=np.array(ParticleMatrix_Inital)
ParticleMatrix_Inital[row,col]=ParticleMatrix_Inital[row,col]+1
ParticleMatrix_Inital=ParticleMatrix_Inital.tolist()
#Plot matrix
plt.imshow(ParticleMatrix_Inital)
plt.colorbar()
plt.show()
Welcome to SO!
There are many ways to approach the problem of "bin-ing" empirical data. I'm proposing an object oriented (OO) solution below, because (in my subjective opinion) it provides clean, tidy and highly readable code. On the other hand, OO-solutions might not be the most efficient if you're simulating huge many-particles systems. If the below code doesn't entirely solve your issues, I still hope that parts of it can be of some help to you.
That being said, I propose implementing your grid as a class. To make life easier for our self, we may apply the convention that all particles have positive coordinates. That is x, y and even z (if introduced) stretches from 0 to whatever box_size you define. However, the class Grid does not really care about the actual box_size, only the resolution of the grid!
class Grid:
def __init__(self, _delta_x, _delta_y):
self.delta_x = _delta_x
self.delta_y = _delta_y
def bounding_cell(self, x, y):
# Note that y-coordinates corresponds to matrix rows,
# and that x-coordinates corresponds to matrix columns
return (int(y/self.delta_y), int(x/self.delta_x))
Yes, this could have been a simple function. However, as a class it is easily expandable. Also, a function would have rely on global variables (yuck!) or explicitly be given the grid spanning (delta) in each dimensional direction, for every determining of which matrix cell (or bin) the given coordinate (x,y) belongs to.
Now, how does it work? Imagine the simplest of cases, where your grid resolution is 1. Then, a particle at position (x,y) = (1.2, 4,9) should be placed in the matrix at (row,col) = (4,1). That is row = int(y/delta_y) and likewise for x. The higher resolution (smaller delta) you have, the larger the matrix gets in terms of number of rows and cols.
Now that we have a Grid, let us also object orient the Particle! Rather straight forward:
class Particle:
def __init__(self, _id, _charge, _pos_x, _pos_y):
self.id = _id
self.charge = _charge
self.pos_x = _pos_x
self.pos_y = _pos_y
def __str__(self):
# implementing the __str__ method let's us 'print(a_particle)'
return "{%d, %d, %3.1f, %3.1f}" % (self.id, self.charge, self.pos_x, self.pos_y)
def get_position(self):
return self.pos_x, self.pos_y
def get_charge(self):
return self.charge
This class is more or less just a collection of data, and could easily have been replaced by a dict. However, the class screams its intent clearly, it is clean and tidy, and also easily expanded.
Now, let's create some instances of particles! Here is a function which by list comprehension creates a list of particles with an id, charge and position (x,y):
import random
def create_n_particles(n_particles, max_pos):
return [Particle(id, # unique ID
random.randint(-1,1), # charge [-1, 0, 1]
random.random()*max_pos, # x coord
random.random()*max_pos) # y coord
for id in range(n_particles)]
And finally, we get to the fun part: putting it all together:
import numpy as np
if __name__ == "__main__":
n_particles = 1000
box_size = 100
grid_resolution = 10
grid_size = int(box_size / grid_resolution)
grid = Grid(grid_resolution, grid_resolution)
particles = create_n_particles(n_particles, box_size)
charge_matrix = np.zeros((grid_size, grid_size))
id_matrix = [[ [] for i in range(grid_size)] for j in range(grid_size)]
for particle in particles:
x, y = particle.get_position()
row, col = grid.bounding_cell(x, y)
charge_matrix[row][col] += particle.get_charge()
# The ID-matrix is similar to the charge-matrix,
# but every cell contains a new list of particle IDs
id_matrix[row][col].append(particle.id)
Notice the initialization of the ID-matrix: This is the list of particle positions for each grid cell that you asked for. It is a matrix, representing the particle container, and each cell contains a list to be filled with particle IDs. You could also populate these lists with entire particle instances (not just their IDs): id_matrix[row][col].append(particle).
The last for loop does the real work, and here the Object Oriented strategy shows us how charming it is: The loop is short and it is very easy to read and understand what is going on: A cell in the charge_matrix contains the total charge within this grid cell/bin. Meanwhile, the id_matrix is filled with the IDs of the particles that is contained within this grid cell/bin.
From the way we've constructed the list of particles, particles, we see that a particle's ID is equivalent to that particle's index in the list. Hence, they may be retrieved like this,
for i,row in enumerate(id_matrix):
for j,col in enumerate(row):
print("[%d][%d] : " % (i, j), end="")
for particle_id in id_matrix[i][j]:
p = particles[particle_id]
# do some work with 'p', or just print it:
print(p, end=", ")
print("") # print new line
# Output:
# [0][0] : {32, -1, 0.2, 0.4}, ... <-- all data of that particle
# ....
I leave optimization of this retrieval to you as I don't really know what data you need and what you're going to do with it. Maybe it's better to contain all the particles in a dict instead of a list; I don't know(?). You choose!
At the very end, I'll suggest that you use matshow which is inteded for displaying matrices, as opposed to imshow which is more aiming more for images.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
cax = ax.matshow(charge_matrix)
fig.colorbar(cax)
ax.invert_yaxis() # flip the matrix such that the y-axis points upwards
fig.savefig("charge_matrix.png")
We can also scatter plot the particles and add grid lines corresponding to our the grid in the matshow above. We color the scatter plots such that negative charges are blue, neutral are gray and positive are red.
def charge_color(charge):
if charge > 0: return 'red'
elif charge < 0: return 'blue'
else: return 'gray'
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_aspect('equal')
ax.set_xticks(np.arange(0, 101, grid_resolution))
ax.set_yticks(np.arange(0, 101, grid_resolution))
ax.grid()
ax.scatter([p.pos_x for p in particles],
[p.pos_y for p in particles],
c=[charge_color(p.get_charge()) for p in particles])
fig.savefig("particle_dist.png")
I have encountered a strange problem: when I store a huge amount of data points from a nonlinear equation to 3 arrays (x, y ,and z) and then tried to plot them in a 2D graph (theta-phi plot, hence its 2D).
I tried to eliminate points needed to be plotted by sampling points from every 20 data points, since the z-data is approximately periodic. I picked those points with z value just above zero to make sure I picked one point for every period.
The problem arises when I tried to do the above. I got only a very limited number of points on the graph, approximately 152 points, regardless of how I changed my initial number of data points (as long as it surpassed a certain number of course).
I suspect that it might be some command I use wrongly or the capacity of array is smaller then I expected (seems unlikely), could anyone help me find out where is the problem?
def drawstaticplot(m,n, d_n, n_o):
counter=0
for i in range(0,m):
n=vector.rungekutta1(n, d_n)
d_n=vector.rungekutta2(n, d_n, i)
x1 = n[0]
y1 = n[1]
z1 = n[2]
if i%20==0:
xarray.append(x1)
yarray.append(y1)
zarray.append(z1)
for j in range(0,(m/20)-20):
if (((zarray[j]-n_o)>0) and ((zarray[j+1]-n_o)<0)):
counter= counter +1
print zarray[j]-n_o,counter
plotthetaphi(xarray[j],yarray[j],zarray[j])
def plotthetaphi(x,y,z):
phi= math.acos(z/math.sqrt(x**2+y**2+z**2))
theta = math.acos(x/math.sqrt(x**2 + y**2))
plot(theta, phi,'.',color='red')
Besides, I tried to apply the code in the following SO question to my code, I want a very similar result except that my data points are not randomly generated.
Shiuan,
I am still investigating your problem, how ever a few notes:
Instead of looping and appending to an array you could do:
select every nth element:
# inside IPython console:
[2]: a=np.arange(0,10)
In [3]: a[::2] # here we select every 2nd element.
Out[3]: array([0, 2, 4, 6, 8])
so instead of calcultating runga-kutta on all elements of m:
new_m = m[::20] # select every element of m.
now call your function like this:
def drawstaticplot(new_m,n, d_n, n_o):
n=vector.rungekutta1(n, d_n)
d_n=vector.rungekutta2(n, d_n, i)
x1 = n[0]
y1 = n[1]
z1 = n[2]
xarray.append(x1)
yarray.append(y1)
zarray.append(z1)
...
about appending, and iterating over large data sets:
append in general is slow, because it copies the whole array and then
stacks the new element. Instead, you already know the size of n, so you could do:
def drawstaticplot(new_m,n, d_n, n_o):
# create the storage based on n,
# notice i assumed that rungekutta, returns n the size of new_m,
# but you can change it.
x,y,z = np.zeros(n.shape[0]),np.zeros(n.shape[0]), np.zeros(n.shape[0])
for idx, itme in enumerate(new_m): # notice the function enumerate, make it your friend!
n=vector.rungekutta1(n, d_n)
d_n=vector.rungekutta2(n, d_n, ite,)
x1 = n[0]
y1 = n[1]
z1 = n[2]
#if i%20==0: # we don't need to check for the 20th element, m is already filtered...
xarray[idx] = n[0]
yarray[idx] = n[1]
zarray[idx] = n[2]
# is the second loop necessary?
if (((zarray[idx]-n_o)>0) and ((zarray[j+1]-n_o)<0)):
print zarray[idx]-n_o,counter
plotthetaphi(xarray[idx],yarray[idx],zarray[idx])
You can use the approach suggested here:
Efficiently create a density plot for high-density regions, points for sparse regions
e.g. histogram where you have too many points and points where the density is low.
Or also you can use rasterized flag for matplotlib, which speeds up matplotlib.
The goal here is to take some list of coordinates, like [[1,2],[3,4],[7,1]], then figure out how big the canvas should be if you want to print all these coords. Take the maximal bottom left coordinate and minimal upper right coordinate that will snugly fit a canvas to these points. In the above list, for example, we're looking for [[1,1],[7,4]], which defines the smallest rectangle where all those points will fit.
In the middle of this function, I'm seeing the incoming "board" assigned a new value.
def print_board(board):
# import pdb; pdb.set_trace()
dimensions = None
for i in board:
if dimensions == None:
dimensions = [i, i]
else:
dimensions[0][0] = min(dimensions[0][0], i[0])
#'board' is redefined !!!
dimensions[0][1] = min(dimensions[0][1], i[1])
#dimensions[1][0] = max(dimensions[1][0], i[0])
#dimensions[1][1] = max(dimensions[1][1], i[1])
# (after we get the canvas size
# we print the canvas with the points on it
# but we never make it that far without an error)
As the for loop moves through the coordinates in the incoming board, it seems to be setting board[0] to whatever coordinate it's looking at at the time. So [[1,2],[3,4],[7,1]] will change first to [[3,4],[3,4],[7,1]], then to [[7,1],[3,4],[7,1]].
I wouldn't expect board to change at all.
(Python 3.2.2)
When you do
dimensions = [i, i]
you're setting both items in dimensions to the first point in your board -- not making copies of that point.
Then when you do
dimensions[0][0] = min(dimensions[0][0], i[0])
dimensions[0][1] = min(dimensions[0][1], i[1])
you're updating that same point --- the first point in your board -- to the results of the min functions.
Try something like this, instead:
def print_board(board):
xs, ys = zip(*board) # separate out the x and y coordinates
min_x, max_x = min(xs), max(xs) # find the mins and maxs
min_y, max_y = min(ys), max(ys)
dimensions = [[min_x, min_y], [max_x, max_y]] # make the dimensions array
As an extension of agfs answer, you can use numpy for even more efficient and succinct code:
import numpy as np
def print_board(board):
a = np.array(board)
return [a.min(axis=0).tolist(), a.max(axis=0).tolist()]
If your board is a numpy array already, and you let the function return a tuple of numpy arrays, it shortens even more:
def print_board(board):
return board.min(axis=0), board.max(axis=0)
The program seems to work, however the linear regression line created does not seem to really be the line of best fit.
I think the problem is the implementation of the equation. I'm not sure if i'm interpreting it right, also I am unsure if I am doing what should be done in regards to the last paragraph of the exercise.
here is the graphics library: http://mcsp.wartburg.edu/zelle/python/ppics1/code/graphics.py
if you want to try it out.
here is the the exercise:
Write a program that graphically plots a regression line, that is, the line with the best fit through acollection of points. First ask the user to specify the data points by clicking on them in a graphicswindow. To find the end of input, place a small rectangle labelled “Done" in the lower left corner ofthe window; the program will stop gathering points when the user clicks inside that rectangle.The regression line is the line with the following equation:
here is the equation: http://i.stack.imgur.com/xj2uu.jpg
I can't post pictures
x is the mean of the x-values and .y is the mean of the y-values.As the user clicks on points, the program should draw them in the graphics window and keep track ofthe count of input values and the running sum of x, y, x2 and xy values. When the user clicks inside the“Done" rectangle, the program then computes value of y (using the equations above) correponding tothe x values at the left and right edges of the window to compute the endpoints of the regression linespanning the window. After the line is drawn, the program will pause for another mouse click beforeclosing the window and quitting.
I can't seem to get the code formatted right so I included this http://pastebin.com/JsQ0eM2R
# 8-13-LOB.py
from graphics import *
def listMulti(list1,list2):
tempAcc = 0
for i in range(len(list1)):
tempAcc += list1[i] * list2[i]
print tempAcc
return tempAcc
def squareList(iterable):
itSum = 0
for i in iterable:
itSum += i**2
return itSum
def listMean(iterable):
return sum(iterable)/len(iterable)
def regression(xList,yList,win):
xBar = listMean(xList)
yBar = listMean(yList)
xListSq = squareList(xList)
xListXyList = listMulti(xList,yList)
m = ((xListXyList) - ((len(xList)*xBar*yBar)))/\
((xListSq) - (len(xList)* (xBar**2)))
y1 = yBar + m*(-50.0 - xBar)
y2 = yBar + m*(50.0 - xBar)
Line(Point(-50.0,y1),Point(50.0,y2)).draw(win)
return "ybar: %f\txBar: %f\tm: %f\ty1: %f\ty2: %f" %(yBar,xBar,m,y1,y2)
def windraw():
win = GraphWin("Line of Best Fit",500,500)
win.setCoords(-50.0,-50.0,50.0,50.0)
doneBox = Rectangle(Point(-50,-50),Point(-40,-45))
doneBox.setWidth(3)
doneBoxTxt = Text(Point(-45,-47.5),"DONE")
doneBox.draw(win)
doneBoxTxt.draw(win)
return win
def pointBuild(xList,yList,win):
tempPoint = Point(25,25) # prime tempPoint for sentinel loop
# tests if given point is past rectangle created for doneBox
while (tempPoint.getX() - (Point(-40,-45)).getX() == abs(tempPoint.getX() - (Point(-40,-45)).getX())) or\
(tempPoint.getY() - (Point(-40,-45)).getY() == abs(tempPoint.getY() - (Point(-40,-45)).getY())):
tempPoint = win.getMouse()
tempPoint.draw(win)
xList.append(tempPoint.getX()); yList.append(tempPoint.getY())
def main():
xList,yList = [],[]
win = windraw()
pointBuild(xList,yList,win)
print regression(xList,yList,win)
# Test out coordinate lists accumulation from pointBuild
for i in range(len(xList)-1):
print "Point(%2.2f,%2.2f)" % (xList[i],yList[i])
win.getMouse()
win.close()
main()
I think the problem is that your pointBuild routine adds the point where the user clicks in the "DONE" box to the regression list, so every dataset has a point in the lower left. You can confirm this by adding "print xList, yList" before pointBuild returns. I would modify the routine to:
while True: # (a common python idiom for "do forever until we break")
tempPoint = win.getMouse()
if (tempPoint is in the DONE rectangle):
# get out, we're done: don't forget to handle the case where
# there are no points in xList/yList!
break
else:
# draw the point
# add it to xList, yList
I think you might also want to look at the "is in the DONE rectangle" logic. IIUC, you simply want to know if tempPoint.getX() is between -50 and -40 and .getY() is between -50 and -45.
Good luck!