diff --git a/tracking/src/main/java/org/hps/svt/OccupancyPlots.java b/tracking/src/main/java/org/hps/svt/OccupancyPlots.java index 041f0fd07d..3d3171ebb3 100644 --- a/tracking/src/main/java/org/hps/svt/OccupancyPlots.java +++ b/tracking/src/main/java/org/hps/svt/OccupancyPlots.java @@ -15,7 +15,8 @@ import org.lcsim.detector.ITransform3D; import org.lcsim.detector.tracker.silicon.ChargeCarrier; import org.lcsim.detector.tracker.silicon.HpsSiSensor; -import org.lcsim.detector.tracker.silicon.SiStrips; +import org.lcsim.detector.tracker.silicon.SiSensorElectrodes; +import org.lcsim.detector.tracker.silicon.SiStriplets; import org.lcsim.event.EventHeader; import org.lcsim.event.GenericObject; import org.lcsim.event.RawTrackerHit; @@ -155,10 +156,15 @@ public static Map createStripPositionMap(HpsSiSensor sensor Map positionMap = new HashMap(); for (ChargeCarrier carrier : ChargeCarrier.values()) { if (sensor.hasElectrodesOnSide(carrier)) { - SiStrips strips = (SiStrips) sensor.getReadoutElectrodes(carrier); + // Declared as the interface rather than SiStrips: L0 sensors + // (HpsThinSiSensor) carry SiStriplets, which extend SiPixels and + // are not SiStrips. getCellPosition() is on the interface and + // SiStriplets overrides it to return the striplet centre, so the + // polymorphic call is correct for both. + SiSensorElectrodes strips = sensor.getReadoutElectrodes(carrier); ITransform3D parentToLocal = sensor.getReadoutElectrodes(carrier).getParentToLocal(); ITransform3D localToGlobal = sensor.getReadoutElectrodes(carrier).getLocalToGlobal(); - for (int physicalChannel = 0; physicalChannel < 640; physicalChannel++) { + for (int physicalChannel = 0; physicalChannel < sensor.getNumberOfChannels(); physicalChannel++) { Hep3Vector localStripPosition = strips.getCellPosition(physicalChannel); Hep3Vector stripPosition = parentToLocal.transformed(localStripPosition); Hep3Vector globalStripPosition = localToGlobal.transformed(stripPosition); @@ -168,7 +174,66 @@ public static Map createStripPositionMap(HpsSiSensor sensor } return positionMap; } - + + /** + * Get the number of readout columns of a sensor. Strip sensors (L1-L6, and + * all layers of the pre-2019 geometry) have a single column. L0 striplet + * sensors split their strips into two columns along the strip direction. + * + * @param sensor : HpsSiSensor + * @return The number of columns, always >= 1 + */ + private static int getNumberOfColumns(HpsSiSensor sensor) { + SiSensorElectrodes electrodes = sensor.getReadoutElectrodes(ChargeCarrier.HOLE); + // Axis 1 is the column axis for both SiStrips (where it is 1) and + // SiStriplets (where it is 2), but only when the electrodes are 2D. + return electrodes.getNAxes() > 1 ? electrodes.getNCells(1) : 1; + } + + /** + * Get the readout column a physical channel belongs to. + * + * Note that SiSensorElectrodes.getColumnNumber(int) is NOT consistent across + * implementations - SiStriplets returns the column, but SiStrips subclasses + * return the strip number itself - so it can only be called on striplets. + * + * @param sensor : HpsSiSensor + * @param physicalChannel : physical channel number + * @return The column number, 0 for strip sensors + */ + private static int getColumnNumber(HpsSiSensor sensor, int physicalChannel) { + SiSensorElectrodes electrodes = sensor.getReadoutElectrodes(ChargeCarrier.HOLE); + if (electrodes instanceof SiStriplets) { + return ((SiStriplets) electrodes).getColumnNumber(physicalChannel); + } + return 0; + } + + /** + * Get the plot name suffix identifying a column. + * + * Empty for single-column (strip) sensors, so that both the map keys and the + * histogram names are unchanged from the pre-L0 behaviour. + * + * @param sensor : HpsSiSensor + * @param column : column number + * @return The suffix + */ + private static String getColumnSuffix(HpsSiSensor sensor, int column) { + return getNumberOfColumns(sensor) == 1 ? "" : " - Column " + column; + } + + /** + * Get the key used to store the per-column position plots of a sensor. + * + * @param sensor : HpsSiSensor + * @param column : column number + * @return The map key + */ + private static String getPositionPlotKey(HpsSiSensor sensor, int column) { + return sensor.getName() + getColumnSuffix(sensor, column); + } + //Grab the channel associated with the cluster based on position. private int getClusterChan(SiTrackerHitStrip1D h, HpsSiSensor sensor){ List rawhits = h.getRawHits(); @@ -178,10 +243,12 @@ private int getClusterChan(SiTrackerHitStrip1D h, HpsSiSensor sensor){ Hep3Vector pos_global = global.getPositionAsVector(); double clusterHitPos = pos_global.y(); RawTrackerHit hit = rawhits.get(0); - int chan = hit.getIdentifierFieldValue("strip"); - double diffmin = Math.abs(getStripPosition(sensor,chan).y() - clusterHitPos); + double diffmin = Double.MAX_VALUE; for(RawTrackerHit rawhit : rawhits){ int chanhit = rawhit.getIdentifierFieldValue("strip"); + // Unbonded channels past the last readout channel have no entry in + // the strip position map. + if (!sensor.isValidChannel(chanhit)) continue; double diff = Math.abs(getStripPosition(sensor,chanhit).y() - clusterHitPos); if(diff < diffmin){ diffmin = diff; @@ -209,8 +276,10 @@ private void resetPlots() { clusterOccupancyPlots.get(sensor.getName()).reset(); if (enablePositionPlots) { - positionPlots.get(sensor.getName()).reset(); - clusterPositionPlots.get(sensor.getName()).reset(); + for (int column = 0; column < getNumberOfColumns(sensor); column++) { + positionPlots.get(getPositionPlotKey(sensor, column)).reset(); + clusterPositionPlots.get(getPositionPlotKey(sensor, column)).reset(); + } //clusterPositionPlotCounts.get(sensor.getName()).reset(); } @@ -219,8 +288,8 @@ private void resetPlots() { } // Reset the hit counters. - occupancyMap.put(sensor.getName(), new int[640]); - clusterOccupancyMap.put(sensor.getName(), new int[640]); + occupancyMap.put(sensor.getName(), new int[sensor.getNumberOfChannels()]); + clusterOccupancyMap.put(sensor.getName(), new int[sensor.getNumberOfChannels()]); } } @@ -252,11 +321,18 @@ protected void detectorChanged(Detector detector) { occupancyPlots.put(sensor.getName(),histogramFactory.createHistogram1D(sensor.getName() + " - Occupancy", nChan, 0, nChan)); clusterOccupancyPlots.put(sensor.getName(),histogramFactory.createHistogram1D(sensor.getName() + " - Cluster Occupancy", nChan, 0, nChan)); if (enablePositionPlots){ - positionPlots.put(sensor.getName(),histogramFactory.createHistogram1D(sensor.getName() + " - Occupancy vs Position", 1000, -60, 60)); - clusterPositionPlots.put(sensor.getName(),histogramFactory.createHistogram1D(sensor.getName() + " - Cluster occupancy vs Position", 1000, -60, 60)); + // Strip sensors have a single column and keep the original plot + // names. L0 striplet sensors get one plot per column, so that the + // channel to position mapping stays one to one within each plot. + for (int column = 0; column < getNumberOfColumns(sensor); column++) { + String key = getPositionPlotKey(sensor, column); + String suffix = getColumnSuffix(sensor, column); + positionPlots.put(key,histogramFactory.createHistogram1D(sensor.getName() + " - Occupancy vs Position" + suffix, 1000, -60, 60)); + clusterPositionPlots.put(key,histogramFactory.createHistogram1D(sensor.getName() + " - Cluster occupancy vs Position" + suffix, 1000, -60, 60)); + } } - occupancyMap.put(sensor.getName(), new int[640]); - clusterOccupancyMap.put(sensor.getName(), new int[640]); + occupancyMap.put(sensor.getName(), new int[nChan]); + clusterOccupancyMap.put(sensor.getName(), new int[nChan]); if (enableMaxSamplePlots) maxSamplePositionPlots.put(sensor.getName(),histogramFactory.createHistogram1D(sensor.getName() + " - Max Sample Number", 6, -0.5, 5.5)); } @@ -342,8 +418,15 @@ public void process(EventHeader event) { } if (maxSamplePosition == -1 || maxSamplePosition == maxSamplePositionFound) { - occupancyMap.get(((HpsSiSensor) rawHit.getDetectorElement()).getName())[rawHit - .getIdentifierFieldValue("strip")]++; + HpsSiSensor sensor = (HpsSiSensor) rawHit.getDetectorElement(); + int strip = rawHit.getIdentifierFieldValue("strip"); + // Raw hits can carry the unbonded channel one past the last + // readout channel (639 on a 639 channel strip sensor), which + // StripMaker also drops. It has no strip position and no + // histogram bin, so skip it rather than index past the counters. + if (sensor.isValidChannel(strip)) { + occupancyMap.get(sensor.getName())[strip]++; + } } if (enableMaxSamplePlots) { @@ -356,14 +439,18 @@ public void process(EventHeader event) { if (event.hasCollection(SiTrackerHitStrip1D.class, stripClusterCollectionName)) { List stripHits1D = event.get(SiTrackerHitStrip1D.class, stripClusterCollectionName); for (SiTrackerHitStrip1D h : stripHits1D) { - int chan = getClusterChan(h,((HpsSiSensor) h.getRawHits().get(0).getDetectorElement())); - + HpsSiSensor sensor = (HpsSiSensor) h.getRawHits().get(0).getDetectorElement(); + int chan = getClusterChan(h, sensor); + if (!sensor.isValidChannel(chan)) { + continue; + } + if (enableClusterTimeCuts) { if (h.getTime() < clusterTimeCutMax && h.getTime() > clusterTimeCutMin) { - clusterOccupancyMap.get(((HpsSiSensor) h.getRawHits().get(0).getDetectorElement()).getName())[chan]++; + clusterOccupancyMap.get(sensor.getName())[chan]++; } } else { - clusterOccupancyMap.get(((HpsSiSensor) h.getRawHits().get(0).getDetectorElement()).getName())[chan]++; + clusterOccupancyMap.get(sensor.getName())[chan]++; } } } @@ -376,7 +463,9 @@ public void process(EventHeader event) { occupancyPlots.get(sensor.getName()).reset(); clusterOccupancyPlots.get(sensor.getName()).reset(); if (enablePositionPlots) { - positionPlots.get(sensor.getName()).reset(); + for (int column = 0; column < getNumberOfColumns(sensor); column++) { + positionPlots.get(getPositionPlotKey(sensor, column)).reset(); + } } for (int channel = 0; channel < strips.length; channel++) { double stripOccupancy = (double) strips[channel] / (double) eventCount; @@ -385,7 +474,7 @@ public void process(EventHeader event) { if (enablePositionPlots) { double stripPosition = this.getStripPosition(sensor, channel).y(); - positionPlots.get(sensor.getName()).fill(stripPosition, stripOccupancy); + positionPlots.get(getPositionPlotKey(sensor, getColumnNumber(sensor, channel))).fill(stripPosition, stripOccupancy); } } for (int channel = 0; channel < clusterStrips.length; channel++) { @@ -395,7 +484,7 @@ public void process(EventHeader event) { if (enablePositionPlots) { double clusterPosition = this.getStripPosition(sensor, channel).y(); - clusterPositionPlots.get(sensor.getName()).fill(clusterPosition, clusterOccupancy); + clusterPositionPlots.get(getPositionPlotKey(sensor, getColumnNumber(sensor, channel))).fill(clusterPosition, clusterOccupancy); } } } @@ -409,10 +498,21 @@ public void endOfData() { System.out.println("%======================== Active Edge Sensor Occupancies =======================%"); System.out.println("%===============================================================================%"); System.out.println("% Total Events: " + eventCount); - // Calculate the occupancies at the sensor edge - int[] topActiveEdgeStripOccupancy = new int[6]; - int[] bottomActiveEdgeStripOccupancy = new int[6]; + // Calculate the occupancies at the sensor edge. The number of layers is + // taken from the geometry rather than assumed to be 6, since the 2019+ + // geometry adds L0 for a total of 7. + int nLayers = 0; + for (HpsSiSensor sensor : sensors) { + nLayers = Math.max(nLayers, getLayerNumber(sensor)); + } + int[] topActiveEdgeStripOccupancy = new int[nLayers]; + int[] bottomActiveEdgeStripOccupancy = new int[nLayers]; for (HpsSiSensor sensor : sensors) { + // The active edge is the last channel on the positron side and the + // second channel on the electron side. Both are sensor-size + // dependent: 638 (of 639) and 1 for strip sensors, 511 (of 512) and 1 + // for L0 striplet sensors. + int edgeChannel = sensor.getNumberOfChannels() - 1; if (sensor.isTopLayer() && sensor.isAxial()) { if (sensor.getSide().equals(HpsSiSensor.ELECTRON_SIDE)) { System.out.println("% Top Layer " + getLayerNumber(sensor) + " Hit Counts: " @@ -420,8 +520,8 @@ public void endOfData() { topActiveEdgeStripOccupancy[getLayerNumber(sensor) - 1] += occupancyMap.get(sensor.getName())[1]; } else { System.out.println("% Top Layer " + getLayerNumber(sensor) + " Hit Counts: " - + occupancyMap.get(sensor.getName())[638]); - topActiveEdgeStripOccupancy[getLayerNumber(sensor) - 1] += occupancyMap.get(sensor.getName())[638]; + + occupancyMap.get(sensor.getName())[edgeChannel]); + topActiveEdgeStripOccupancy[getLayerNumber(sensor) - 1] += occupancyMap.get(sensor.getName())[edgeChannel]; } } else if (sensor.isBottomLayer() && sensor.isAxial()) { if (sensor.getSide().equals(HpsSiSensor.ELECTRON_SIDE)) { @@ -430,13 +530,13 @@ public void endOfData() { bottomActiveEdgeStripOccupancy[getLayerNumber(sensor) - 1] += occupancyMap.get(sensor.getName())[1]; } else { System.out.println("% Bottom Layer " + getLayerNumber(sensor) + " Hit Counts: " - + occupancyMap.get(sensor.getName())[638]); - bottomActiveEdgeStripOccupancy[getLayerNumber(sensor) - 1] += occupancyMap.get(sensor.getName())[638]; + + occupancyMap.get(sensor.getName())[edgeChannel]); + bottomActiveEdgeStripOccupancy[getLayerNumber(sensor) - 1] += occupancyMap.get(sensor.getName())[edgeChannel]; } } } - for (int layerN = 0; layerN < 6; layerN++) { + for (int layerN = 0; layerN < nLayers; layerN++) { double topStripOccupancy = (double) topActiveEdgeStripOccupancy[layerN] / (double) eventCount; topStripOccupancy /= this.timeWindowWeight; System.out.println("% Top Layer " + (layerN + 1) + ": Occupancy in " + (24 / this.timeWindowWeight)