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)
Related
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'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
I'm relatively new to python and getting my head around OOP. I'm making a class to perform some basic methods on data, but ideally i'd like to access those class methods as regular functions without necessarily creating an instance first.
I have the following code set up for MyMethod containing a single method 'clip_sphere', which takes in xyz coordinates as an array and returns coordinates that are inside a sphere centered at 'center' with radius 'radius'
import numpy as np
class MyMethod:
def __init__(self,xyz):
self.xyz = xyz
def clip_sphere(self, center, radius):
self.xyz = self.xyz - np.array(center).reshape(1,-1)
r = (np.sum(self.xyz**2, axis = 1))**0.5
idx = r < radius
xyz_clip = self.xyz[idx,:]
self.clip = xyz_clip
return xyz_clip
what i would like to do is be able to run clip sphere in two ways, wither by 1:
C = MyMethod(xyz)
xyz_clip = C.clip_sphere(center =[0,0,0],radius = 2)
or simply by calling it as a function like:
xyz_clip = clip_sphere(xyz,center =[0,0,0],radius = 2)
Prefereably without rewriting as an ordinary function. Is this possible to do with some decorators? or is this even possible at all.
EDIT: After looking through some of the answers, I guess what I'm asking is how to get a function like numpy reshape. as this works by both allowing a statement like:
a = np.reshape(np.array([1,2,3]),[3,1])
Which is acting like a function
As well as:
a = np.array([1,2,3])
a.reshape([3,1])
which is acting like a class method
It is sort-of built-in - all you need to do is get the function from the class' namespace:
C = MyMethod(xyz)
xyz_clip = MyMethod.clip_sphere(C, center =[0,0,0], radius = 2)
However, this still requires that you have an instance of the class. The issue is that the code is written to find xyz etc. attributes in a specific named object, the one named self. (There is nothing special about the name self in Python beyond convention.)
If you really need to be able to use xyz for this functionality, then the sane approach is to just write a plain function that handles it:
# At top level
def clip(xyz, center, radius):
xyz -= np.array(center).reshape(1,-1)
r = (np.sum(xyz**2, axis = 1))**0.5
idx = r < radius
return xyz[idx,:]
And then to avoid repetitive code, you can use this to implement the method:
# inside the class
def clip_sphere(self, center, radius):
# This part of the process needs to be repeated, because the
# `xyz` variable during the `clip` call is a separate name.
# However, you should consider whether you *actually* want to modify
# the object's `self.xyz` as a part of this process. My guess is you do not.
self.xyz -= np.array(center).reshape(1,-1)
self.clip = clip(self.xyz, center, radius) # uses the top-level `clip`.
Keeping in mind how descriptors work, you can make a small modification to a properly written clip_sphere method that will let you run the method as either a method, or as a classmethod that accepts coordinates as the first argument:
def clip_sphere(self, center, radius):
xyz = self.xyz if isinstance(self, __class__) else self
xyz -= np.array(center).reshape(1,-1)
r = (np.sum(self.xyz**2, axis = 1))**0.5
idx = r < radius
xyz_clip = self.xyz[idx,:]
if isinstance(self, __class__):
self.clip = xyz_clip
self.xyz = xyz
return xyz_clip
You can call it either as a method:
>>> xyz = ...
>>> MyMethod().clip_sphere(...)
or as a classmethod:
>>> MyMethod.clip_sphere(xyz, ...)
Looks like you want a standalone function, rather than a class/object:
def clip_sphere(xyz, center, radius):
xyz = xyz - np.array(center).reshape(1,-1)
r = (np.sum(xyz**2, axis = 1))**0.5
idx = r < radius
xyz_clip = xyz[idx,:]
clip = xyz_clip
return xyz_clip
You can make a global function named clip_sphere():
def clip_sphere(xyz, center, radius):
return MyMethod(xyz).clip_sphere(center, radius)
But doing this makes me wonder why you need the class at all. From what I can tell that you are doing here, it would probably make more sense to create a Sphere class. This is clearly an object with some attributes: radius and center. Then you can add a clip() method to do what you want:
class Sphere:
def __init__(center, radius):
self.center = center
self.radius = radius
def clip(x, y, z):
# code goes here
Response to edit:
After looking through some of the answers, I guess what I'm asking is how to get a function like numpy reshape. as this works by both allowing a statement like:
a = np.reshape(np.array([1,2,3]),[3,1])
Which is acting like a function As well as:
a = np.array([1,2,3])
a.reshape([3,1])
This is two different functions, not two different ways of calling the same function.
I am having an issue calling a method in a very simple python class. I have something that looks like this:
from shape import Shape
class Figure:
def __init__(self):
self.shapes = [] # the shape objects that make up self
self.shape0 = shape([1, 3, 2], [30, 20])
self.shape1 = shape([2, 3, 4], [25, 35])
def get_points(self):
# Returns all .points of all members of self.shapes
all_points = self.shape0
all_points.extend(self.shape1)
print(all_points)
get_points()
In get_points I'm trying to create a list of all of the instances of shape whether they be squares, pentagons, or anything. Right now, they are only triangles (the first array is a label of their points, the second is the angle of two of their vertices).
I'm currently trying to test if all_points returns a list of all of the points in both triangles (so 1,2,3,4). But currently, I'm getting an error trying to call get_points(). Does anyone know why I can't call get_points() right here? Error says, 'Unresolved reference 'get_points'.
Create an instance of Figure first.
f = Figure()
f.get_points()
You need an instance of Figure to call the method upon:
f = Figure()
print f.get_points()
I'm making a game that involves a map of square tiles (like Civ III). Each square in the map is an object with x,y coordinates and some other attributes (resources on the tile, elevation, etc).
My map file is stored as an array, like so:
tilemap = [
[GRASS, DIRT, DIRT],
[GRASS, GRASS, WATER],
[GRASS, GRASS, WATER],
[DIRT, GRASS, WATER],
[DIRT, GRASS, GRASS]
]
I want to go through the map and make a square at the x,y coordinates given by the location in the array, and with the type indicated by the text (grass, dirt, etc).
So far, I've tried this:
class LevelMap():
def __init__(self, level_array=tilemap):
self.level_array = level_array
# Go through every row in the map...
for row in range(MAPHEIGHT):
# ... and every column...
for column in range(MAPWIDTH):
# ...and make a square at that location.
Square(row, column, tilemap[row][column])
The Square class takes the arguments Square(x,y,square_type).
However, I do not know how to give each square its own unique name, so I can say
square_1 = the square at 0,0
square_2 = the square at 0,1
and so on, so that I can use e.g. square1.get_square_type() or square_1.change_terrain_type(rocky).
How can I turn my map array into a group of Square objects with unique names?
I'm using Python 3.4 and pygame.
You don't.
You said already that you have your map stored as a 2D list. You only reference your tiles by their index in that array.
class LevelMap():
def __init__(self, level_array=tilemap):
self.level_array = level_array
# Go through every row in the map...
for row in range(MAPHEIGHT):
# ... and every column...
for column in range(MAPWIDTH):
# ...and make a square at that location.
Square(row, column, tilemap[row][column])
map = LevelMap(level_array=[])
# change the tile at (0, 1) to rocky:
map.level_array[1][0].change_terrain_to("rocky")
I'd even strongly recommend writing a helper function onto LevelMap that gets a tile given its (x,y).
class LevelMap():
...
def get_tile(self, x, y):
return self.level_array[y][x]
map = LevelMap()
tile = map.get_tile(0, 1)
# or
location = (0, 1)
tile = map.get_tile(*location)
When I've written things like this in the past, I've made my Map a child of list
class Map(list):
def __init__(self, height, width):
for y in range(height):
self.append([Square(y, x, tilemap[y][x]) for x in range(width)])
def get_tile(self, x, y):
return self[y][x]