info.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 '''
3 Functions to provide information about wires
4 '''
5 from wirecell import units
6 import numpy
7 import math
8 
9 def p2p(p):
10  return dict(x=p.x, y=p.y, z=p.z)
11 
12 def todict(store):
13  '''
14  Convert the store to a dict-based representation. This will
15  inflate any duplicate references
16  '''
17  d_anodes = list()
18  for anode in store.anodes:
19  d_anode = dict(ident = anode.ident, faces = list())
20  for iface in anode.faces:
21  face = store.faces[iface]
22  d_face = dict(ident = face.ident, planes = list())
23  for iplane in face.planes:
24  plane = store.planes[iplane]
25  d_plane = dict(ident = plane.ident, wires = list())
26 
27  #print ("anode:%d face:%d plane:%d" % (anode.ident, face.ident, plane.ident))
28 
29  for wind in plane.wires:
30  wire = store.wires[wind]
31  d_wire = dict(ident = wire.ident,
32  channel = wire.channel,
33  segment = wire.segment,
34  head = p2p(store.points[wire.head]),
35  tail = p2p(store.points[wire.tail]))
36  d_plane["wires"].append(d_wire)
37  d_face["planes"].append(d_plane)
38  d_anode["faces"].append(d_face)
39  d_anodes.append(d_anode)
40 
41  # fixme: this should support detectors, for now, just assume one
42  return [dict(ident=0, anodes=d_anodes)]
43 
44 
45 class BoundingBox(object):
46  def __init__(self):
47  self.minp = None
48  self.maxp = None
49 
50  def __call__(self, p):
51  if self.minp is None:
52  self.minp = dict(p)
53  self.maxp = dict(p)
54  return
55 
56  for c,v in self.minp.items():
57  if p[c] < v:
58  self.minp[c] = p[c]
59 
60  for c,v in self.maxp.items():
61  if p[c] > v:
62  self.maxp[c] = p[c]
63 
64  def center(self):
65  return {c:0.5*(self.minp[c]+self.maxp[c]) for c in "xyz"}
66 
67 class Ray(object):
68  def __init__(self, wire):
69  self.head = numpy.asarray([wire['head'][c] for c in "xyz"])
70  self.tail = numpy.asarray([wire['tail'][c] for c in "xyz"])
71  @property
72  def ray(self):
73  return self.head - self.tail
74  @property
75  def center(self):
76  return 0.5* (self.head + self.tail)
77 
78 
79 def pitch_mean_rms(wires):
80  '''
81  Return [mean,rms] of pitch
82  '''
83  eks = numpy.asarray([1.0,0.0,0.0])
84  zero = numpy.asarray([0.0, 0.0, 0.0])
85 
86  r0 = Ray(wires[0])
87  pdir = numpy.cross(eks, r0.ray)
88  pdir = pdir/numpy.linalg.norm(pdir)
89 
90  pmax=pmin=None
91 
92  prays = list()
93  for w in wires:
94  r = Ray(w)
95  p = numpy.dot(pdir, r.center)
96  prays.append((p,r))
97  prays.sort()
98  rays = [pr[1] for pr in prays]
99  psum = psum2 = 0.0
100  for r1,r2 in zip(rays[:-1], rays[1:]):
101  p = numpy.dot(pdir,r2.center - r1.center)
102  if pmax is None:
103  pmax = pmin = p
104  else:
105  pmax = max(pmax, p)
106  pmin = min(pmin, p)
107  psum += abs(p)
108  psum2 += p*p
109  n = len(wires)-1
110  pmean = psum/n
111  assert(pmean>0)
112  pvar = (pmean*pmean - psum2/n)/(n-1)
113  return pmean, math.sqrt(abs(pvar)), pmin, pmax
114 
116  pmm = tuple([pp/units.mm for pp in p])
117  return "(%.3f +/- %.3f [%.3f<%.3f]) " % pmm
118 
119 def summary(store):
120  '''
121  Return a summary data structure about the wire store.
122  '''
123  lines = list()
124  for det in todict(store):
125  for anode in det['anodes']:
126  for face in anode['faces']:
127  bb = BoundingBox()
128  pitches = list()
129  for plane in face['planes']:
130  pitches.append(format_pitch(pitch_mean_rms(plane['wires'])))
131  for wire in plane['wires']:
132  bb(wire['head']);
133  bb(wire['tail']);
134 
135  # if anode['ident']==1 and face['ident']==1:
136  # if wire['ident'] == 220:
137  # print("--------special")
138  # print(wire)
139  # print("--------special")
140 
141 
142  lines.append("anode:%d face:%d X=[%.2f,%.2f]mm Y=[%.2f,%.2f]mm Z=[%.2f,%.2f]mm" % \
143  (anode['ident'], face['ident'],
144  bb.minp['x']/units.mm, bb.maxp['x']/units.mm,
145  bb.minp['y']/units.mm, bb.maxp['y']/units.mm,
146  bb.minp['z']/units.mm, bb.maxp['z']/units.mm))
147  for pind, plane in enumerate(face['planes']):
148  lines.append('\t%d: x=%.2fmm dx=%.4fmm n=%d pitch=%s' % \
149  (plane['ident'],
150  plane['wires'][0]['head']['x']/units.mm,
151  (plane['wires'][0]['head']['x']-face['planes'][2]['wires'][0]['head']['x'])/units.mm,
152  len(plane['wires']), pitches[pind]))
153  return lines
154 
155 def jsonnet_volumes(store,
156  danode=0.0, dresponse=10*units.cm, dcathode=1*units.m, volpat=None):
157  '''
158  Return a Jsonnet string suitable for copying to set
159  params.det.volumes found in the pgrapher configuration.
160 
161  The "namepat" should be a string with format markers "{variable}"
162  and will be formated with "detector", "anode" set to their
163  numbers.
164 
165  The "d" arguments give the distance measured from the collection
166  plane to each of these logical planes.
167  '''
168 
169  voltexts = list()
170  volpat = volpat or '''
171  wires: {anode},
172  name: anode{anode},
173  faces: [ {faces} ],
174 '''
175  facepat = "anode:{anodex}*wc.cm, cathode:{cathodex}*wc.cm, response:{responsex}*wc.cm"
176 
177  for idet, det in enumerate(todict(store)):
178  for anode in det['anodes']:
179  faces = anode['faces']
180  assert (len(faces) <= 2)
181  facetexts = ["null","null"]
182  for face in faces:
183  face_bbs = list()
184  for plane in face['planes']:
185  bb = BoundingBox()
186  for wire in plane['wires']:
187  bb(wire['head']);
188  bb(wire['tail']);
189  face_bbs.append(bb)
190  continue # plane
191  uvw_x = [bb.minp['x'] for bb in face_bbs]
192  sign = +1.0
193  find = 0
194  if uvw_x[0] < uvw_x[2]: # U below W means "back" face
195  sign = -1.0
196  find = 1
197 
198  xorigin = uvw_x[2]
199  facetexts[find] = "\n {" + facepat.format(
200  anodex = (xorigin + sign*danode)/units.cm,
201  responsex = (xorigin + sign*dresponse)/units.cm,
202  cathodex = (xorigin + sign*dcathode)/units.cm) + "}"
203  continue # face
204 
205  facetext = ','.join(facetexts)
206  voltext = " {" + volpat.format(anode=anode["ident"], faces=facetext) + " }"
207  voltexts.append(voltext)
208  continue # anode
209  continue # detector
210 
211  argstext = " local volumeargs = { danode:%f*wc.cm, dresponse:%f*wc.cm, dcathode:%f*wc.cm },\n" \
212  % (danode/units.cm, dresponse/units.cm, dcathode/units.cm)
213  volumetext = " volumes: [\n" + ',\n'.join(voltexts) + "\n ],\n";
214  return argstext+volumetext;
215 
def __init__(self, wire)
Definition: info.py:68
def todict(store)
Definition: info.py:12
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
def pitch_mean_rms(wires)
Definition: info.py:79
T abs(T value)
def jsonnet_volumes(store, danode=0.0, dresponse=10 *units.cm, dcathode=1 *units.m, volpat=None)
Definition: info.py:156
static int max(int a, int b)
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
auto zip(Iterables &&...iterables)
Range-for loop helper iterating across many collections at the same time.
Definition: zip.h:295
def summary(store)
Definition: info.py:119