29 import xboa.core.Hitcore
30 from xboa.core.Hitcore
import Hitcore
33 from numpy
import matrix
39 BadEventError is raised if Hit reads a bad
50 Represents a particle at a point in some phase space. Hit contains functions for i/o,
51 as well as accessors for rectangular or cylindrical coordinate systems and functions to
52 perform translations, abelian transformations etc.
54 Hit has the following variables (stored for real in C struct Hitcore)
56 - **x** transverse horizontal position
57 - **y** transverse vertical position
58 - **z** longitudinalposition
60 - **px** transverse horizontal component of momentum
61 - **py** transverse vertical component of momentum
62 - **pz** longitudinal component of momentum
63 - **energy** total energy
64 - **local_weight** local statistical weight (for a particular hit)
65 - **mass** particle mass
66 - **bx** horizontal component of magnetic field
67 - **by** vertical component of magnetic field
68 - **bz** longitudinal component of magnetic field
69 - **ex** x component of electric field
70 - **ey** y component of electric field
71 - **ez** z component of electric field
72 - **sx** x component of spin vector
73 - **sy** y component of spin vector
74 - **sz** z component of spin vector
75 - **path_length** total distance traversed by a particle
76 - **proper_time** proper time of the particle
77 - **e_dep** energy deposited, as registered by a Monte Carlo code
78 - **charge** particle charge, in units of electron charge
79 - **station** output plane index
80 - **pid** PDG particle ID (am I an electron? am I a proton?)
81 - **status** is the particle track okay? (code dependent)
82 - **spill** indexes the spill (for MICE)
83 - **event_number** indexes the event
84 - **particle_number** indexes the particle track within the event
88 - global_weight is a global statistical weight (for a particular particle).
89 All hits with the same (spill, event_number, particle_number) will register
90 the same global weight.
93 __slots__ = [
'__hitcore']
96 """Initialise to an empty event. Alternatively use static initialisers defined below - I prefer static initialisers"""
100 """Formatting for print command"""
104 """Shallow copy i.e. copy as reference"""
109 """Deep copy i.e. copy as data"""
113 def __eq__(self, target, float_tolerance=Common.float_tolerance):
114 """Test for equality of data values between self and target"""
115 if type(self) != type(target):
return False
116 for key
in self.__hitcore.get_variables():
117 if abs(self.__hitcore.get(key) - target.__hitcore.get(key)) > float_tolerance:
return False
120 def __ne__(self, target, float_tolerance=Common.float_tolerance):
121 """Test for inequality of data values between self and target"""
122 return not self.
__eq__(target, float_tolerance)
125 """Mimic some aspects of dict"""
126 return self.
get(variable)
129 """Mimic some aspects of dict"""
130 return self.
set(variable, value)
139 Static function returns a new hit object, setting data using string:value dict. Then forces E^2=p^2+m^2 by changing mass_shell_string.
141 - set_dict = dict of string:value pairs where strings are from set_variables()
142 - mass_shell_string = string from list mass_shell_variables that references the value that will be changed to force E^2=p^2+m^2
144 e.g. myHit = Hit.new_from_dict({'x':5, 'y':0, 'z':100, 'px':0, 'py':5, 'pz':200, 'pid':-13}, 'energy' )
147 for k,v
in set_dict.iteritems():
149 if(mass_shell_string !=
''):
150 my_hit.mass_shell_condition(mass_shell_string)
152 new_from_dict = staticmethod(new_from_dict)
156 Static function returns a new hit object, read from filehandle with a built-in format
158 - format = string from the list file_types() that defines the format of the input
159 - filehandle = filehandle from which the hit object will be read
161 e.g. myHit = Hit.new_from_read_builtin('zgoubi', myfile)
162 Note that this will read one event, typically corresponding to one line in <filehandle>
165 if format ==
'opal_loss':
166 opal_read = hit.__read_opal_loss_file(filehandle)
168 hit.read_builtin_formatted(format, filehandle)
170 new_from_read_builtin = staticmethod(new_from_read_builtin)
174 Static function returns a new hit object sets data using string/value pairs from set_dict and forces E^2=p^2+m^2 by changing mass_shell_string
176 - format_list = ordered list of variables from get_variables() that contains the particle variables on each line of your input file
177 - format_units_dict = dict of variables:units that are used to transform to internal system of units
178 - filehandle = file handle, created using e.g. filehandle = open('for009.dat')
180 e.g. myHit = Hit.new_from_read_user(['x','y','z','px','py','pz'], {'x':'mm', 'y':'mm', 'z':'mm', 'px':'MeV/c', 'py':'MeV/c', 'pz':'MeV/c'}, my_input_file)
183 hit.read_user_formatted(format_list, format_units_dict, filehandle)
185 new_from_read_user = staticmethod(new_from_read_user)
189 Static function returns a list of hit objects found in the spill
191 - maus_types = list of types to take from the maus file. Types which are not maus_root_types are ignored
192 - root_spill = maus spill data
193 - spill_number = maus spill number (used as the event number)
195 Returns a list of hit objects. Station number will be taken from the relevant maus_type. event_number will be given by the spill_number. track_number will be given by
196 the index on mc_events or recon_events
199 spill = root_spill.GetSpill()
200 for i
in range(spill.GetMCEventSize()):
201 for maus_root_type, getter
in Hit.__maus_root_mc_types.iteritems():
202 if maus_root_type
in maus_types:
203 new_hits = getter(spill.GetAnMCEvent(i), i)
205 hit[
'event_number'] = i
207 for i
in range(spill.GetReconEventSize()):
208 for maus_root_type, getter
in Hit.__maus_root_recon_types.iteritems():
209 if maus_root_type
in maus_types:
210 new_hits = getter(spill.GetAReconEvent(i), i)
212 hit[
'event_number'] = i
215 hit[
'spill'] = spill_number
217 print 'AARGH - pid = found'
219 new_list_from_maus_root_spill = staticmethod(new_list_from_maus_root_spill)
223 Convert a dict from a maus hit to an xboa hit
225 - type_name = name of the maus type
226 - maus_dict = dict containing maus data
227 - event_number = indexes the originating spill
230 three_vec_conversions = Hit.__maus_three_vec_conversions[type_name]
231 conversion_dict = Hit.__maus_variable_conversions[type_name]
232 for maus_name, xboa_suffix
in three_vec_conversions.iteritems():
233 for maus_xyz, value
in maus_dict[maus_name].iteritems():
234 xboa_dict[xboa_suffix+maus_xyz] = value
235 for maus_key, xboa_key
in conversion_dict.iteritems():
236 xboa_dict[xboa_key] = maus_dict[maus_key]
237 xboa_dict[
'event_number'] = event_number
238 for key, value
in Hit._Hit__file_units[type_name]:
239 xboa_dict[key] /= Hit._Hit__file_units[type_name][key]
240 if 'mass' not in xboa_dict.keys():
241 xboa_dict[
'mass'] = Common.pdg_pid_to_mass[abs(xboa_dict[
'pid'])]
242 if 'charge' not in xboa_dict.keys():
243 xboa_dict[
'charge'] = Common.pdg_pid_to_charge[xboa_dict[
'pid']]
244 return Hit.new_from_dict(xboa_dict, Hit.__file_mass_shell[type_name])
245 new_from_maus_object = staticmethod(new_from_maus_object)
248 """Return a shallow copy of self (copying data as references)"""
252 """Return a deep copy of target (deep copying target's data to self as well)"""
254 target = copy.deepcopy(self)
259 def get_vector(self, get_variable_list, origin_dict={}):
261 Return a numpy vector of data values taking data from get_variable_list, relative to some origin
263 - get_variable_list = list of variable strings from get_variables()
264 - origin_dict = dict of variable strings to origin value; if not set, assumes 0
266 e.g. transverse_vector = myHit.get_vector(['x', 'y', 'px', 'py'])
270 for key
in get_variable_list:
271 if key
in origin_dict: origin = origin_dict[key]
273 my_list.append(self.
get(key) - origin)
274 return matrix(numpy.array(my_list))
276 def translate(self, translation_dict, mass_shell_string):
278 Iterate over translation_dict and add the value in the dict to the value stored in Hit. Then force E^2 = p^2 + m^2
280 - translation_dict = dict of strings from list set_variables() to floats
281 - mass_shell_string = string from list mass_shell_variables()
283 for k,v
in translation_dict.iteritems():
284 self.
set(k, v+self.
get(k))
287 def abelian_transformation(self, rotation_list, rotation_matrix, translation_dict={}, origin_dict={}, mass_shell_variable=''):
289 Perform an abelian transformation about the origin, i.e. V_out - O = R*(V_in-O) + T.
290 Then force E^2 = p^2 + m^2
292 - rotation_list = list of variables to be rotated
293 - rotation_matrix = matrix R
294 - translation_dict = dict of strings from set_variables() to floats. Becomes O
295 - mass_shell_variable = string from list mass_shell_variables()
296 - origin_dict = dict of strings from set_variables() to floats. Becomes t
298 e.g. hit.abelian_transformation(['x','px'], array[[1,0.5],[0,1]],{'x':10},'energy') will
299 look like a drift space plus a translation
301 vector = (self.
get_vector(rotation_list)).transpose()
302 origin = copy.deepcopy(origin_dict)
303 trans = copy.deepcopy(translation_dict)
304 for key
in rotation_list:
305 if not key
in origin: origin[key] = 0
306 if not key
in trans: trans [key] = 0
307 for i
in range( len(rotation_list) ):
308 vector[i,0] -= origin[rotation_list[i]]
309 vector = rotation_matrix*vector
310 for i
in range( len(rotation_list) ):
311 self.
set(rotation_list[i], float(vector[i,0]+trans[rotation_list[i]]+origin[rotation_list[i]]))
317 Change variable represented by variable_string to force E^2 = p^2 + m^2
319 - variable_string = string which should be one of the list mass_shell_variables().
321 if(variable_string ==
''):
326 e = self.
get(
'energy')
328 if(variable_string ==
'p'):
329 self.
set(
'p', ( (e-m)*(e+m) )**0.5 )
330 elif(variable_string ==
'px'):
331 val = (e*e-m*m-py*py-pz*pz)
332 if val>float_tolerance: self.
set(
'px', abs(val)**1.5/val )
333 else: self.
set(
'px', 0.)
334 elif(variable_string ==
'py'):
335 val = (e*e-m*m-px*px-pz*pz)
336 if val>float_tolerance: self.
set(
'py', abs(val)**1.5/val )
337 else: self.
set(
'py', 0.)
338 elif(variable_string ==
'pz'):
339 val = (e*e-m*m-px*px-py*py)
340 if val>float_tolerance: self.
set(
'pz', abs(val)**1.5/val )
341 else: self.
set(
'pz', 0.)
342 elif(variable_string ==
'energy'):
343 self.
set(
'energy', (m*m+px*px+py*py+pz*pz) **0.5 )
345 raise IndexError(
'mass_shell_condition did not recognise \''+str(variable_string)+
'\'. Options are '+str(self.
__mass_shell_variables))
351 Return the value referenced by key
353 - key = string which should be one of the list get_variables()
356 return self.__hitcore.get(key)
362 raise IndexError(
'Key \''+str(key)+
'\' could not be found for Hit.get() - should be one of '+str(Hit.get_variables()))
364 def set(self, key, value):
366 Set the value referenced by key
368 - key = string which should be one of the list get_variables()
372 self.__hitcore.set(key, value)
378 raise IndexError(
'Key \''+str(key)+
'\' could not be found for Hit.set() - should be one of '+str(Hit.set_variables()))
380 def check(self, tolerance_float=1e-3):
381 """Return True if mass shell condition is obeyed and pid is correct for the mass else return False"""
382 pid = self.
get(
'pid')
383 if (
not abs(pid)
in Common.pdg_pid_to_mass)
and (
not pid
in Hit.__bad_pids)
and (
not pid == 0):
384 print 'pid not recognised',self.
get(
'pid')
386 if abs(pid)
in Common.pdg_pid_to_mass.keys():
387 if abs(self.
get(
'mass')-Common.pdg_pid_to_mass[abs(pid)]) > tolerance_float:
388 print 'Mass',self.
get(
'mass'),
'does not match pid',self.
get(
'pid')
390 if abs(round(self.
get(
'p')**2 + self.
get(
'mass')**2) - round(self.
get(
'energy')**2)) > tolerance_float :
396 Return a dict that uniquely defines the hit, so that new_from_dict(dict_from_hit(hit)) returns a copy of hit
399 for key
in self.__hitcore.set_variables():
400 my_dict[key] = self.__hitcore.get(key)
407 Write to a file formatted according to built-in file_type format
409 - format = string from file_types
410 - file_handle = file handle made using e.g. open() command
412 e.g. aHit.write_builtin_formatted('icool_for009', for009_dat) would write aHit in icool_for009 format to for009_dat
414 if( format.find(
'maus') > -1 ):
415 raise IOError(
"Can't write single maus hits, only lists of hits")
416 if( format.find(
'muon1_csv') > -1 ):
417 raise IOError(
"muon1 is only available for reading - sorry")
418 if( format.find(
'icool') > -1 ):
419 self.
set(
'pid', Common.pdg_pid_to_icool[self.
get(
'pid')])
420 if( format.find(
'mars') > -1 ):
421 self.
set(
'pid', Common.pdg_pid_to_mars [self.
get(
'pid')])
423 if( format.find(
'icool') > -1 ):
424 self.
set(
'pid', Common.icool_pid_to_pdg[self.
get(
'pid')])
425 if( format.find(
'mars') > -1 ):
426 self.
set(
'pid', Common.mars_pid_to_pdg [self.
get(
'pid')])
430 Write a list of hits to a file formatted according to built-in file_type format
432 - format = string from file_types
433 - file_handle = file handle made using e.g. open() command
434 - user_comment = comment included in some output formats (e.g. problem title, etc)
436 e.g. aHit.write_builtin_formatted('icool_for009', for009_dat) would write aHit in icool_for009 format to for009_dat
438 if( file_type_string.find(
'maus_root') > -1 ):
439 raise IOError(
"Can't write maus_root formats")
440 filehandle = Hit.open_filehandle_for_writing(file_type_string, file_name, user_comment)
441 if( file_type_string.find(
'maus') > -1 ):
443 if file_type_string
in Hit.__force_unique_particle_number:
444 list_of_hits = Hit.force_unique_particle_number(list_of_hits)
445 maus_tree = Hit.get_maus_tree(list_of_hits, file_type_string)
446 for item
in maus_tree:
447 print >>filehandle,json.dumps(item)
449 if file_type_string.find(
'g4mice') > -1:
450 comptor = (Hit.__event_cmp)
452 comptor = (Hit.__station_cmp)
453 list_of_hits.sort(comptor)
456 for hit_in
in list_of_hits:
457 if old_hit ==
None or comptor(hit_in, old_hit) == 0:
458 current_hits.append(hit_in)
460 if file_type_string ==
'g4mice_special_hit':
461 filehandle.write(str(len(current_hits))+
' SpecialHits\n')
462 if file_type_string ==
'g4mice_virtual_hit':
463 filehandle.write(str(len(current_hits))+
' VirtualHits\n')
464 for hit_out
in current_hits:
465 try: hit_out.write_builtin_formatted(file_type_string, filehandle)
467 if file_type_string.find(
'g4mice' ) > -1: filehandle.write(
'-1 STOP\n')
468 current_hits = [hit_in]
470 if file_type_string ==
'g4mice_special_hit':
471 filehandle.write(str(len(current_hits))+
' SpecialHits\n')
472 if file_type_string ==
'g4mice_virtual_hit':
473 filehandle.write(str(len(current_hits))+
' VirtualHits\n')
474 for hit_out
in current_hits:
476 hit_out.write_builtin_formatted(file_type_string, filehandle)
478 print 'Warning - failed to write ',hit_out
479 if file_type_string.find(
'g4mice' ) > -1: filehandle.write(
'-1 STOP\n')
482 write_list_builtin_formatted = staticmethod(write_list_builtin_formatted)
486 Open a file handle of the specified type for writing. Some filehandles need special care, e.g. some are gzipped etc
488 - file_type_string = open filehandle for this file type
489 - file_name = string name of the file
492 if file_type_string.find(
'g4mice') > -1: filehandle = gzip.GzipFile(file_name,
'w')
493 else: filehandle = open(file_name,
'w')
494 filehandle.write(Hit.file_header(file_type_string, user_comment))
496 open_filehandle_for_writing = staticmethod(open_filehandle_for_writing)
498 def file_header(file_type_string, user_comment=None):
500 Return the file_header for the given file_type. Optionally, can add a user comment
502 - file_type_string = header returned for this file type. Select from file_types()
503 - user_comment = add a user comment - default is 'File generated by xboa'
505 e.g. Hit.file_header('icool_for009', 'This is my for009 file') would set 'This is my for009 file'
506 as a user comment and return the header string
508 if user_comment ==
None: file_header = Hit.__file_headers[file_type_string].replace(str(
'<user>'), str(Hit.__default_user_string))
509 else: file_header = Hit.__file_headers[file_type_string].replace(str(
'<user>'), str(user_comment))
511 file_header = staticmethod(file_header)
515 Read a single event (typically a single line) from a file formatted according to built-in file_type format
517 - format = string from file_types
518 - file_handle = file handle made using e.g. open() command
520 e.g. aHit.read_builtin_formatted('icool_for009', for009_dat) would read aHit in icool_for009 format from for009_dat
522 if( format.find(
'maus') > -1 ):
523 raise IOError(
"Can't read single maus hits, only bunches")
524 if (format ==
'muon1_csv'):
528 if( format.find(
'icool') > -1 ):
529 self.
set(
'pid', Common.icool_pid_to_pdg[self.
get(
'pid')])
530 if( format.find(
'mars') > -1 ):
531 self.
set(
'pid', Common.mars_pid_to_pdg[self.
get(
'pid')])
533 self.
set(
'mass', Common.pdg_pid_to_mass[abs(self.
get(
'pid'))])
539 print 'Warning - could not resolve PID ',self.
get(
'pid'),
' setting mass to 0.'
540 self.__bad_pids.append(self.
get(
'pid'))
543 self.
set(
'charge', Common.pdg_pid_to_charge[self.
get(
'pid')])
546 print 'Warning - could not resolve PID ',self.
get(
'pid'),
' setting charge to 0.'
547 self.__bad_pids.append(self.
get(
'pid'))
551 Write to a file formatted according to built-in file_type format
553 - format_list = ordered list of strings from get_variables()
554 - format_units_dict = dict of formats from format_list to units
555 - file_handle = file handle made using e.g. open() command
557 e.g. aHit.write_user_formatted(['x','px','y','py'], ['x':'m','y':'m','px':'MeV/c','py':'MeV/c'], some_file, '@') would make output like
558 0.001@0.002@0.001@0.002 in some_file
565 Read to a file formatted according to built-in file_type format
567 - format_list = ordered list of strings from get_variables()
568 - format_units_dict = dict of formats from format_list to units
569 - file_handle = file handle made using e.g. open() command
571 e.g. aHit.write_user_formatted('icool_for009', for009_dat) would write aHit in icool_for009 format to for009_dat
574 self.
set(
'mass', Common.pdg_pid_to_mass[abs(self.
get(
'pid'))] );
577 """Static function returns a list of available file types"""
578 return Hit.__file_types
579 file_types = staticmethod(file_types)
583 Force all hits in the list to have unique particle numbers
585 - list_of_hits = list of hits to check for particle numbers
587 Returns list_of_hits with particle numbers updated to ensure they are unique
588 Starts adding new particle numbers indexed from 0
591 new_hits = [
'']*len(list_of_hits)
592 for i, hit
in enumerate(list_of_hits):
593 new_hits[i] = copy.deepcopy(hit)
594 particle_numbers = []
595 lowest_unused_int = 0
596 for i, hit
in enumerate(new_hits):
597 while lowest_unused_int
in particle_numbers:
598 lowest_unused_int += 1
599 if new_hits[i][
'particle_number']
in particle_numbers:
600 new_hits[i][
'particle_number'] = lowest_unused_int
601 particle_numbers.append(new_hits[i][
'particle_number'])
603 force_unique_particle_number = staticmethod(force_unique_particle_number)
607 Convert from list of hits to a tree of maus objects
609 - list_of_hits = list of hits to be converted
610 - type_name = maus type, used to define position in the maus tree
612 Return value is a list of maus spills (each of which is a data tree)
617 return Hit.__get_maus_tree_recursive(list_of_hits, [[
"event_number"]]+Hit.__maus_paths[type_name], type_name)
618 get_maus_tree = staticmethod(get_maus_tree)
622 Convert from hit to a maus dict for MAUS IO
624 - type_name = name of the maus type to generate
626 Returns a tuple of (maus_dict, spill_number)
629 three_vec_conversions = Hit.__maus_three_vec_conversions[type_name]
630 conversion_dict = Hit.__maus_variable_conversions[type_name]
631 for maus_name, xboa_suffix
in three_vec_conversions.iteritems():
632 maus_dict[maus_name] = {}
633 for xyz
in [
'x',
'y',
'z']:
634 maus_dict[maus_name][xyz] = self[xboa_suffix+xyz]
635 for maus_key, xboa_key
in conversion_dict.iteritems():
636 maus_dict[maus_key] = self[xboa_key]
637 for key, value
in Hit._Hit__file_units[type_name]:
638 xboa_dict[key] *= Hit._Hit__file_units[type_name][key]
639 return (maus_dict, self[
'event_number'])
644 """Static function returns a list of variables suitable for mass_shell_condition calls"""
645 return Hit.__mass_shell_variables
646 mass_shell_variables = staticmethod(mass_shell_variables)
649 """Static function returns a list of variable suitable for get calls"""
650 return Hit.__get_keys
651 get_variables = staticmethod(get_variables)
654 """Static function returns a list of variable suitable for set calls"""
655 return Hit.__set_keys
656 set_variables = staticmethod(set_variables)
660 If Hit fails to read a pid, it is stored in list bad_pids for later reference. Returns the list
662 return Hit.__bad_pids
663 get_bad_pids = staticmethod(get_bad_pids)
667 If Hit fails to read a pid, it is stored in list bad_pids for later reference. Set the list
669 Hit.__bad_pids = bad_pid_list
670 set_bad_pids = staticmethod(set_bad_pids)
674 Returns a dict of <maus_type>:<path> where <maus_type> is a string type name and <path> is a list
675 that tells how data is stored in maus json dicts.
677 return Hit.__maus_paths
678 get_maus_paths = staticmethod(get_maus_paths)
683 Set up opal loss file pid
685 - pid = (int) pdg particle id of particles in the opal_loss file
688 set_opal_pid = staticmethod(set_opal_pid)
692 Get opal loss file pid
694 return Hit.__opal_pid
695 get_opal_pid = staticmethod(get_opal_pid)
699 Set up opal probe name to station mapping
701 - probes = Dict mapping probe name to station number. Stations should be
702 numbered sequentially from 1. Station number will be calculated
703 like hit['station'] = probe_index+n_probes*turn_number. In any
704 case, xboa will add any unrecognised probe commands as a new
707 Hit.__opal_probes = probes
708 set_opal_probes = staticmethod(set_opal_probes)
713 """Returns total momentum of the hit"""
714 return (self.
get(
'px')**2+self.
get(
'py')**2+self.
get(
'pz')**2)**0.5
717 """Returns transverse distance (i.e. in x,y space) from 0,0"""
718 return (self.
get(
'x')**2+self.
get(
'y')**2)**0.5
721 """Returns transverse angle (i.e. in x,y space) in range (-pi, pi); phi = 0. is positive y and phi = pi/2 is positive x"""
722 return math.atan2(self[
'y'], self[
'x'])
725 """Returns transverse momentum of the hit"""
726 return (self[
'px']**2+self[
'py']**2)**0.5
729 """Returns transverse angle of momentum (i.e. in px,py space) in range (-pi, pi); phi = 0. is positive py and phi = pi/2 is positive px"""
730 return math.atan2(self[
'py'], self[
'px'])
733 """Returns x divergence i.e. px/pz of the hit"""
734 return (self[
'x']**2+self[
'y']**2)
737 """Returns x divergence i.e. px/pz of the hit"""
738 return (self[
'px']/self[
'pz'])
741 """Returns y divergence i.e. py/pz of the hit"""
742 return (self[
'py']/self[
'pz'])
745 """Returns t \'divergence\' i.e. E/pz of the hit"""
746 return (-self[
'energy']/self[
'pz'])
749 """Returns dr/dz = pt/pz of the hit"""
750 return (self.
get(
'pt')/self[
'pz'])
753 """Returns absolute value of the spin"""
754 return (self.
get(
'sx')**2+self.
get(
'sy')**2+self.
get(
'sz')**2)**0.5
757 """Returns speed_of_light*t of the hit"""
758 return (self[
't']*Common.constants[
'c_light'])
761 """Returns total energy - mass, ie kinetic energy of the hit"""
762 return self[
'energy'] - self.
get(
'mass')
764 def set_ek (self, value_float):
765 """Sets kinetic energy = total energy - mass of the hit"""
766 self[
'energy'] = value_float + self.
get(
'mass')
769 """Returns kinetic angular momentum about the z-axis.
770 To use a different axis, you will have to perform your own transformation"""
771 return self[
'x']*self[
'py'] - self[
'y']*self[
'px']
773 def set_ct (self, value_float):
774 """Sets t = value_float/c_light"""
775 self[
't'] = value_float/Common.constants[
'c_light']
777 def set_p(self, value_float):
778 """Set p to value_float keeping momentum direction constant"""
782 scale = value_float/self.
get_p()
787 def set_xP (self, value_float):
788 """Set x\' to value_float keeping pz constant"""
789 if(math.fabs(self[
'pz']) < 1e-9):
raise FloatingPointError(
'Cant set x\' while pz is 0')
790 self[
'px'] = value_float*self[
'pz']
792 def set_yP (self, value_float):
793 """Set y\' to value_float keeping pz constant"""
794 if(math.fabs(self[
'pz']) < 1e-9):
raise FloatingPointError(
'Cant set y\' while pz is 0')
795 self[
'py'] = value_float*self[
'pz']
797 def set_tP (self, value_float):
798 """Set t\' (dt/dz=-E/pz) to value_float keeping pz constant; note sign of pz may change"""
799 if(math.fabs(self[
'pz']) < 1e-9):
raise FloatingPointError(
'Cant set t\' while pz is 0')
800 self[
'energy'] = -value_float*self[
'pz']
802 self[
'energy'] *= -1.
804 if self[
'energy'] < self.
get(
'mass'):
raise FloatingPointError(
'Energy less than muon mass')
807 """Returns total weight for this Hit"""
808 return self.
get(
'global_weight')*self.
get(
'local_weight')
811 """Set all global weights to 1"""
812 Hitcore.clear_global_weights()
813 clear_global_weights = staticmethod(clear_global_weights)
816 """Clear memory allocated to global weights - also resets global weights to 1"""
817 raise NotImplementedError(
"delete_global_weights is deprecated - please use clear_global_weights")
818 delete_global_weights = staticmethod(delete_global_weights)
821 """Returns local weight for this Hit"""
822 return self.
get(
'local_weight')
825 """Set local weight for this Hit to be value"""
826 self.
set(
'local_weight', value)
829 """Returns global weight for this Hit"""
830 return self.
get(
'global_weight')
833 """Set global weight for this Hit to be value"""
834 self.
set(
'global_weight', value)
838 g4beamline_track_file can take different units for length - set the unit here
840 - unit = string that is a unit of length
842 e.g. set_g4bl_unit('m') would set the length unit to metres
844 Hit.__file_units[
'g4beamline_bl_track_file'][
'x'] = unit
845 Hit.__file_units[
'g4beamline_bl_track_file'][
'y'] = unit
846 Hit.__file_units[
'g4beamline_bl_track_file'][
'z'] = unit
847 set_g4bl_unit = staticmethod(set_g4bl_unit)
855 line = file_handle.next()
856 except StopIteration:
857 raise EOFError(
"End of file reached")
860 if not(len(words) == len(format_list)):
861 raise IOError(
"Read operation failed with line "+str(line))
862 for key
in format_list:
866 value = Hit.__default_var_types[key](value)
869 value = Hit.__default_var_types[key](value)
870 if Hit.__default_var_types[key] == float:
871 value *= Common.units[format_units_dict[key]]
878 probes = Hit.__opal_probes
881 raise RuntimeError(
"Error - need to set pid using Hit.set_opal_pid()")
883 line = file_handle.next()
884 except StopIteration:
885 raise EOFError(
"End of file reached")
887 if words[0]
in Hit.__opal_ignore_probes:
889 if not len(words) == 10:
891 if words[0]
not in probes.keys():
892 probes[words[0]] = len(probes.keys())
893 hit_dict = dict( (key, float(words[i+1]) )
for i, key
in enumerate([
'x',
'y',
'z',
'px',
'py',
'pz',
'event_number',
'station',
't']))
894 hit_dict[
'pid'] = pid
895 hit_dict[
'mass'] = Common.pdg_pid_to_mass[abs(pid)]
896 hit_dict[
'charge'] = Common.pdg_pid_to_charge[pid]
897 hit_dict[
'station'] = probes[words[0]]+len(probes)*(int(words[8])-1)
898 for item
in [
'px',
'py',
'pz']:
899 hit_dict[item] *= hit_dict[
'mass']
900 for key, value
in hit_dict.iteritems():
907 line = filehandle.next()
908 except StopIteration:
909 raise EOFError(
"End of file reached")
911 words = line.split(
',')
916 line = filehandle.next()
917 words = line.split(
',')
919 except StopIteration:
920 raise EOFError(
"End of file reached")
924 hit_dict = dict( (key, float(words[i]) )
for i, key
in enumerate([
'x',
'y',
't',
'px',
'py',
'pz']))
926 pid = Common.muon1_pid_to_pdg[words[-2]]
927 mass = Common.pdg_pid_to_mass[abs(pid)]
928 mom_units = mass/(Common.constants[
'c_light']/Common.units[
'm']*Common.units[
's'])
929 units = {
'x':Common.units[
'm'],
'y':Common.units[
'm'],
't':Common.units[
's'],
'px':mom_units,
'py':mom_units,
'pz':mom_units}
930 for key, value
in hit_dict.iteritems():
931 hit_dict[key] *= units[key]
932 hit_dict[
'pid'] = pid
933 hit_dict[
'mass'] = mass
934 hit_dict[
'charge'] = Common.pdg_pid_to_charge[hit_dict[
'pid']]
935 hit_dict[
'station'] = 0
936 for key, value
in hit_dict.iteritems():
941 def __write_formatted(self, format_list, format_units_dict, file_handle, separator=' '):
942 for key
in format_list:
943 if key ==
'': value = 0
944 else: value = Hit.__default_var_types[key](self.
get(key)/Common.units[ format_units_dict[key] ])
945 file_handle.write( str( value )+separator)
946 file_handle.write(
'\n')
951 for i
in range(mc_event.GetVirtualHitsSize()):
952 maus_hit = mc_event.GetAVirtualHit(i)
953 pid = maus_hit.GetParticleId()
954 hit_dict = {
'pid':pid,
't':maus_hit.GetTime(),
'charge':maus_hit.GetCharge(),
'proper_time':maus_hit.GetProperTime(),
955 'path_length':maus_hit.GetPathLength(),
'station':maus_hit.GetStationId(),
956 'x':maus_hit.GetPosition().x(),
'y':maus_hit.GetPosition().y(),
'z':maus_hit.GetPosition().z(),
957 'px':maus_hit.GetMomentum().x(),
'py':maus_hit.GetMomentum().y(),
'pz':maus_hit.GetMomentum().z(),
958 'bx':maus_hit.GetBField().x(),
'by':maus_hit.GetBField().y(),
'bz':maus_hit.GetBField().z(),
959 'ex':maus_hit.GetEField().x(),
'ey':maus_hit.GetEField().y(),
'ez':maus_hit.GetEField().z(),
960 'particle_number':maus_hit.GetTrackId()}
961 hit_dict[
'mass'] = Common.pdg_pid_to_mass[abs(pid)]
962 if hit_dict[
'pid'] != 0:
963 hit_list.append(Hit.new_from_dict(hit_dict,
'energy'))
965 print 'Warning - pid 0 detected in maus_root_virtual_hit; hit will not be loaded'
967 __get_maus_root_virtual_hits = staticmethod(__get_maus_root_virtual_hits)
970 maus_hit = mc_event.GetPrimary()
971 pid = maus_hit.GetParticleId()
972 hit_dict = {
'pid':pid,
'energy':maus_hit.GetEnergy(),
't':maus_hit.GetTime(),
973 'x':maus_hit.GetPosition().x(),
'y':maus_hit.GetPosition().y(),
'z':maus_hit.GetPosition().z(),
974 'px':maus_hit.GetMomentum().x(),
'py':maus_hit.GetMomentum().y(),
'pz':maus_hit.GetMomentum().z()}
975 hit_dict[
'particle_number'] = 0
976 hit_dict[
'charge'] = Common.pdg_pid_to_charge[abs(pid)]
977 hit_dict[
'mass'] = Common.pdg_pid_to_mass[abs(pid)]
978 return [Hit.new_from_dict(hit_dict,
'energy')]
979 __get_maus_root_primary_hits = staticmethod(__get_maus_root_primary_hits)
985 value = item[sort_attribute]
986 if not value
in a_dict: a_dict[value] = []
987 a_dict[value].append(item)
988 return a_dict.values()
989 __split_list_by_equality = staticmethod(__split_list_by_equality)
993 if len(maus_path) == 1:
994 if type(maus_path[0]) == type(
""):
995 if len(list_of_hits) > 1:
996 raise IOError(
"More than one hit for maus key")
997 return {maus_path[0]:list_of_hits[0].
get_maus_dict(format)[0]}
998 if type(maus_path[0]) == type([]):
999 return [hit.get_maus_dict(format)[0]
for hit
in list_of_hits]
1000 if type(maus_path[0]) == type([]):
1001 list_of_hits_new = Hit.__split_list_by_equality(list_of_hits, maus_path[0][0])
1002 my_output = [Hit.__get_maus_tree_recursive(x, maus_path[1:], format)
for x
in list_of_hits_new]
1003 if len(maus_path) == 1:
1005 for out
in my_output: output += out
1006 else: output = my_output
1008 if type(maus_path[0]) == type(
""):
1009 return {maus_path[0]:Hit.__get_maus_tree_recursive(list_of_hits, maus_path[1:], format)}
1010 __get_maus_tree_recursive = staticmethod(__get_maus_tree_recursive)
1020 __file_types = [
'icool_for009',
'icool_for003',
'g4beamline_bl_track_file',
'g4mice_special_hit',
'g4mice_virtual_hit',
'mars_1',
'muon1_csv']
1023 __file_types += [
'maus_virtual_hit',
'maus_primary']
1028 __file_types += [
'maus_root_virtual_hit',
'maus_root_primary']
1032 __mass_shell_variables = [
'',
'p',
'px',
'py',
'pz',
'energy']
1033 __get_variables = {
'p':get_p,
'r':get_r,'phi':get_phi,'pt':get_pt,'pphi':get_pphi,'x\'':get_xP,'y\'':get_yP,'t\'':get_tP, 'ct\'':get_tP,'r\'':get_rP,'spin':get_spin,
1034 'weight':get_weight,
'ct':get_ct,
'r_squared':get_r_squared,
'z\'':__return_one,
'kinetic_energy':get_ek,
1035 'l_kin':get_l_kin,
'':__do_nothing}
1036 __set_variables = {
'p':set_p,
'x\'':set_xP,
'y\'':set_yP,
't\'':set_tP,
'ct':set_ct,
'kinetic_energy':set_ek,
'':__do_nothing}
1037 __default_var_types = {
'x':float,
'y':float,
'z':float,
't':float,
'px':float,
'py':float,
'pz':float,
'energy':float,
'bx':float,
'by':float,
'bz':float,
1038 'ex':float,
'ey':float,
'ez':float,
'eventNumber':int,
'event_number':int,
'particleNumber':int,
'particle_number':int,
'pid':int,
'status':int,
'station':int,
'local_weight':float,
1039 'sx':float,
'sy':float,
'sz':float,
'mass':float,
'path_length':float,
'proper_time':float,
'e_dep':float,
'charge':float}
1043 for key
in Hitcore.get_variables():
1044 __get_keys.append(key)
1045 for key
in Hitcore.set_variables():
1046 __set_keys.append(key)
1047 for key, value
in __get_variables.iteritems():
1048 __get_keys.append(key)
1049 for key, value
in __set_variables.iteritems():
1050 __set_keys.append(key)
1051 for key, value
in __get_variables.iteritems():
1052 if not key
in __default_var_types:
1053 __default_var_types[key] = float
1057 'icool_for009' : [
'eventNumber',
'particleNumber',
'pid',
'status',
'station',
't',
'x',
'y',
'z',
'px',
'py',
'pz',
'bx',
'by',
'bz',
'local_weight',
1058 'ex',
'ey',
'ez',
'',
'sx',
'sy',
'sz'],
1059 'icool_for003' : [
'eventNumber',
'particleNumber',
'pid',
'status',
't',
'local_weight',
'x',
'y',
'z',
'px',
'py',
'pz',
'sx',
'sy',
'sz'],
1060 'g4beamline_bl_track_file' : [
'x',
'y',
'z',
'px',
'py',
'pz',
't',
'pid',
'eventNumber',
'particleNumber',
'',
'local_weight'],
1064 'g4mice_special_hit': [
'',
'station',
'',
'',
'',
'',
'particleNumber',
'',
'pid',
'mass',
'x',
'y',
'z',
't',
'px',
'py',
'pz',
'energy',
'e_dep',
'bx',
'by',
'bz',
'ex',
'ey',
'ez',
1065 '',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'',
'path_length',
'proper_time'],
1066 'g4mice_virtual_hit': [
'',
'station',
'particleNumber',
'pid',
'mass',
'',
'x',
'y',
'z',
't',
'px',
'py',
'pz',
'energy',
'bx',
'by',
'bz',
'ex',
'ey',
'ez',
'',
''],
1067 'mars_1' : [
'eventNumber',
'pid',
'x',
'y',
'z',
'px',
'py',
'pz',
'energy',
'ct',
'local_weight']
1070 'icool_for009' : {
'eventNumber':
'',
'particleNumber':
'',
'pid':
'',
'status':
'',
'station':
'',
't':
's',
'x':
'm',
'y':
'm',
'z':
'm',
'px':
'GeV/c',
'py':
'GeV/c',
1071 'pz':
'GeV/c',
'bx':
'T',
'by':
'T',
'bz':
'T',
'local_weight':
'',
1072 'ex':
'GV/m',
'ey':
'GV/m',
'ez':
'GV/m',
'sx':
'',
'sy':
'',
'sz':
'',
'':
''},
1073 'icool_for003' : {
'eventNumber':
'',
'particleNumber':
'',
'pid':
'',
'status':
'',
't':
's',
'local_weight':
'',
'x':
'm',
'y':
'm',
'z':
'm',
'px':
'GeV/c',
'py':
'GeV/c',
'pz':
'GeV/c',
'sx':
'',
'sy':
'',
'sz':
''},
1074 'g4beamline_bl_track_file' : {
'x':
'mm',
'y':
'mm',
'z':
'mm',
'px':
'MeV/c',
'py':
'MeV/c',
'pz':
'MeV/c',
't':
'ns',
'pid':
'',
'eventNumber':
'',
'station':
'',
'local_weight':
'',
'particleNumber':
''},
1078 'g4mice_special_hit': {
'':
'',
'station':
'',
'':
'',
'':
'',
'':
'',
'':
'',
'particleNumber':
'',
'':
'',
'pid':
'',
'mass':
'MeV/c2',
'x':
'mm',
'y':
'mm',
'z':
'mm',
't':
'ns',
'px':
'MeV/c',
'py':
'MeV/c',
'pz':
'MeV/c',
'energy':
'MeV',
'e_dep':
'MeV',
'bx':
'kT',
'by':
'kT',
'bz':
'kT',
'ex':
'GV',
'ey':
'GV',
'ez':
'GV',
1079 '':
'',
'':
'',
'':
'',
'':
'',
'':
'',
'':
'',
'':
'',
'':
'',
'':
'',
'':
'',
'':
'',
'':
'',
'path_length':
'mm',
'proper_time':
'ns'},
1080 'g4mice_virtual_hit': {
'':
'',
'station':
'',
'particleNumber':
'',
'':
'',
'pid':
'',
'mass':
'MeV/c2',
'x':
'mm',
'y':
'mm',
'z':
'mm',
't':
'ns',
'px':
'MeV/c',
'py':
'MeV/c',
'pz':
'MeV/c',
'energy':
'MeV',
'':
'',
'bx':
'kT',
'by':
'kT',
'bz':
'kT',
'ex':
'GV',
'ey':
'GV',
'ez':
'GV'},
1081 'mars_1' : {
'eventNumber':
'',
'pid':
'',
'x':
'mm',
'y':
'mm',
'z':
'mm',
'px':
'GeV/c',
'py':
'GeV/c',
'pz':
'GeV/c',
'energy':
'GeV',
'ct':
'cm',
'local_weight':
''},
1082 'maus_virtual_hit': {},
1087 'icool_for003':
'<user>\n0. 0. 0. 0. 0. 0. 0. 0.\n',
1088 'icool_for009':
'#<user>\n# units = [s] [m] [GeV/c] [T] [V/m]\nevt par typ flg reg time x y z Px Py Pz Bx By Bz wt Ex Ey Ez arclength polX polY polZ\n',
1089 'g4beamline_bl_track_file':
'#BLTrackFile <user>\n#x y z Px Py Pz t PDGid EvNum TrkId Parent weight\n',
1093 'g4mice_special_hit':
'',
1094 'g4mice_virtual_hit':
'',
1096 'maus_virtual_hit':
'',
1099 __default_user_string =
'File generated by X_BOA'
1102 return cmp(lhs.get(
'eventNumber'), rhs.get(
'eventNumber'))
1103 __event_cmp = staticmethod(__event_cmp)
1106 return cmp(lhs.get(
'station'), rhs.get(
'station'))
1107 __station_cmp = staticmethod(__station_cmp)
1109 __file_mass_shell = {
'icool_for009':
'energy',
'icool_for003':
'energy',
'g4beamline_bl_track_file':
'energy',
'zgoubi':
'',
'Turtle':
'',
'MadX':
'',
1110 'g4mice_special_hit':
'',
'g4mice_virtual_hit':
'',
'mars_1':
'energy',
'maus_virtual_hit':
'energy',
"maus_primary":
"p",
1111 'opal_loss':
'',
'muon1_csv':
''}
1113 __hit_sort_comparator = {
1114 'icool_for009': __event_cmp,
1115 'icool_for003': __event_cmp,
1116 'g4beamline_bl_track_file': __event_cmp,
1117 'ZGoubi': __event_cmp,
1118 'Turtle': __event_cmp,
1119 'MadX': __event_cmp,
1120 'g4mice_special_hit': __station_cmp,
1121 'g4mice_virtual_hit': __station_cmp,
1122 'maus_virtual_hit': __event_cmp,
1123 'maus_primary': __event_cmp,
1124 'mars_1': __event_cmp,
1125 'muon1_csv': __event_cmp,
1128 __force_unique_particle_number = [
'maus_primary']
1130 __angular_momentum_centroid = (0.,0.,0.)
1131 __angular_momentum_vector = (0.,0.,1.)
1134 __maus_three_vec_conversions = {
1135 "maus_virtual_hit":{
"position":
"",
"momentum":
"p",
"b_field":
"b",
"e_field":
"e"},
1136 "maus_primary":{
"position":
"",
"momentum":
"p"}
1139 __maus_variable_conversions = {
1140 "maus_virtual_hit":{
"station_id":
"station",
"particle_id":
"pid",
"track_id":
"particle_number",
"time":
"t",
"mass":
"mass",
"charge":
"charge",
"proper_time":
"proper_time",
"path_length":
"path_length"},
1141 "maus_primary":{
"particle_id":
"pid",
"time":
"t",
"energy":
"energy"}
1145 "maus_virtual_hit":[
"mc_events", [
"particle_number"],
"virtual_hits", [
"station"]],
1146 "maus_primary":[
"mc_events", [
"particle_number"],
"primary"],
1149 __maus_root_mc_types = {
1150 "maus_root_virtual_hit":
lambda x, y: Hit.__get_maus_root_virtual_hits(x, y),
1151 "maus_root_primary":
lambda x, y: Hit.__get_maus_root_primary_hits(x, y)
1153 __maus_root_recon_types = {}
1155 __opal_ignore_probes = [
"RING"]
1160 """Creates some summary documentation for the Hit class. If verbose is True then will also print any functions or data not included in summary"""
1161 name_list = [
'initialise',
'get',
'set',
'transform',
'io',
'ancillary']
1163 'initialise' : [
'new_from_dict',
'new_from_read_builtin',
'new_from_read_user',
'new_from_maus_object',
'copy',
'deepcopy'],
1164 'get' : [
'get',
'get_ct',
'get_ek',
'get_global_weight',
'get_l_kin',
'get_local_weight',
'get_p',
'get_phi',
'get_pphi',
'get_pt',
'get_r',
'get_rP',
'get_r_squared',
'get_spin',
'get_tP',
'get_vector',
'get_weight',
'get_xP',
'get_yP'],
1165 'set' : [
'set',
'set_ct',
'set_ek',
'set_local_weight',
'set_p',
'set_tP',
'set_variables',
'set_xP',
'set_yP',
'set_global_weight'],
1166 'transform' : [
'abelian_transformation',
'translate',
'mass_shell_condition'],
1167 'io' : [
'file_header',
'file_types',
'set_g4bl_unit',
'write_builtin_formatted',
'write_list_builtin_formatted',
'write_user_formatted',
'open_filehandle_for_writing',
'read_builtin_formatted',
'read_user_formatted',
'get_maus_dict',
'get_maus_paths',
'get_maus_tree'],
1168 'ancillary' : [
'check',
'clear_global_weights',
'delete_global_weights',
'get_bad_pids',
'set_bad_pids',
'dict_from_hit',
'mass_shell_variables',
'get_variables']
1171 'initialise':
'Functions that can be used to initialise a Hit in various different ways:',
1172 'get' :
'Functions to get Hit data',
1173 'set' :
'Functions to set Hit data',
1174 'transform' :
'Functions that transform a Hit in some way:',
1175 'io' :
'Output and some input helper functions:',
1176 'ellipse':
'Functions to calculate beam ellipses based on Twiss parameters etc:',
1177 'ancillary':
'Some other useful functions'
1179 hit_doc =
'\nHit class stores all data for a Hit on e.g. a detector - so for example, (x,y,z) and (px,py,pz) data. Mimics a string-indexed dict; full documentation for internal variables is given below under Hitcore. In brief, gettable variables are\n'+str(Hit.get_variables())+
'\n and settable variables are\n'+str(Hit.set_variables())+
'.\n Call using e.g. my_hit[\'x\'] = 3. Also has IO functions and a few other useful functions.\n'
1182 print 'The following functions and data are in Bunch but not in overview_doc:'
1183 for func
in dir_hit:
1185 for func_sublist
in function_list.values():
1186 if func
in func_sublist: found =
True
1187 if not found:
print func,
1190 print 'The following functions and data are in bunch_overview_doc but not in Bunch:'
1191 for func_sublist
in function_list.values():
1192 for func
in func_sublist:
1193 if func
not in dir_hit:
1198 for key
in name_list:
1199 doc = doc + function_doc[key]+
'\n'
1200 for item
in function_list[key]:
1201 doc = doc+
' '+item+
'\n'
1203 hit_overview_doc = staticmethod(hit_overview_doc)
1205 __doc__ = Hit.hit_overview_doc()