from chroma import make, view
from chroma.geometry import Solid, Geometry
from chroma.transform import make_rotation_matrix
from chroma.demo.optics import glass, water, vacuum, r7081hqe_photocathode
from chroma.demo.optics import black_surface
import numpy as np
def build_pd(size, glass_thickness):
"""Returns a simple photodetector Solid. The photodetector is a cube of
size `size` constructed out of a glass envelope with a photosensitive
face on the inside of the glass envelope facing up."""
# outside of the glass envelope
outside_mesh = make.cube(size)
# inside of the glass envelope
inside_mesh = make.cube(size-glass_thickness*2)
# outside solid with water on the outside, and glass on the inside
outside_solid = Solid(outside_mesh,glass,water)
# now we need to determine the triangles which make up
# the top face of the inside mesh, because we are going to place
# the photosensitive surface on these triangles
# do this by seeing which triangle centers are at the maximum z
# coordinate
z = inside_mesh.get_triangle_centers()[:,2]
top = z == max(z)
# see np.where() documentation
# Here we make the photosensitive surface along the top face of the inside
# mesh. The rest of the inside mesh is perfectly absorbing.
inside_surface = np.where(top,r7081hqe_photocathode,black_surface)
inside_color = np.where(top,0x00ff00,0x33ffffff)
# construct the inside solid
inside_solid = Solid(inside_mesh,vacuum,glass,surface=inside_surface,
color=inside_color)
# you can add solids and meshes!
return outside_solid + inside_solid
def iter_box(nx,ny,nz,spacing):
"""Yields (position, direction) tuple for a series of points along the
boundary of a box. Will yield nx points along the x axis, ny along the y
axis, and nz along the z axis. `spacing` is the spacing between the points.
"""
dx, dy, dz = spacing*(np.array([nx,ny,nz])-1)
# sides
for z in np.linspace(-dz/2,dz/2,nz):
for x in np.linspace(-dx/2,dx/2,nx):
yield (x,-dy/2,z), (0,1,0)
for y in np.linspace(-dy/2,dy/2,ny):
yield (dx/2,y,z), (-1,0,0)
for x in np.linspace(dx/2,-dx/2,nx):
yield (x,dy/2,z), (0,-1,0)
for y in np.linspace(dy/2,-dy/2,ny):
yield (-dx/2,y,z), (1,0,0)
# bottom and top
for x in np.linspace(-dx/2,dx/2,nx)[1:-1]:
for y in np.linspace(-dy/2,dy/2,ny)[1:-1]:
# bottom
yield (x,y,-dz/2), (0,0,1)
# top
yield (x,y,dz/2), (0,0,-1)
size = 100
glass_thickness = 10
nx, ny, nz = 20, 20, 20
spacing = size*2
def build_detector():
"""Returns a cubic detector made of cubic photodetectors."""
g = Geometry(water)
for pos, dir in iter_box(nx,ny,nz,spacing):
# convert to arrays
pos, dir = np.array(pos), np.array(dir)
# we need to figure out what rotation matrix to apply to each
# photodetector so that the photosensitive surface will be facing
# `dir`.
if tuple(dir) == (0,0,1):
rotation = None
elif tuple(dir) == (0,0,-1):
rotation = make_rotation_matrix(np.pi,(1,0,0))
else:
rotation = make_rotation_matrix(np.arccos(dir.dot((0,0,1))),
np.cross(dir,(0,0,1)))
# add the photodetector
g.add_solid(build_pd(size,glass_thickness),rotation=rotation,
displacement=pos)
world = Solid(make.box(spacing*nx,spacing*ny,spacing*nz),water,vacuum,
color=0x33ffffff)
g.add_solid(world)
return g
if __name__ == '__main__':
from chroma.sim import Simulation
from chroma.sample import uniform_sphere
from chroma.event import Photons
from chroma.loader import load_bvh
from chroma.generator import vertex
import matplotlib.pyplot as plt
g = build_detector()
g.flatten()
g.bvh = load_bvh(g)
sim = Simulation(g)
# photon bomb from center
def photon_bomb(n,wavelength,pos):
pos = np.tile(pos,(n,1))
dir = uniform_sphere(n)
pol = np.cross(dir,uniform_sphere(n))
wavelengths = np.repeat(wavelength,n)
return Photons(pos,dir,pol,wavelengths)
# write it to a root file
from chroma.io.root import RootWriter
f = RootWriter('test.root')
# sim.simulate() always returns an iterator even if we just pass
# a single photon bomb
for ev in sim.simulate([photon_bomb(1000,400,(0,0,0))],
keep_photons_beg=True,keep_photons_end=True,
run_daq=False,max_steps=100):
# write the python event to a root file
f.write_event(ev)
# simulate some electrons!
gun = vertex.particle_gun(['e-']*10,
vertex.constant((0,0,0)),
vertex.isotropic(),
vertex.flat(500,1000))
for ev in sim.simulate(gun,keep_photons_beg=True,keep_photons_end=True,
run_daq=False,max_steps=100):
f.write_event(ev)
detected = (ev.photons_end.flags & (0x1 << 2)).astype(bool)
plt.hist(ev.photons_end.t[detected],100)
plt.xlabel('Time (ns)')
plt.title('Photon Hit Times')
plt.show()
f.close()