GeneralBrokenLines V03-00-00
gblpy
gblsit.py
Go to the documentation of this file.
1'''
2Created on 28 Sep 2018
3
4@author: kleinwrt
5'''
6
7
27
28import numpy as np
29import math
30import random
31import time
32from gblfit import GblPoint, GblTrajectory
33
34
35
82
83 #
84 random.seed(47117)
85
86 # magnetic field
87 bfac = 0.003 # B*c for 1 T
88 #bfac = 0. # B*c for 0 T
89 # detector layers: name, position (x,y,z), thickness (X/X_0), (1 or 2) measurements (direction in YZ, resolution), spacing for composite layers
90 det = gblSiliconDet([ ['PIX1', (2.0, 0., 0.), 0.0033, [(0., 0.0010), (90., 0.0020)] ], # pixel
91 ['PIX2', (3.0, 0., 0.), 0.0033, [(0., 0.0010), (90., 0.0020)] ], # pixel
92 ['PIX3', (4.0, 0., 0.), 0.0033, [(0., 0.0010), (90., 0.0020)] ], # pixel
93 ['S2D4', (6.0, 0., 0.), 0.0033, [(0., 0.0025), (5., 0.0025)], 0.1 ], # strip 2D (composite), 5 deg stereo angle
94 ['S2D5', (8.0, 0., 0.), 0.0033, [(0., 0.0025), (-5., 0.0025)], 0.1 ], # strip 2D (composite), -5 deg stereo angle
95 ['S2D6', (10.0, 0., 0.), 0.0033, [(0., 0.0025), (5., 0.0025)] ], # strip 2D (double sided), 5 deg stereo angle
96 ['S2D7', (12.0, 0., 0.), 0.0033, [(0., 0.0025), (-5., 0.0025)] ], # strip 2D (double sided), -5 deg stereo angle
97 ['S1D8', (15.0, 0., 0.), 0.0033, [(0., 0.0040)] ] # strip 1D
98 ], bfac)
99
100 nTry = 1000 #: number of tries
101 qbyp = 0.2 # 5 GeV
102 binaryFile = open("milleBinary.dat", "wb")
103 #binaryFile = None
104 #
105 print " Gblsit $Id$ ", nTry
106 #
107 start = time.clock()
108 Chi2Sum = 0.
109 NdfSum = 0
110 LostSum = 0.
111 #
112 for iTry in range(nTry):
113 # helix parameter for track generation
114 dca = random.gauss(0., 0.1)
115 z0 = random.gauss(0., 0.1)
116 phi0 = 0.5 * random.uniform(-1., 1.)
117 dzds = 0.3 * random.uniform(-1., 1.)
118 curv = bfac * qbyp * math.sqrt(1. + dzds * dzds)
119 genPar = [curv, phi0, dca, dzds, z0]
120 #print " genPar ", iTry, genPar
121 # generate hits
122 genHits = det.generateHits(qbyp, genPar)
123 # seed (with true parameters)
124 seedPar = genPar
125 seed = gblSimpleHelix(seedPar)
126 sOld = 0.
127 cosLambda = 1. / math.sqrt(1. + seedPar[3] ** 2)
128 # construct GBL trajectory
129 traj = GblTrajectory(bfac != 0.)
130 # add GBL points
131 for l, layer in enumerate(det.getLayers()):
132 # prediction from seeding helix
133 pred = layer.intersectWithHelix(seed)
134 measPred = pred.getMeasPred()
135 sArc = pred.getArcLength()
136 # residuals
137 res = np.array([genHits[l][0] - measPred[0], genHits[l][1] - measPred[1]])
138 # measurement precision
139 measPrec = np.array(layer.getPrecision())
140 # Curvilinear system: track direction T, U = Z x T / |Z x T|, V = T x U
141 # as local system
142 curviDirs = pred.getCurvilinearDirs()
143 # projection matrix (local to measurement)
144 proL2m = np.linalg.inv(np.dot(curviDirs, np.linalg.inv(layer.getMeasSystemDirs())[:, :2]))
145 # propagation
146 jacPointToPoint = gblSimpleJacobian((sArc - sOld) / cosLambda, cosLambda, bfac)
147 sOld = sArc
148 # point with (independent) measurements (in measurement system)
149 point = GblPoint(jacPointToPoint)
150 # composite?
151 if layer.isComposite():
152 # 2nd prediction
153 pred = layer.intersectWithHelix2(seed)
154 measPred = pred.getMeasPred()
155 # 4D measurement
156 pro4D = np.zeros((4, 4)); pro4D[2:, 2:] = proL2m; pro4D[3, :2] = proL2m[1, :] * layer.getSpacing();
157 res4D = np.array([0., 0., res[0], genHits[l][1] - measPred[1]])
158 prec4D = np.array([0., 0., measPrec[0], measPrec[1]])
159 point.addMeasurement([pro4D, res4D, prec4D])
160 else:
161 # 2D measurement
162 point.addMeasurement([proL2m, res, measPrec])
163 # global parameters for rigid body alignment?
164 if binaryFile is not None:
165 # local (alignment) system, per layer
166 labels = [l * 10 + 1, l * 10 + 2, l * 10 + 3, l * 10 + 4, l * 10 + 5, l * 10 + 6]
167 labGlobal = np.array([labels, labels])
168 derGlobal = layer.getRigidBodyDerLocal(pred.getPos(), pred.getDirection())
169 # composite?
170 if layer.isComposite():
171 # 4D
172 labG4D = np.array([labels, labels, labels, labels])
173 derG4D = np.zeros((4, 6)); derG4D[2:] = derGlobal
174 point.addGlobals(labG4D, derG4D)
175 else:
176 # 2D
177 point.addGlobals(labGlobal, derGlobal)
178 # add scatterer to point
179 radlen = layer.getRadiationLength() / abs(pred.getCosIncidence())
180 scatErr = gblMultipleScatteringError(qbyp, radlen) # simple model
181 if scatErr > 0.:
182 scat = np.array([0., 0.])
183 scatP = np.array([1. / scatErr ** 2, 1. / scatErr ** 2])
184 # composite?
185 if layer.isComposite():
186 # two similar sub layers
187 scatP *= 0.5
188 point.addScatterer([scat, scatP])
189 # add point to trajectory
190 traj.addPoint(point)
191
192 # fit trajectory
193 Chi2, Ndf, Lost = traj.fit()
194 print " Record, Chi2, Ndf, Lost", iTry, Chi2, Ndf, Lost
195 # sum up
196 Chi2Sum += Chi2
197 NdfSum += Ndf
198 LostSum += Lost
199 # write to binary file
200 if binaryFile is not None:
201 traj.milleOut(binaryFile)
202
203 end = time.clock()
204 print " Time [s] ", end - start
205 print " Chi2Sum/NdfSum ", Chi2Sum / NdfSum
206 print " LostSum/nTry ", LostSum / nTry
207
208
209
219def gblSimpleJacobian(ds, cosl, bfac):
220 jac = np.eye(5)
221 jac[1, 0] = -bfac * ds * cosl
222 jac[3, 0] = -0.5 * bfac * ds * ds * cosl
223 jac[3, 1] = ds
224 jac[4, 2] = ds
225 return jac
226
227
228
236 return 0.015 * abs(qbyp) * math.sqrt(xbyx0)
237
238
239
240class gblSiliconLayer(object):
241
242
246 def __init__(self, layer):
247
248 self.__center = np.array(layer[1])
249
250 self.__xbyx0 = layer[2]
251 # measurements (1D or 2D)
252 meas = layer[3]
253
254 self.__resolution = (meas[0][1], meas[1][1] if len(meas) > 1 else 0.)
255
256 self.__precision = (1. / meas[0][1] ** 2, 1. / meas[1][1] ** 2 if len(meas) > 1 else 0.)
257 # measurement angles
258 uPhi = meas[0][0] / 180. * math.pi
259 vPhi = meas[1][0] / 180. * math.pi if len(meas) > 1 else uPhi + 0.5 * math.pi
260
261 self.__uDir = np.array([0., math.cos(uPhi), math.sin(uPhi)])
262
263 self.__vDir = np.array([0., math.cos(vPhi), math.sin(vPhi)])
264
265 self.__nDir = np.array([1., 0., 0.])
266
267 self.__measDirs = np.array([self.__uDir, self.__vDir, self.__nDir])
268
269 self.__ijkDirs = np.array([[0., 1., 0.], [0., 0., 1.], [1., 0., 0.]])
270
271 self.__spacing = layer[4] if len(layer) > 4 else None
272
273
275 return self.__xbyx0
276
277
278 def getResolution(self):
279 return self.__resolution
280
281
282 def getPrecision(self):
283 return self.__precision
284
285
287 return self.__measDirs
288
289
294 def intersectWithHelix(self, helix):
295 return helix.getPrediction(self.__center, self.__uDir, self.__vDir)
296
297
302 def intersectWithHelix2(self, helix):
303 return helix.getPrediction(self.__center + self.__spacing * self.__nDir, self.__uDir, self.__vDir)
304
305
306 def isComposite(self):
307 return self.__spacing is not None
308
309
310 def getSpacing(self):
311 return self.__spacing
312
313
319 def getRigidBodyDerGlobal(self, position, trackDir):
320 # lever arms (for rotations)
321 dist = position
322 # dr/dm (residual vs measurement, 1-tdir*ndir^t/tdir*ndir)
323 drdm = np.eye(3) - np.outer(trackDir, self.__nDir) / np.dot(trackDir, self.__nDir)
324 # dm/dg (measurement vs 6 rigid body parameters)
325 dmdg = np.zeros((3, 6))
326 dmdg[0][0] = 1.; dmdg[0][4] = -dist[2]; dmdg[0][5] = dist[1]
327 dmdg[1][1] = 1.; dmdg[1][3] = dist[2]; dmdg[1][5] = -dist[0]
328 dmdg[2][2] = 1.; dmdg[2][3] = -dist[1]; dmdg[2][4] = dist[0]
329 # drl/drg (local vs global residuals)
330 drldrg = self.__measDirs
331 # drl/dg (local residuals vs rigid body parameters)
332 drldg = np.dot(drldrg, np.dot(drdm, dmdg))
333 return drldg
334
335
341 def getRigidBodyDerLocal(self, position, trackDir):
342 # track direction in local system
343 tLoc = np.dot(self.__ijkDirs, trackDir)
344 # local slopes
345 uSlope = tLoc[0] / tLoc[2]
346 vSlope = tLoc[1] / tLoc[2]
347 # (u,v) lever arms
348 uPos, vPos = np.dot(self.__ijkDirs, position - self.__center)[:2]
349 # wPos = 0 (in detector plane)
350 # drl/dg (local residuals vs rigid body parameters)
351 drldg = np.array([[1.0, 0.0, -uSlope, vPos * uSlope, -uPos * uSlope, vPos], \
352 [0.0, 1.0, -vSlope, vPos * vSlope, -uPos * vSlope, -uPos]])
353 return drldg
354
355
356
357class gblSiliconDet(object):
358
359
364 def __init__(self, layers, bfac):
365
366 self.__layers = []
367
368 self.__bfac = bfac
369
370 for layer in layers:
371 self.__layers.append(gblSiliconLayer(layer))
372
373
374 def getLayers(self):
375 return self.__layers
376
377
383 def generateHits(self, qbyp, genPar):
384
385# list of hits
386 hits = []
387 localPar = genPar
388 # print " track ", helix
389 for layer in self.__layers:
390 # local constant (Bfield) helix
391 hlx = gblSimpleHelix(localPar)
392 # prediction from local helix
393 pred = layer.intersectWithHelix(hlx)
394 meas = [pred.getMeasPred()]
395 # scatter at intersection point
396 xPos, yPos = pred.getPos()[:2]
397 radlen = layer.getRadiationLength() / abs(pred.getCosIncidence())
398 errMs = gblMultipleScatteringError(qbyp, radlen) # simple model
399 cosLambda = 1. / math.sqrt(1. + localPar[3] ** 2)
400 # move to intersection point
401 newpar = hlx.moveTo((xPos, yPos))
402 newpar[1] += random.gauss(0., errMs / cosLambda) # phi
403 newpar[3] += random.gauss(0., errMs / cosLambda ** 2) # dzds
404 newhlx = gblSimpleHelix(newpar)
405 # move back
406 localPar = newhlx.moveTo((-xPos, -yPos))
407 # composite layer
408 if layer.isComposite():
409 # 2nd prediction from local helix
410 pred = layer.intersectWithHelix2(hlx)
411 meas.append(pred.getMeasPred())
412 # scatter at intersection point
413 xPos, yPos = pred.getPos()[:2]
414 cosLambda = 1. / math.sqrt(1. + localPar[3] ** 2)
415 # move to intersection point
416 newpar = hlx.moveTo((xPos, yPos))
417 newpar[1] += random.gauss(0., errMs / cosLambda) # phi
418 newpar[3] += random.gauss(0., errMs / cosLambda ** 2) # dzds
419 newhlx = gblSimpleHelix(newpar)
420 # move back
421 localPar = newhlx.moveTo((-xPos, -yPos))
422
423 # add (smeared) hit
424 sigma = layer.getResolution()
425 hits.append((random.gauss(meas[0][0], sigma[0]), random.gauss(meas[-1][1], sigma[1])))
426
427 return hits
428
429
430
434class gblSimpleHelix(object):
435
436
444 def __init__(self, parameter):
445
446 self.__rinv = parameter[0]
447
448 self.__phi0 = parameter[1]
449
450 self.__dir0 = (math.cos(self.__phi0), math.sin(self.__phi0))
451
452 self.__dca = parameter[2]
453
454 self.__dzds = parameter[3]
455
456 self.__z0 = parameter[4]
457
458 self.__xRelCenter = -(1. - self.__dca * self.__rinv) * self.__dir0[1]
459
460 self.__yRelCenter = (1. - self.__dca * self.__rinv) * self.__dir0[0]
461
462
471 def getPrediction(self, refPos, uDir, vDir):
472 # normal to (u,v) measurement plane
473 nDir = np.cross(uDir, vDir); nDir /= np.linalg.norm(nDir)
474 # ZS direction
475 cosLambda = 1. / math.sqrt(1. + self.__dzds * self.__dzds)
476 sinLambda = self.__dzds * cosLambda
477 # line (or helix)
478 if self.__rinv == 0.:
479 # track direction
480 tDir = np.array([cosLambda * self.__dir0[0], cosLambda * self.__dir0[1], sinLambda])
481 # distance (of point at dca to reference)
482 pca = np.array([ self.__dca * self.__dir0[1] , -self.__dca * self.__dir0[0], self.__z0])
483 dist = pca - refPos
484 # arc-length
485 sArc3D = -np.dot(dist, nDir) / np.dot(tDir, nDir); sArc2D = sArc3D * cosLambda
486 # distance (of point at sArc to reference)
487 pos = pca + sArc3D * tDir
488 dist = pos - refPos
489 else:
490 # initial guess of 2D arc-length
491 sArc2D = self.getArcLengthXY(refPos[0], refPos[1])
492 nIter = 0
493 while nIter < 10:
494 nIter += 1
495 # track direction
496 dPhi = sArc2D * self.__rinv
497 cosPhi = math.cos(self.__phi0 + dPhi); sinPhi = math.sin(self.__phi0 + dPhi)
498 tDir = np.array([cosLambda * cosPhi, cosLambda * sinPhi, sinLambda])
499 # distance (of point at sArc to reference)
500 pos = np.array([(self.__xRelCenter + sinPhi) / self.__rinv, (self.__yRelCenter - cosPhi) / self.__rinv, self.__z0 + self.__dzds * sArc2D])
501 dist = pos - refPos
502 # arc-length correction (linearizing helix at sNew)
503 sCorr3D = -np.dot(dist, nDir) / np.dot(tDir, nDir)
504 if abs(sCorr3D) > 0.0001:
505 sArc2D += sCorr3D * cosLambda
506 else:
507 break
508
509 # prediction in measurement directions
510 pred = [np.dot(dist, uDir), np.dot(dist, vDir)]
511 return gblHelixPrediction(sArc2D, pred, tDir, uDir, vDir, nDir, pos)
512
513
522 def getArcLengthXY(self, xPos, yPos):
523 # line
524 if self.__rinv == 0:
525 return self.__dir0[0] * xPos + self.__dir0[1] * yPos
526 # helix
527 dx = (xPos * self.__rinv - self.__xRelCenter)
528 dy = (yPos * self.__rinv - self.__yRelCenter)
529 dphi = math.atan2(dx, -dy) - self.__phi0
530 if (abs(dphi) > math.pi):
531 dphi -= cmp(dphi, 0.) * 2.0 * math.pi
532 return dphi / self.__rinv
533
534
540 def moveTo(self, newRefPoint):
541
542 rho = self.__rinv
543 phi = self.__phi0
544 dca = self.__dca
545 dzds = self.__dzds
546 z0 = self.__z0
547
548 u = 1. - rho * dca
549 dp = -newRefPoint[0] * self.__dir0[1] + newRefPoint[1] * self.__dir0[0] + dca
550 dl = newRefPoint[0] * self.__dir0[0] + newRefPoint[1] * self.__dir0[1]
551 sa = 2. * dp - rho * (dp * dp + dl * dl)
552 sb = rho * newRefPoint[0] + u * self.__dir0[1]
553 sc = -rho * newRefPoint[1] + u * self.__dir0[0]
554 sd = math.sqrt(1. - rho * sa)
555 # transformed parameters
556 if rho == 0.:
557 dca = dp
558 sArc = dl
559 newPar = [rho, phi, dca]
560 else:
561 phi = math.atan2(sb, sc)
562 dca = sa / (1. + sd)
563 dphi = phi - self.__phi0
564 if abs(dphi) > math.pi: dphi -= cmp(dphi, 0.) * 2.0 * math.pi
565 sArc = dphi / rho
566 newPar = [rho, phi, dca]
567 z0 += sArc * dzds
568 newPar += [dzds, z0]
569
570 return newPar
571
572
573
575class gblHelixPrediction(object):
576
577
587 def __init__(self, sArc, pred, tDir, uDir, vDir, nDir, pos):
588
589 self.__sarc = sArc
590
591 self.__pred = pred
592
593 self.__tdir = tDir
594
595 self.__udir = uDir
596
597 self.__vdir = vDir
598
599 self.__ndir = nDir
600
601 self.__pos = pos
602 #
603
604
605 def getArcLength(self):
606 return self.__sarc
607
608
609 def getMeasPred(self):
610 return self.__pred
611
612
613 def getPos(self):
614 return self.__pos
615
616
617 def getDirection(self):
618 return self.__tdir
619
620
622 return np.dot(self.__tdir, self.__ndir)
623
624
629 cosTheta = self.__tdir[2]; sinTheta = math.sqrt(self.__tdir[0] ** 2 + self.__tdir[1] ** 2)
630 cosPhi = self.__tdir[0] / sinTheta; sinPhi = self.__tdir[1] / sinTheta
631 return np.array([[-sinPhi, cosPhi, 0.], [-cosPhi * cosTheta, -sinPhi * cosTheta, sinTheta]])
632
633
634if __name__ == '__main__':
635 exampleSit()
User supplied point on (initial) trajectory.
Definition: gblfit.py:182
General Broken Lines Trajectory.
Definition: gblfit.py:762
Prediction (from helix at measurement)
Definition: gblsit.py:575
def getDirection(self)
Get (track) direction.
Definition: gblsit.py:617
def getArcLength(self)
Get arc-length
Definition: gblsit.py:605
def getCosIncidence(self)
Get cosine of incidence.
Definition: gblsit.py:621
def getCurvilinearDirs(self)
Get curvilinear directions (U,V)
Definition: gblsit.py:628
def getMeasPred(self)
Get measurement prediction.
Definition: gblsit.py:609
def __init__(self, sArc, pred, tDir, uDir, vDir, nDir, pos)
Constructor.
Definition: gblsit.py:587
def getPos(self)
Get Position.
Definition: gblsit.py:613
Silicon detector.
Definition: gblsit.py:357
def generateHits(self, qbyp, genPar)
Generate hits on helix.
Definition: gblsit.py:383
def getLayers(self)
Get layers.
Definition: gblsit.py:374
def __init__(self, layers, bfac)
Constructor.
Definition: gblsit.py:364
def isComposite(self)
Is composite?
Definition: gblsit.py:306
__precision
precision (for reconstruction)
Definition: gblsit.py:256
__measDirs
measurement directions
Definition: gblsit.py:267
def intersectWithHelix(self, helix)
Intersect with helix.
Definition: gblsit.py:294
def getSpacing(self)
Get spacing.
Definition: gblsit.py:310
__ijkDirs
local alignment system (IJK = YZX)
Definition: gblsit.py:269
def getRadiationLength(self)
Get radiation length.
Definition: gblsit.py:274
def __init__(self, layer)
Constructor.
Definition: gblsit.py:246
__uDir
measurement direction u
Definition: gblsit.py:261
def getRigidBodyDerLocal(self, position, trackDir)
Get rigid body derivatives in local (alignment) frame.
Definition: gblsit.py:341
def getPrecision(self)
Get precision.
Definition: gblsit.py:282
def intersectWithHelix2(self, helix)
Intersect with helix (2nd sub layer)
Definition: gblsit.py:302
__xbyx0
radiation length
Definition: gblsit.py:250
__nDir
normal to measurement plane
Definition: gblsit.py:265
__resolution
resolution (for simulation)
Definition: gblsit.py:254
__vDir
measurement direction v
Definition: gblsit.py:263
def getResolution(self)
Get resolution.
Definition: gblsit.py:278
def getMeasSystemDirs(self)
Get directions of measurement system.
Definition: gblsit.py:286
def getRigidBodyDerGlobal(self, position, trackDir)
Get rigid body derivatives in global frame.
Definition: gblsit.py:319
__spacing
spacing (for composite layers)
Definition: gblsit.py:271
__xRelCenter
XY circle parameter: X position of center / R.
Definition: gblsit.py:458
__rinv
curvature (in XY)
Definition: gblsit.py:446
__dir0
direction vector at point of closest approach (in XY)
Definition: gblsit.py:450
__phi0
flight direction at point of closest approach (in XY)
Definition: gblsit.py:448
def __init__(self, parameter)
Constructor.
Definition: gblsit.py:444
__z0
Z position at distance of closest approach.
Definition: gblsit.py:456
__dca
distance of closest approach in (XY)
Definition: gblsit.py:452
def moveTo(self, newRefPoint)
Change reference point.
Definition: gblsit.py:540
def getPrediction(self, refPos, uDir, vDir)
Get prediction.
Definition: gblsit.py:471
__yRelCenter
XY circle parameter: Y position of center / R.
Definition: gblsit.py:460
def getArcLengthXY(self, xPos, yPos)
Get (2D) arc length for given point.
Definition: gblsit.py:522
def gblSimpleJacobian(ds, cosl, bfac)
Simple jacobian.
Definition: gblsit.py:219
def gblMultipleScatteringError(qbyp, xbyx0)
Multiple scattering error.
Definition: gblsit.py:235
def exampleSit()
Simulate and reconstruct helical tracks in silicon pixel and (1D or 2D) strip detectors.
Definition: gblsit.py:81