View Javadoc
1   package org.lcsim.detector.tracker.silicon;
2   
3   import hep.physics.matrix.BasicMatrix;
4   import hep.physics.vec.BasicHep3Vector;
5   import hep.physics.vec.VecOp;
6   
7   import java.util.HashMap;
8   import java.util.HashSet;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Set;
12  
13  import org.lcsim.detector.IDetectorElement;
14  import org.lcsim.detector.IRotation3D;
15  import org.lcsim.detector.ITranslation3D;
16  import org.lcsim.detector.RotationPassiveXYZ;
17  import org.lcsim.detector.Transform3D;
18  import org.lcsim.detector.Translation3D;
19  import org.lcsim.detector.identifier.IIdentifier;
20  import org.lcsim.detector.solids.Box;
21  import org.lcsim.detector.solids.LineSegment3D;
22  import org.lcsim.detector.solids.Polygon3D;
23  
24  /**
25   * This class extends {@link SiSensor} with conditions specific to HPS SVT
26   * half-modules (sensors) used during the engineering run and beyond. Each
27   * half-module is uniquely identified by a FEB ID/Hybrid ID pair which is then
28   * related to calibration conditions such as baseline, noise, gain etc.
29   *
30   * @author Omar Moreno, SLAC National Accelerator Laboratory
31   */
32  public class HpsSiSensor extends SiSensor {
33  
34      // --------------//
35      //   Constants   //
36      // --------------//
37  
38      public final static int NUMBER_OF_SAMPLES = 6;
39      private final static int NUMBER_OF_SHAPE_FIT_PARAMETERS = 4;
40  
41      public final static int AMPLITUDE_INDEX = 0;
42      public final static int T0_INDEX = 1;
43      public final static int TP_INDEX = 2;
44  
45      public final static String ELECTRON_SIDE = "ELECTRON";
46      public final static String POSITRON_SIDE = "POSITRON";
47  
48      //-----------------------//
49      //   Sensor properties   //
50      //-----------------------//
51  
52      protected int febID;
53      protected int febHybridID;
54      protected double t0Shift = 0;
55      protected boolean isAxial = false;
56      protected boolean isStereo = false;
57  
58      private final double readoutStripCapacitanceIntercept = 0;
59      private final double readoutStripCapacitanceSlope = 0.16; // pf/mm
60      private final double senseStripCapacitanceIntercept = 0;
61      private final double senseStripCapacitanceSlope = 0.16; // pf/mm
62  
63      /*
64       * Adding separate strip capacitance for long detectors following
65       * S/N = mip_charge/(270e- + 36*C[pf/cm]*L[cm] e.g. for expected S/N=16
66       * and L=20cm -> C=0.1708pf/mm e.g. for expected S/N=8 and
67       * L=20cm -> C=0.39pf/mm FIXME: This should be taken into account by the
68       * noise model.
69       */
70      protected double longSensorLengthThreshold = 190.0; // mm
71      protected double readoutLongStripCapacitanceSlope = 0.39; // pf/mm
72      protected double senseLongStripCapacitanceSlope = 0.39; // pf/mm
73  
74      // --------------------//
75      //   Conditions Maps   //
76      // --------------------//
77      protected Map<Integer, double[]> pedestalMap = new HashMap<Integer, double[]>();
78      protected Map<Integer, double[]> noiseMap = new HashMap<Integer, double[]>();
79      protected Map<Integer, Double> gainMap = new HashMap<Integer, Double>();
80      protected Map<Integer, Double> offsetMap = new HashMap<Integer, Double>();
81      protected Map<Integer, double[]> shapeFitParametersMap = new HashMap<Integer, double[]>();
82      protected Set<Integer> badChannels = new HashSet<Integer>();
83      protected int millepedeId = -1;
84  
85      /**
86       * This class constructor matches the signature of <code>SiSensor</code>.
87       *
88       * @param sensorid The sensor ID.
89       * @param name The name of the sensor.
90       * @param parent The parent DetectorElement.
91       * @param support The physical support path.
92       * @param id The identifier of the sensor.
93       */
94      public HpsSiSensor(final int sensorid, final String name, final IDetectorElement parent, final String support,
95              final IIdentifier id) {
96          super(sensorid, name, parent, support, id);
97  
98          // Set the default sensor orientation using the layer number. For sensors
99          // belonging to the top volume, odd (even) layers are axial (stereo).
100         // For sensors belonging to the bottom, even (odd) layers are axial (stereo).
101         if (this.isTopLayer() && this.getLayerNumber() % 2 == 1) {
102             this.setAxial(true);
103         } else if (this.isBottomLayer() && this.getLayerNumber() % 2 == 0) {
104             this.setAxial(true);
105         } else {
106             this.setStereo(true);
107         }
108 
109         this.initialize();
110     }
111 
112     /**
113      * Get whether this sensor is in the top half of the detector. Modules in the top half have module numbers of 0 or
114      * 2.
115      *
116      * @return True if sensor is in top layer; false if not.
117      */
118     public boolean isTopLayer() {
119         return getModuleNumber() % 2 == 0;
120     }
121 
122     /**
123      * Get whether this sensor is in the bottom half of the detector. Modules in the bottom half have module numbers of
124      * 1 or 3.
125      *
126      * @return True if sensor is in bottom layer; false if not.
127      */
128     public boolean isBottomLayer() {
129         return getModuleNumber() % 2 != 0;
130     }
131 
132     /**
133      * Get the module number of the sensor.
134      *
135      * @return The module number of the sensor.
136      */
137     public int getModuleNumber() {
138         return getTrackerIdHelper().getModuleValue(getIdentifier());
139     }
140 
141     /**
142      * Get the specific type of identifier helper for this component.
143      *
144      * @return The identifier helper.
145      */
146     public SiTrackerIdentifierHelper getTrackerIdHelper() {
147         return (SiTrackerIdentifierHelper) getIdentifierHelper();
148     }
149 
150     /**
151      * Get whether this sensor is axial.
152      *
153      * @return True if sensor is axial; false if not.
154      */
155     public boolean isAxial() {
156         return this.isAxial;
157     }
158 
159     /**
160      * Get whether this sensor is stereo.
161      *
162      * @return True is sensor is stereo; false if not.
163      */
164     public boolean isStereo() {
165         return this.isStereo;
166     }
167 
168     /**
169      * Get the pedestal for the given channel and sample number.
170      *
171      * @param channel The channel number.
172      * @param sample The sample number.
173      * @return The pedestal value for the given channel and sample or null if not set.
174      */
175     public Double getPedestal(final int channel, final int sample) {
176         if (sample >= NUMBER_OF_SAMPLES) {
177             throw new RuntimeException("The sample number must be less than " + NUMBER_OF_SAMPLES);
178         }
179         return this.pedestalMap.get(channel)[sample];
180     }
181 
182     /**
183      * Get the noise for the given channel and sample number.
184      *
185      * @param channel The channel number.
186      * @param sample The sample number.
187      * @return The noise value for the given channel and sample or null if not set.
188      */
189     public Double getNoise(final int channel, final int sample) {
190         if (sample >= NUMBER_OF_SAMPLES) {
191             throw new RuntimeException("The sample number must be less than " + NUMBER_OF_SAMPLES);
192         }
193         return this.noiseMap.get(channel)[sample];
194     }
195 
196     /**
197      * Get the gain for the given channel.
198      *
199      * @param channel The channel number.
200      * @return The gain value for the channel or null if not set.
201      */
202     public Double getGain(final int channel) {
203         return this.gainMap.get(channel);
204     }
205 
206     /**
207      * Get the offset for the given channel.
208      *
209      * @param channel The channel number.
210      * @return The offset for the channel or null if not set.
211      */
212     public Double getOffset(final int channel) {
213         return this.offsetMap.get(channel);
214     }
215 
216     /** @return The charge transfer efficiency of the readout strips. */
217     public double getReadoutTransferEfficiency() {
218         return 0.986;
219     }
220 
221     /** @return The charge transfer efficiency of the sense strips. */
222     public double getSenseTransferEfficiency() {
223         return 0.419;
224     }
225 
226     /**
227      * Get the shape fit parameters (amplitude, t0, tp) associated with a given channel.
228      *
229      * @param channel The channel number.
230      * @return The shape fit results for the channel.
231      */
232     public double[] getShapeFitParameters(final int channel) {
233         return this.shapeFitParametersMap.get(channel);
234     }
235 
236     /**
237      * Get whether the given channel is bad or not.
238      *
239      * @param channel The channel number.
240      * @return True if channel is bad; false if not.
241      */
242     public boolean isBadChannel(final int channel) {
243         return this.badChannels.contains(channel);
244     }
245 
246     /**
247      * Get the total number of channels in the sensor.
248      *
249      * @return The total number of channels in the sensor.
250      */
251     public int getNumberOfChannels() {
252         return this.getReadoutElectrodes(ChargeCarrier.HOLE).getNCells();
253     }
254 
255     /** @return The total number of sense strips per sensor. */
256     public int getNumberOfSenseStrips() {
257         return 1277;
258     }
259 
260     /**
261      * Get whether the given channel number if valid.
262      *
263      * @param channel The channel number.
264      * @return True if channel number is valid; false if not.
265      */
266     public boolean isValidChannel(final int channel) {
267         return this.getNumberOfChannels() >= 0 && channel < this.getNumberOfChannels();
268     }
269 
270     /**
271      * Get the front end board (FEB) ID associated with this sensor.
272      *
273      * @return The FEB ID
274      */
275     public int getFebID() {
276         return this.febID;
277     }
278 
279     /**
280      * Get the FEB hybrid ID of the sensor.
281      *
282      * @return The FEB hybrid number of the sensor.
283      */
284     public int getFebHybridID() {
285         return this.febHybridID;
286     }
287 
288     /**
289      * Get the layer number of the sensor.
290      *
291      * @return The layer number of the sensor.
292      */
293     public int getLayerNumber() {
294         return getIdentifierHelper().getValue(getIdentifier(), "layer");
295     }
296 
297     /**
298      * Get the t0 shift for this sensor.
299      *
300      * @return The t0 shift for this sensor.
301      */
302     public double getT0Shift() {
303         return this.t0Shift;
304     }
305 
306     /**
307      * Get the sensor side (ELECTRON or POSITRON). For single sensor half-modules, the side will always be ELECTRON.
308      *
309      * @return The side the sensor is on (ELECTRON or POSITRON)
310      */
311     public String getSide() {
312         return this.getModuleNumber() < 2 ? ELECTRON_SIDE : POSITRON_SIDE;
313     }
314 
315     /** @return The readout strip pitch. */
316     public double getReadoutStripPitch() {
317         return 0.060; // mm
318     }
319 
320     /** @return The sense strip pitch. */
321     public double getSenseStripPitch() {
322         return 0.030; // mm
323     }
324 
325     /**
326      * Generate an ID for a channel (strip) on a sensor.
327      *
328      * @param channel : Physical channel number
329      * @return the channel ID
330      */
331     public long makeChannelID(final int channel) {
332         final int sideNumber = this.hasElectrodesOnSide(ChargeCarrier.HOLE) ? ChargeCarrier.HOLE.charge()
333                 : ChargeCarrier.ELECTRON.charge();
334         return this.makeStripId(channel, sideNumber).getValue();
335     }
336 
337     /**
338      * Set the pedestal value for all samples for a given channel.
339      *
340      * @param channel The channel number.
341      * @param pedestal The pedestal values for all samples.
342      */
343     public void setPedestal(final int channel, final double[] pedestal) {
344         if (pedestal.length > NUMBER_OF_SAMPLES) {
345             throw new RuntimeException("The number of pedestal samples must be equal to" + NUMBER_OF_SAMPLES);
346         }
347         this.pedestalMap.put(channel, pedestal);
348     }
349 
350     /**
351      * Set the noise value for the given channel.
352      *
353      * @param channel The channel number.
354      * @param noise The noise values for all samples.
355      */
356     public void setNoise(final int channel, final double[] noise) {
357         if (noise.length > NUMBER_OF_SAMPLES) {
358             throw new RuntimeException("The number of pedestal samples must be equal to" + NUMBER_OF_SAMPLES);
359         }
360         this.noiseMap.put(channel, noise);
361     }
362 
363     /**
364      * Set the gain value for the given channel.
365      *
366      * @param channel The channel number.
367      * @param gain The gain value.
368      */
369     public void setGain(final int channel, final double gain) {
370         this.gainMap.put(channel, gain);
371     }
372 
373     /**
374      * Set the offset for the given channel.
375      *
376      * @param channel The channel number.
377      * @param offset The offset value.
378      */
379     public void setOffset(final int channel, final double offset) {
380         this.offsetMap.put(channel, offset);
381     }
382 
383     /**
384      * Set the shape fit results for the given channel.
385      *
386      * @param channel The channel number.
387      * @param shapeFitParameters The shape fit results array (should be length 4).
388      */
389     public void setShapeFitParameters(final int channel, final double[] shapeFitParameters) {
390         if (shapeFitParameters.length != NUMBER_OF_SHAPE_FIT_PARAMETERS) {
391             throw new IllegalArgumentException("Number of shape fit parameters is incorrect: "
392                     + shapeFitParameters.length);
393         }
394         this.shapeFitParametersMap.put(channel, shapeFitParameters);
395     }
396 
397     /**
398      * Flag the given channel as bad.
399      *
400      * @param channel The channel number.
401      */
402     public void setBadChannel(final int channel) {
403         this.badChannels.add(channel);
404     }
405 
406     /**
407      * Set the front end board (FEB) ID of the sensor.
408      *
409      * @param febID FEB ID The FEB ID of the sensor.
410      */
411     public void setFebID(final int febID) {
412         this.febID = febID;
413     }
414 
415     /**
416      * Set the FEB hybrid ID of the sensor.
417      *
418      * @param febHybridID FEB hybrid ID The FEB hybrid ID.
419      */
420     public void setFebHybridID(final int febHybridID) {
421         this.febHybridID = febHybridID;
422     }
423 
424     /**
425      * Set the t0 shift for this sensor.
426      *
427      * @param t0Shift The t0 shift for this sensor.
428      */
429     public void setT0Shift(final double t0Shift) {
430         this.t0Shift = t0Shift;
431     }
432 
433     /**
434      * Flag the sensor as being axial.
435      *
436      * @param isAxial true if the sensor is Axial, false otherwise
437      */
438     public void setAxial(final boolean isAxial) {
439         this.isAxial = isAxial;
440     }
441 
442     /**
443      * Flag the sensor as being stereo
444      *
445      * @param isStereo true is the sensor is stereo, false otherwise
446      */
447     public void setStereo(final boolean isStereo) {
448         this.isStereo = isStereo;
449     }
450 
451     /**
452      * Reset the time dependent conditions data of this sensor. This does NOT reset the sensor setup information, which
453      * is assumed to be fixed once it is setup for a given session.
454      */
455     public void reset() {
456         this.pedestalMap.clear();
457         this.noiseMap.clear();
458         this.offsetMap.clear();
459         this.shapeFitParametersMap.clear();
460         this.badChannels.clear();
461         this.gainMap.clear();
462         this.t0Shift = 0;
463     }
464 
465     @Override
466     public String toString() {
467 
468         final StringBuffer buffer = new StringBuffer();
469         buffer.append("HpsSiSensor: " + this.getName());
470         buffer.append("\n");
471         buffer.append("----------------------------------");
472         buffer.append("\n");
473         buffer.append("Feb ID: " + this.getFebID() + "\n");
474         buffer.append("Feb Hybrid ID: " + this.getFebHybridID() + "\n");
475         buffer.append("Layer: " + this.getLayerNumber() + "\n");
476         buffer.append("Module: " + this.getModuleNumber() + "\n");
477         buffer.append("Number of readout strips: " + this.getReadoutElectrodes(ChargeCarrier.HOLE).getNCells() + "\n");
478         buffer.append("Number of sense strips: " + this.getSenseElectrodes(ChargeCarrier.HOLE).getNCells() + "\n");
479         buffer.append("Strip length: " + this.getStripLength() + "\n");
480         buffer.append("----------------------------------");
481 
482         return buffer.toString();
483     }
484 
485     /**
486      * Setup the geometry and electrical characteristics of an {@link HpsSiSensor}
487      */
488     @Override
489     public void initialize() {
490 
491         // Get the solid corresponding to the sensor volume
492         final Box sensorSolid = (Box) this.getGeometry().getLogicalVolume().getSolid();
493 
494         // Get the faces of the solid corresponding to the n and p sides of the sensor
495         final Polygon3D pSide = sensorSolid.getFacesNormalTo(new BasicHep3Vector(0, 0, 1)).get(0);
496         final Polygon3D nSide = sensorSolid.getFacesNormalTo(new BasicHep3Vector(0, 0, -1)).get(0);
497 
498         // p side collects holes.
499         this.setBiasSurface(ChargeCarrier.HOLE, pSide);
500 
501         // n side collects electrons.
502         this.setBiasSurface(ChargeCarrier.ELECTRON, nSide);
503 
504         // Translate to the outside of the sensor solid in order to setup the electrodes
505         final ITranslation3D electrodesPosition = new Translation3D(VecOp.mult(-pSide.getDistance(), pSide.getNormal()));
506 
507         // Align the strips with the edge of the sensor.
508         final IRotation3D electrodesRotation = new RotationPassiveXYZ(0, 0, 0);
509         final Transform3D electrodesTransform = new Transform3D(electrodesPosition, electrodesRotation);
510 
511         // Set the number of readout and sense electrodes.
512         final SiStrips readoutElectrodes = new SiStrips(ChargeCarrier.HOLE, getReadoutStripPitch(), this,
513                 electrodesTransform);
514         final SiStrips senseElectrodes = new SiStrips(ChargeCarrier.HOLE, getSenseStripPitch(),
515                 this.getNumberOfSenseStrips(), this, electrodesTransform);
516 
517         final double readoutCapacitance = this.getStripLength() > this.longSensorLengthThreshold ? this.readoutLongStripCapacitanceSlope
518                 : this.readoutStripCapacitanceSlope;
519         final double senseCapacitance = this.getStripLength() > this.longSensorLengthThreshold ? this.senseLongStripCapacitanceSlope
520                 : this.senseStripCapacitanceSlope;
521 
522         // Set the strip capacitance.
523         readoutElectrodes.setCapacitanceIntercept(this.readoutStripCapacitanceIntercept);
524         readoutElectrodes.setCapacitanceSlope(readoutCapacitance);
525         senseElectrodes.setCapacitanceIntercept(this.senseStripCapacitanceIntercept);
526         senseElectrodes.setCapacitanceSlope(senseCapacitance);
527 
528         // Set sense and readout electrodes.
529         this.setSenseElectrodes(senseElectrodes);
530         this.setReadoutElectrodes(readoutElectrodes);
531 
532         // Set the charge transfer efficiency of both the sense and readout
533         // strips.
534         final double[][] transferEfficiencies = {{this.getReadoutTransferEfficiency(), this.getSenseTransferEfficiency()}};
535         this.setTransferEfficiencies(ChargeCarrier.HOLE, new BasicMatrix(transferEfficiencies));
536 
537     }
538 
539     /**
540      * Return the length of an {@link HpsSiSensor} strip. This is done by getting the face of the {@link HpsSiSensor}
541      * and returning the length of the longest edge.
542      *
543      * @return The length of the longest {@link HpsSiSensor} edge
544      */
545     protected double getStripLength() {
546 
547         double length = 0;
548 
549         // Get the faces normal to the sensor
550         final List<Polygon3D> faces = ((Box) this.getGeometry().getLogicalVolume().getSolid())
551                 .getFacesNormalTo(new BasicHep3Vector(0, 0, 1));
552         for (final Polygon3D face : faces) {
553 
554             // Loop through the edges of the sensor face and find the longest
555             // one
556             final List<LineSegment3D> edges = face.getEdges();
557             for (final LineSegment3D edge : edges) {
558                 if (edge.getLength() > length) {
559                     length = edge.getLength();
560                 }
561             }
562         }
563         return length;
564     }
565 
566     /**
567      * Set the sensor id used by millepede.
568      *
569      * @param id - millepede sensor id
570      */
571     public void setMillepedeId(final int id) {
572         this.millepedeId = id;
573     }
574 
575     /**
576      * Get the sensor id used by millepede.
577      *
578      * @return the millepede sensor id.
579      */
580     public int getMillepedeId() {
581         return this.millepedeId;
582     }
583 }