Commit a40b3a26 authored by Breno Rilho Lemos's avatar Breno Rilho Lemos 💬

Merge branch 'ParticleTypes' into 'master'

Particle types

See merge request !2
parents ccfe2e66 1f54dbcc
# ALICE Open Data Blender animation
## Project Description
This project has the purpose of generating a 3D animation of an ALICE particle collision event, inside the LHC, using data obtained from CERN's Open Data Portal, which makes ESDs - Event Summary Data files, that contain information about such events - open and available for analysis.
ESD files regarding the ALICE experiment can be found on http://opendata.cern.ch/search?page=1&size=20&experiment=ALICE, and they should be processed using the Aliroot software, as indicated in the AliESD_Example git repository: https://git.cta.if.ufrgs.br/ALICE-open-data/AliESD_Example/tree/Blender_animation
The software that makes the animation is Blender, which is free and open source. Blender's 2.79b version should be downloaded for this project, and can be found on https://www.blender.org/download/releases/2-79/
The animation making can be summarized in three basic steps:
1) Downloading of an ESD file (for example, any file on this list: http://opendata.cern.ch/record/1102);
2) Processing of ESD file using Aliroot macros;
3) Run code to generate Blender animation using the ESD processing results.
## Requirements
* Blender 2.79b
## Run
Run animation example:
`blender -noaudio --background -P animate_particles.py -- -radius=0.05 -duration=1 -camera="BarrelCamera" -datafile="esd-detail.dat" -simulated_t=0.02 -fps=24 -resolution=100`
In the example above, argument 'radius' has value 0.05, 'duration' has value 10 and so on.
*-radius*:
particle radius; must be a number; type float
*-duration*:
animation duration; must be a number; type int
*-camera*:
defines animation view; must be a string; available options: OverviewCamera, BarrelCamera, ForwardCamera
*-datafile*:
filename for event data file; must be a string
*-simulated_t*:
simulated time of event; must be a number; type float
*-fps*:
frames per second; must be a number; type int
*-resolution*:
animation resolution percent; must be a number; type int
Implement command line arguments:
https://blender.stackexchange.com/questions/6817/how-to-pass-command-line-arguments-to-a-blender-python-script
# -*- coding: utf-8 -*-
# animate_particles.py - Animate HEP events
#
# For console only rendering:
# $ blender -noaudio --background -P animate_particles.py
# For console only rendering (example):
# $ blender -noaudio --background -P animate_particles.py -- -radius=0.05 -duration=1 -camera="BarrelCamera" -datafile="esd-detail.dat" -simulated_t=0.02 -fps=24 -resolution=100 -stamp_note="Texto no canto"
#
# TODO: - Implement command line arguments
# - https://blender.stackexchange.com/questions/6817/how-to-pass-command-line-arguments-to-a-blender-python-script
import os
import bpy
import argparse
import sys
# Pass on command line arguments to script:
class ArgumentParserForBlender(argparse.ArgumentParser):
def _get_argv_after_doubledash(self):
try:
idx = sys.argv.index("--")
return sys.argv[idx+1:] # the list after '--'
except ValueError as e: # '--' not in the list:
return []
# overrides superclass
def parse_args(self):
return super().parse_args(args=self._get_argv_after_doubledash())
parser = ArgumentParserForBlender()
parser.add_argument('-radius','--r_part')
parser.add_argument('-duration','--duration')
parser.add_argument('-camera','--render_camera')
parser.add_argument('-datafile','--datafile')
parser.add_argument('-simulated_t','--simulated_t')
parser.add_argument('-fps','--fps')
parser.add_argument('-resolution','--resolution_percent')
parser.add_argument('-stamp_note','--stamp_note')
args = parser.parse_args()
bpy.context.user_preferences.view.show_splash = False
# Import Drivers, partiles and scence functions:
filename = os.path.join(os.path.basename(bpy.data.filepath), "drivers.py")
exec(compile(open(filename).read(), filename, 'exec'))
# Set animation parameters
r_part = 0.05 # Particle radius
simulated_t = 0.015
duration = 15
fps = 24
resolution_percent = 100
r_part = float(args.r_part) # Particle radius
simulated_t = float(args.simulated_t) # in microsseconds
duration = int(args.duration) # in seconds
fps = int(args.fps)
resolution_percent = int(args.resolution_percent)
#configure output
outputPath = "/tmp/blender/"
fileIdentifier = "PhysicalTrajectories_"
## RenderCameras: ["ForwardCamera", "OverviewCamera", "BarrelCamera"]
renderCamera="ForwardCamera"
renderCamera= args.render_camera
renderAnimation = True # True
saveBlenderFile = False # False
"""
# Create and configure animation driver
n_particles = 100 # Event Multiplicity
driver = genDriver("GaussianGenerator",n_particles,3.0) # Simple genDriver takes two parameters: number of particles and Gaussian width
driver.configure(renderCamera, duration, fps, simulated_t, outputPath, fileIdentifier, resolution_percent)
"""
# Create and configure animation driver
driver = dataDriver("AlirootFileGenerator",args.datafile) # Simple dataDriver takes one parameters: filename
driver.configure(renderCamera, duration, fps, simulated_t, outputPath, fileIdentifier, resolution_percent)
### Build scene
init() # Cleanup, addCameras, addALICE_TPC
init(args.stamp_note) # Cleanup, addCameras, addALICE_TPC
particles = driver.getParticles()
blender_particles = createSceneParticles(particles) # Create blender objects - one sphere per particle
blender_particles, blender_tracks = createSceneParticles(particles,createTracks = True) # Create blender objects - one sphere per particle
#Animate scene using driver
animate(blender_particles,particles,driver)
animate_tracks(blender_tracks,particles,driver)
bpy.context.scene.frame_current = 24
## Save blender file
......
......@@ -59,24 +59,39 @@ class genDriver(animationDriver): # A driver for particle generators
#loop over particles
for i in range(0, self.N_particles):
charge = random.choice([+1,-1])
part = ParticlePropagator(i,x,y,z)
mass = random.choice([0.000510999, 0.13957, 0.105658, 0.938272, 0.493677]) # Available mass values
part = ParticlePropagator(i,x,y,z,charge,mass)
part.SetType()
part.SetMagneticField()
part.SetProperties(random.gauss(0,self.par1),random.gauss(0,self.par1),random.gauss(0,self.par1),charge)
part.SetProperties(random.gauss(0,self.par1),random.gauss(0,self.par1),random.gauss(0,self.par1))
particles.append(part)
return particles;
class dataDriver(animationDriver): # A driver for data from files. Under construction
def __init__(self,name,filename):
self.name = name+"_"+filename+"_"
class dataDriver(animationDriver): # A driver for data from files.
def __init__(self,name,datafile):
self.name = name+"_"+datafile+"_"
self.datafile = datafile
def getParticles(self): # Create particles acording to parameters from file
# TODO: load data into vectors
# Count number of lines in file = number of particles
detail_file = open(self.datafile, 'r')
lines = detail_file.readlines()
N_particles = len(lines)
# Create particles list
particles=[]
x = y = z = 0;
#loop over particles
#loop over particles and get information from data file
for i in range(0, N_particles):
charge = random.choice([+1,-1])
part = ParticlePropagator(i,x,y,z)
part.SetMagneticField()
part.SetProperties(random.gauss(0,par1),random.gauss(0,par1),random.gauss(0,par1),charge)
x = lines[i].split(' ')[0]
y = lines[i].split(' ')[1]
z = lines[i].split(' ')[2]
mass = lines[i].split(' ')[3]
charge = lines[i].split(' ')[4]
Px = lines[i].split(' ')[5]
Py = lines[i].split(' ')[6]
Pz = lines[i].split(' ')[7]
part = ParticlePropagator(i,float(x),float(y),float(z),float(charge),float(mass))
part.SetType()
part.SetMagneticField(0.5)
part.SetProperties(float(Px),float(Py),float(Pz))
particles.append(part)
detail_file.close()
return particles;
......@@ -12,6 +12,12 @@ class Particle:
self.z=z
self.charge=charge
self.mass = mass
def SetType(self):
p_mass = {"Electron":0.000510999, "Pion":0.13957, "Muon":0.105658, "Proton":0.938272, "Kaon":0.493677}
self.p_type = "Unknown"
for p_type in p_mass:
if p_mass[p_type] == self.mass:
self.p_type = p_type
def PrintPosition(self):
print(str(self.x) + " ; " + str(self.y) + " ; " + str(self.z))
def GetPosition(self):
......@@ -22,8 +28,7 @@ class Particle:
class ParticlePropagator(Particle):
def SetMagneticField(self, B = 0.5):
self.B = B
def SetProperties(self, Px, Py, Pz, charge = 1):
self.charge = charge
def SetProperties(self, Px, Py, Pz):
self.Px = Px # unit: Gev/c
self.Py = Py # unit: Gev/c
self.Pz = Pz # unit: Gev/c
......
def init():
def init(unique_id):
bcs = bpy.context.scene
# Configure Environment
bcs.world.light_settings.use_environment_light = False
bcs.world.light_settings.environment_energy = 0.1
# Configure Stamp
bpy.context.scene.render.use_stamp = True
bpy.context.scene.render.use_stamp_time = False
bpy.context.scene.render.use_stamp_date = False
bpy.context.scene.render.use_stamp_render_time = False
bpy.context.scene.render.use_stamp_frame = False
bpy.context.scene.render.use_stamp_scene = False
bpy.context.scene.render.use_stamp_camera = False
bpy.context.scene.render.use_stamp_filename = False
bpy.context.scene.render.stamp_note_text = unique_id
bpy.context.scene.render.use_stamp_note = True
# Cleanup
bpy.data.objects.remove(bpy.data.objects['Cube'])
bpy.data.objects.remove(bpy.data.objects['Camera'])
......@@ -52,15 +64,16 @@ def addCameras():
# Function that creates Blender Objects from input list of particles.
## Returns a list of blender objects
def createSceneParticles(particles):
def createSceneParticles(particles, createTracks = False):
# Associate particles and colors
particle_types = ["Electron","Pion","Muon","Proton","Kaon"]
particle_types = ["Electron","Pion","Muon","Proton","Kaon","Unknown"]
clRed = (1, 0, 0)
clGreen = (0, 1, 0)
clBlue = (0, 0, 1)
clMagenta = (0.75, 0, 1)
clYellow = (1, 1, 0)
particle_colors = {"Electron":clRed, "Pion":clGreen, "Muon":clBlue, "Proton":clMagenta, "Kaon": clYellow}
clWhite = (255, 255, 255)
particle_colors = {"Electron":clRed, "Pion":clGreen, "Muon":clBlue, "Proton":clMagenta, "Kaon": clYellow, "Unknown": clWhite}
#Create Materials
for type in particle_types:
......@@ -72,8 +85,9 @@ def createSceneParticles(particles):
# Create blender spheres (particles)
blender_particles=[]
n_particles=len(particles)
for particle in particles:
this_type=random.choice(particle_types)
this_type=particle.p_type
print("Adding Sphere - Particle " + str(len(blender_particles))+" of "+str(n_particles-1)+" - "+this_type)
bpy.ops.mesh.primitive_uv_sphere_add()
this_particle = bpy.context.object
......@@ -83,7 +97,41 @@ def createSceneParticles(particles):
this_particle.data.materials.clear()
this_particle.data.materials.append(bpy.data.materials[this_type])
blender_particles.append(this_particle)
return blender_particles
# Create blender curves (tracks)
blender_tracks=[]
if createTracks:
for track in particles:
this_type=track.p_type #TO DO: make this not random, but according to file data
print("Adding Curve - Track " + str(len(blender_tracks))+" of "+str(n_particles-1)+" - "+this_type)
# create the Curve Datablock
curveTrack = bpy.data.curves.new('CurveTrack', type='CURVE')
curveTrack.dimensions = '3D'
curveTrack.resolution_u = 2
curveTrack.fill_mode = 'FULL'
curveTrack.bevel_depth = 0.02
curveTrack.bevel_resolution = 3
# map coords to spline
bcs = bpy.context.scene
polyline = curveTrack.splines.new('NURBS')
polyline.points.add(bcs.frame_end) # Add one point per frame
for i in range(bcs.frame_end):
polyline.points[i].co = (particle.x,particle.y,particle.z, 1)
# create Object
trackOB = bpy.data.objects.new('Track', curveTrack)
trackOB.data.materials.clear()
trackOB.data.materials.append(bpy.data.materials[this_type])
scn = bpy.context.scene
scn.objects.link(trackOB)
blender_tracks.append(trackOB)
return blender_particles, blender_tracks
# Function that animates the scene using the particle propagator class
def animate(objects, particles, driver):
......@@ -93,8 +141,39 @@ def animate(objects, particles, driver):
for f in range(1, bcs.frame_end):
t = driver.delta_t*f
bcs.frame_current = f
print("Configuring Frame: "+str(f)+" of "+str(bcs.frame_end))
print("Configuring particles in frame: "+str(f)+" of "+str(bcs.frame_end))
for i in range(0, len(objects)):
bcs.objects.active=objects[i]
objects[i].location=(particles[i].Propagate(t))
objects[i].keyframe_insert(data_path='location')
# Function that animates particle tracks using the particle propagator class
def animate_tracks(tracks, particles, driver):
bcs = bpy.context.scene
#Animate tracks
for f in range(1, bcs.frame_end):
t = driver.delta_t*f
bcs.frame_current = f
print("Configuring tracks in frame: "+ str(f) +" of "+ str(bcs.frame_end))
for point in range(f,bcs.frame_end):
for i in range(0, len(particles)):
#bcs.objects.active=tracks[i]
tracks[i].data.splines[0].points[point].keyframe_insert(data_path="co", frame = f)
x, y, z = particles[i].Propagate(t)
tracks[i].data.splines[0].points[point].co = (x, y, z, 1)
##polyline = curveTrack.splines.new('NURBS')
##polyline.points.add(len(coords))
##for i, coord in enumerate(coords):
## x,y,z = coord
## polyline.points[i].co = (x, y, z, 1)
#curve = bpy.data.objects["Track"]
#curve.data.splines[0].points[1].co
#point.keyframe_insert(data_path="co", frame = i)
# https://blender.stackexchange.com/questions/73630/animate-curves-by-changing-spline-data-using-a-python-script
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment