schema.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 '''
3 This module defines an schema of object which describe the
4 wire-related geometry for the Wire Cell Toolkit.
5 
6 It covers a hierarchy of object types:
7 
8  - Detector
9 
10  - Anode (eg, one APA)
11 
12  - Face
13 
14  - Plane
15 
16  - Wire
17 
18  - Point
19 
20 Except for Point, all objects are organizational and describe
21 parentage. Points are used to describe the end points of wire
22 segments (wires) and are MUST be expressed in GLOBAL coordinates.
23 
24 In the following cases the children are referenced by their parent in
25 a strictly ordered list:
26 
27  - A face orders its child planes following how charge drifts.
28  That is, in U/V/W order.
29 
30  - A plane orders its child wires in increasing pitch direction
31  such that the cross product of the wire direction (see next) and
32  this pitch direction gives a direction which is normal to the
33  wire plane and anti-parallel to the nominal drift direction for
34  that plane.
35 
36  - Wire places its head end-point closer to the input of the
37  detector electronics. The wire direction is taken from tail to
38  head end-points. For top DUNE APAs and all other current
39  detectors this direction points generally upward and for bottom
40  DUNE APAs it points generally downward.
41 
42 Ordering of all other lists is not defined.
43 
44 Most objects in the schema have an "ident" attribute. This number is
45 made available to user C++ code but is treated as OPAQUE but UNIQUE by
46 the toolkit. If an object is referenced in multiple contexts their
47 ident MUST match. For example, a wire plane and a field reponse plane
48 must be correlated by their ident.
49 
50 Schema objects are held in flat, per type store and a reference to an
51 object of a particular type is made via its index into the store for
52 that type.
53 
54 All values MUST be in the WCT system of units. Code that generates
55 these objects may use the Python module wirecell.units to assure this.
56 
57 Although not specified here, there are places where "front" and "back"
58 qualifiers are applied to "face". A "front" face is one for which the
59 cross product of wire and pitch directions (as described above) is
60 parallel to the global X-axis.
61 '''
62 
63 
64 from collections import namedtuple
65 
66 
67 class Point(namedtuple("Point", "x y z")):
68  '''
69  A position.
70 
71  :param float x:
72  :param float y:
73  :param float z:
74  '''
75  __slots__ = ()
76 
77 
78 class Wire(namedtuple("Wire","ident channel segment tail head")):
79  '''
80  A Wire object holds information about one physical wire segment.
81 
82  A wire is a ray which points in the direction that signals flow
83  toward the electronics.
84 
85  :param int ident:
86  :param int channel: numerical identifier unique to this conductor.
87  It is made available via IWire::channel(). This number is
88  treated as opaque by the toolkit except that it is expected to
89  uniquely identify an electronics input channel.
90  :param int segment: count the number of other wires between this
91  wire and the channel input.
92  :param int tail: index referencing the tail end point of the wire
93  in the coordinate system for the anode face.
94  :param int head: index referencing the head end point of the wire
95  in the coordinate system for the anode face.
96  '''
97  __slots__ = ()
98 
99 
100 class Plane(namedtuple("Plane", "ident wires")):
101  '''
102  A Plane object collects the coplanar wires.
103 
104  :param int ident:
105  :param list wires: list of indices referencing the wires that make
106  up this plane. This list MUST be sorted in "increasing wire
107  pitch location" as described above.
108  '''
109  __slots__ = ()
110 
111 
112 class Face(namedtuple("Face", "ident planes")):
113  '''
114  A Face collects the wire and conductor planes making up one face
115  of an anode plane.
116 
117  :param int ident:
118  :param list planes: list of indices referencing planes. This list
119  MUST be in sorted in U/V/W order as described above.
120  '''
121  __slots__ = ()
122 
123 
124 
125 class Anode(namedtuple("Anode","ident faces")):
126  '''
127  An Anode object collects together Faces.
128 
129  A detector like MicroBooNE has just one face per its single anode.
130  protoDUNE/SP has two faces per each of its six anodes (aka APAs).
131 
132  :param int ident:
133  :param list faces: list indices referencing faces.
134  '''
135  __slots__ = ()
136 
137 
138 class Detector(namedtuple("Detector", "ident anodes")):
139  '''
140  A detector of anodes.
141 
142  This allows one or more sets ot anodes to be defined.
143  :param int ident:
144  :param list anodes: list of anodes
145  '''
146  __slots__ = ()
147 
148 
149 class Store(namedtuple("Store","anodes faces planes wires points")):
150  '''
151  A store of collections of the objects of this schema.
152 
153  This somewhat awkward indirection of a reference into a store is
154  so that multiple things may refer to something without having to
155  invent some kind of "pointer" which must be carried by each
156  object.
157 
158  :param list detectors: list of the Detector objects.
159  :param list anodes: list of the Anode objects.
160  :param list faces: list of the Face objects
161  :param list planes: list of the Plane objects.
162  :param list wires: list of the Wire objects.
163  :param list points: list of the Point objects.
164  '''
165  __slots__ = ()
166 
167  def __repr__(self):
168  return "<Store: %d detectors, %d anodes, %d faces, %d planes, %d wires, %d points>" % \
169  (len(self.detectors), len(self.anodes), len(self.faces), len(self.planes), len(self.wires), len(self.points))
170 
171 def classes():
172  import sys, inspect
173  ret = list()
174  for name, obj in inspect.getmembers(sys.modules[__name__]):
175  if inspect.isclass(obj):
176  ret.append(obj)
177  return ret
178 
179 def maker():
180  '''
181  Return a schema instance maker.
182 
183  >>> m = maker()
184  >>> hid = m.make('Point', x=..., y=..., z=...)
185  >>> tid = m.make('Point', x=..., y=..., z=...)
186  >>> wid = m.make('Wire', ident=0, channel=0, segment=0 tail=tid, head=hid)
187 
188  >>> wire = m.get('Wire', wid)
189 
190  >>> store = m.schema()
191  '''
192  import sys, inspect
193  class SchemaMaker(object):
194 
195  def __init__(self):
196  self._makers = dict()
197  for klass in classes():
198  lname = klass.__name__.lower()
199  self.__dict__[lname+'s'] = list()
200  self._makers[lname] = klass
201 
202  def make(self, what, *args):
203  klass = self._makers[what]
204  collection = self.__dict__[what+'s']
205  nthings = len(collection)
206  thing = klass(*args)
207  collection.append(thing)
208  return nthings
209 
210  def get(self, what, ind):
211  collection = self.__dict__[what+'s']
212  return collection[ind]
213 
214  def wire_ypos(self, ind):
215  wire = self.get("wire", ind)
216  p1 = self.get("point", wire.tail)
217  p2 = self.get("point", wire.head)
218  return 0.5*(p1.y + p2.y)
219  def wire_zpos(self, ind):
220  wire = self.get("wire", ind)
221  p1 = self.get("point", wire.tail)
222  p2 = self.get("point", wire.head)
223  return 0.5*(p1.z + p2.z)
224 
225  def schema(self):
226  'Return self as a schema.Store'
227  return Store(self.anodes, self.faces, self.planes, self.wires, self.points)
228  return SchemaMaker()
229 
230 
231 layer_mask = 0x7
232 face_shift = 3
233 face_mask = 0x1
234 apa_shift = 4
235 
236 def wire_plane_id(plane, face, apa):
237  'See WireCellIface/WirePlaneId.h'
238  return (plane&layer_mask) | (face << face_shift) | (apa << apa_shift)
239 
240 def plane_face_apa(wpid):
241  #return (wpid&layer_mask, (wpid>>face_shift)&face_mask, wpid>>apa_shift)
242  return (wpid&layer_mask, (wpid&(1<<face_shift))>>3, wpid>>apa_shift)
243 
def wire_plane_id(plane, face, apa)
Definition: schema.py:236
unique_ptr< InputSource > make(ParameterSet const &conf, InputSourceDescription &desc)
def plane_face_apa(wpid)
Definition: schema.py:240
auto const & get(AssnsNode< L, R, D > const &r)
Definition: AssnsNode.h:115