From fc1ec34f8a83f60b1696abe5483f9a22ee6c3967 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Fri, 1 Aug 2025 13:18:09 -0400 Subject: [PATCH 01/29] feat: downsampling --- .../detector/scalers/DaqScalersSequence.java | 54 +++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index e81880c990..cc71cf4ccd 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -26,10 +26,10 @@ public class DaqScalersSequence implements Comparator { public static final double TI_CLOCK_FREQ = 250e6; // Hz protected final List scalers=new ArrayList<>(); - + protected final List subsequences=new ArrayList<>(); + private Bank runConfigBank=null; private Bank runScalerBank=null; - private Bank rawScalerBank=null; static final Logger logger = Logger.getLogger(DaqScalersSequence.class.getName()); @@ -102,8 +102,14 @@ public DaqScalersSequence(SchemaFactory schema) { runScalerBank=new Bank(schema.getSchema("RUN::scaler")); } + public DaqScalersSequence(List inputScalers) { + for (DaqScalers inputScaler : inputScalers) + this.add(inputScaler); + } + public void clear() { scalers.clear(); + subsequences.clear(); } protected boolean add(DaqScalers ds) { @@ -293,7 +299,41 @@ public static DaqScalersSequence rebuildSequence(int tags, ConstantsManager conm } return seq; } - + + /** + * Sample this sequence, keeping every nth scaler readout as a subsequence of samples. + * Methods such as `getInterval` will only consider scaler readouts in this sample subsequence. + * The n _original_ scaler readouts between each sample are preserved as subsequences. + * @param n keep every nth scaler readout, including the zeroth and the last. `n` should be `>1`. + */ + public void downsample(int n) { + if (this.scalers.isEmpty() || n<=1) + return; + subsequences.clear(); + List keep = new ArrayList<>(); + keep.add(0); + for (int i=0; i=0; i--) + if (!keep.contains(i)) + this.scalers.remove(i); + } + + public double getBeamChargeProxy() { + return 0; // FIXME + } + + public DaqScalersSequence getSubsequence(long timestamp) { + return this.subsequences.get(this.findIndex(timestamp)); // FIXME: catch exception + } + + public List getSubsequences() { + return subsequences; + } + public static void main(String[] args) { final String dir="/Users/baltzell/data/CLAS12/rg-a/decoded/6b.2.0/"; @@ -348,6 +388,14 @@ public static void main(String[] args) { System.out.println("DaqScalersSequence: bad/good/badPercent: " +bad+" "+good+" "+100*((float)bad)/(bad+good)+"%"); + // QADB usage + long timestamp = 1000; + seq.downsample(2000); + for(DaqScalersSequence subseq : seq.getSubsequences()) + System.out.println(subseq.getBeamChargeProxy()); + System.out.println(seq.getSubsequence(timestamp).getBeamChargeProxy()); + System.out.println(seq.getInterval(timestamp).getBeamChargeGated()); + reader.close(); } From f0e9f7f552c1fed6649725f189d52d83c3396adf Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Tue, 5 Aug 2025 13:50:35 -0400 Subject: [PATCH 02/29] feat: test wrapper --- bin/testDaqScalersSequence | 10 ++++++++++ .../jlab/detector/scalers/DaqScalersSequence.java | 12 +++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) create mode 100755 bin/testDaqScalersSequence diff --git a/bin/testDaqScalersSequence b/bin/testDaqScalersSequence new file mode 100755 index 0000000000..687fbfffd1 --- /dev/null +++ b/bin/testDaqScalersSequence @@ -0,0 +1,10 @@ +#!/bin/bash + +. `dirname $0`/../libexec/env.sh + +export MALLOC_ARENA_MAX=1 + +java -Xmx768m -Xms768m -XX:+UseSerialGC \ + -cp ${COATJAVA_CLASSPATH:-''} \ + org.jlab.detector.scalers.DaqScalersSequence \ + $* diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index cc71cf4ccd..9538f7fcfe 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -381,7 +381,7 @@ public static void main(String[] args) { else { good++; // do something useful with beam charge here: - System.out.println(timestamp+" "+ds.dsc2.getBeamCharge()+" "+ds.dsc2.getBeamChargeGated()); + // System.out.println(timestamp+" "+ds.dsc2.getBeamCharge()+" "+ds.dsc2.getBeamChargeGated()); } } @@ -389,12 +389,14 @@ public static void main(String[] args) { +bad+" "+good+" "+100*((float)bad)/(bad+good)+"%"); // QADB usage - long timestamp = 1000; seq.downsample(2000); + System.out.println("SUBSEQUENCES:"); + int i_seq = 0; for(DaqScalersSequence subseq : seq.getSubsequences()) - System.out.println(subseq.getBeamChargeProxy()); - System.out.println(seq.getSubsequence(timestamp).getBeamChargeProxy()); - System.out.println(seq.getInterval(timestamp).getBeamChargeGated()); + System.out.printf("%d %f\n", i_seq++, subseq.getBeamChargeProxy()); + // long timestamp = 1000; + // System.out.println(seq.getSubsequence(timestamp).getBeamChargeProxy()); + // System.out.println(seq.getInterval(timestamp).getBeamChargeGated()); reader.close(); From bfa5202e2a14f066174a68c1b51f339cd4b8f19a Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Tue, 5 Aug 2025 15:10:34 -0400 Subject: [PATCH 03/29] feat: write charge comparison table --- .../detector/scalers/DaqScalersSequence.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index 9538f7fcfe..7097940cbc 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -307,19 +307,24 @@ public static DaqScalersSequence rebuildSequence(int tags, ConstantsManager conm * @param n keep every nth scaler readout, including the zeroth and the last. `n` should be `>1`. */ public void downsample(int n) { + System.out.println("DEBUG: original size = " + this.scalers.size()); // FIXME: remove if (this.scalers.isEmpty() || n<=1) return; subsequences.clear(); List keep = new ArrayList<>(); keep.add(0); for (int i=0; i=0; i--) + System.out.println("DEBUG: keep indices = " + keep); // FIXME: remove + for (int i=this.scalers.size()-1; i>=0; i--) { if (!keep.contains(i)) this.scalers.remove(i); + } + System.out.println("DEBUG: sampled size = " + this.scalers.size()); // FIXME: remove + System.out.println("DEBUG: num subsequences = " + this.subsequences.size()); // FIXME: remove } public double getBeamChargeProxy() { @@ -389,11 +394,17 @@ public static void main(String[] args) { +bad+" "+good+" "+100*((float)bad)/(bad+good)+"%"); // QADB usage - seq.downsample(2000); + seq.downsample(200); // nominally 2000 System.out.println("SUBSEQUENCES:"); int i_seq = 0; - for(DaqScalersSequence subseq : seq.getSubsequences()) - System.out.printf("%d %f\n", i_seq++, subseq.getBeamChargeProxy()); + System.out.printf("%20s %20s %20s\n", "bin", "q_gated", "q_corrected"); + for(DaqScalersSequence subseq : seq.getSubsequences()) { + System.out.printf("%20d %20.5f %20.5f\n", + i_seq++, + subseq.getInterval().getBeamChargeGated(), + subseq.getBeamChargeProxy() + ); + } // long timestamp = 1000; // System.out.println(seq.getSubsequence(timestamp).getBeamChargeProxy()); // System.out.println(seq.getInterval(timestamp).getBeamChargeGated()); From 14c87eddbe59241b92176636120274d5f74c088a Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Tue, 5 Aug 2025 16:19:53 -0400 Subject: [PATCH 04/29] feat: initial correction implementation --- .../detector/scalers/DaqScalersSequence.java | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index 7097940cbc..4c6c156931 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -38,6 +38,10 @@ public class DaqScalersSequence implements Comparator { public static class Interval { private DaqScalers previous = null; private DaqScalers next = null; + public Interval(DaqScalers previous, DaqScalers next) { + this.previous = previous; + this.next = next; + } public Interval(DaqScalersSequence seq) { if (!seq.scalers.isEmpty()) { this.previous = seq.scalers.get(0); @@ -75,6 +79,12 @@ public double getBeamCurrent() { } return 0; } + public double getLivetime() { + if (next!=null) + return this.next.dsc2.getLivetime(); + return 0; + } + } @Override @@ -327,14 +337,23 @@ public void downsample(int n) { System.out.println("DEBUG: num subsequences = " + this.subsequences.size()); // FIXME: remove } - public double getBeamChargeProxy() { - return 0; // FIXME - } - - public DaqScalersSequence getSubsequence(long timestamp) { - return this.subsequences.get(this.findIndex(timestamp)); // FIXME: catch exception + /** + * @return DAQ-gated charge from the livetime-weighted sum of the ungated charge + */ + public double getBeamChargeLivetimeWeighted() { + double result = 0; + for(int i=1; i getSubsequences() { return subsequences; } @@ -402,12 +421,9 @@ public static void main(String[] args) { System.out.printf("%20d %20.5f %20.5f\n", i_seq++, subseq.getInterval().getBeamChargeGated(), - subseq.getBeamChargeProxy() + subseq.getBeamChargeLivetimeWeighted() ); } - // long timestamp = 1000; - // System.out.println(seq.getSubsequence(timestamp).getBeamChargeProxy()); - // System.out.println(seq.getInterval(timestamp).getBeamChargeGated()); reader.close(); From fdfdeff56d1cc35dc854f9678a1c4df0cc28d6f8 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Wed, 6 Aug 2025 11:15:05 -0400 Subject: [PATCH 05/29] fix: need protected default constructor for inheritance --- .../main/java/org/jlab/detector/scalers/DaqScalersSequence.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index 4c6c156931..6a1d1b48f7 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -33,7 +33,7 @@ public class DaqScalersSequence implements Comparator { static final Logger logger = Logger.getLogger(DaqScalersSequence.class.getName()); - private DaqScalersSequence(){}; + protected DaqScalersSequence(){}; public static class Interval { private DaqScalers previous = null; From 780eb7a1a5598548f2918b9f607c1636cfe6452d Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Wed, 6 Aug 2025 17:58:31 -0400 Subject: [PATCH 06/29] refactor: separate `readSequence` event loop to a protected method so that a derived class can use it --- .../detector/scalers/DaqScalersSequence.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index 6a1d1b48f7..e92228ae16 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -235,18 +235,25 @@ public Interval getInterval(Event event1, Event event2) { * @return sequence */ public static DaqScalersSequence readSequence(List filenames) { - logger.info("DaqScalersSequence:: Reading scaler sequence from "+String.join(",", filenames)); - DaqScalersSequence seq=new DaqScalersSequence(); + seq.readFiles(filenames); + return seq; + } + /** + * @param filenames list of names of HIPO files to read + */ + protected void readFiles(List filenames) { + logger.info("DaqScalersSequence:: Reading scaler sequence from "+String.join(",", filenames)); + for (String filename : filenames) { HipoReader reader = new HipoReader(); reader.setTags(1); reader.open(filename); - if (seq.runConfigBank==null) { - seq.runConfigBank = new Bank(reader.getSchemaFactory().getSchema("RUN::config")); + if (this.runConfigBank==null) { + this.runConfigBank = new Bank(reader.getSchemaFactory().getSchema("RUN::config")); } SchemaFactory schema = reader.getSchemaFactory(); @@ -269,13 +276,11 @@ public static DaqScalersSequence readSequence(List filenames) { DaqScalers ds=DaqScalers.create(scalerBank); ds.setTimestamp(timestamp); - seq.add(ds); + this.add(ds); } reader.close(); } - - return seq; } /** From d99544e7fc705414cbf0583d401ccfa2f296defc Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Wed, 6 Aug 2025 18:03:15 -0400 Subject: [PATCH 07/29] fix: cleanup, since downsampling moved downstream --- bin/testDaqScalersSequence | 10 ---- .../detector/scalers/DaqScalersSequence.java | 51 +------------------ 2 files changed, 1 insertion(+), 60 deletions(-) delete mode 100755 bin/testDaqScalersSequence diff --git a/bin/testDaqScalersSequence b/bin/testDaqScalersSequence deleted file mode 100755 index 687fbfffd1..0000000000 --- a/bin/testDaqScalersSequence +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -. `dirname $0`/../libexec/env.sh - -export MALLOC_ARENA_MAX=1 - -java -Xmx768m -Xms768m -XX:+UseSerialGC \ - -cp ${COATJAVA_CLASSPATH:-''} \ - org.jlab.detector.scalers.DaqScalersSequence \ - $* diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index e92228ae16..8ef9f71ec4 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -26,7 +26,6 @@ public class DaqScalersSequence implements Comparator { public static final double TI_CLOCK_FREQ = 250e6; // Hz protected final List scalers=new ArrayList<>(); - protected final List subsequences=new ArrayList<>(); private Bank runConfigBank=null; private Bank runScalerBank=null; @@ -119,7 +118,6 @@ public DaqScalersSequence(List inputScalers) { public void clear() { scalers.clear(); - subsequences.clear(); } protected boolean add(DaqScalers ds) { @@ -315,33 +313,6 @@ public static DaqScalersSequence rebuildSequence(int tags, ConstantsManager conm return seq; } - /** - * Sample this sequence, keeping every nth scaler readout as a subsequence of samples. - * Methods such as `getInterval` will only consider scaler readouts in this sample subsequence. - * The n _original_ scaler readouts between each sample are preserved as subsequences. - * @param n keep every nth scaler readout, including the zeroth and the last. `n` should be `>1`. - */ - public void downsample(int n) { - System.out.println("DEBUG: original size = " + this.scalers.size()); // FIXME: remove - if (this.scalers.isEmpty() || n<=1) - return; - subsequences.clear(); - List keep = new ArrayList<>(); - keep.add(0); - for (int i=0; i Date: Thu, 7 Aug 2025 18:14:27 -0400 Subject: [PATCH 08/29] feat: store event number --- .../src/main/java/org/jlab/detector/scalers/DaqScalers.java | 3 +++ .../java/org/jlab/detector/scalers/DaqScalersSequence.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalers.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalers.java index 67d60a8584..ac439764a3 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalers.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalers.java @@ -50,11 +50,14 @@ public class DaqScalers { public StruckScalers struck=null; private long timestamp=0; + private int evnum=0; public DaqScalers setTimestamp(long timestamp) { this.timestamp=timestamp; return this; } public long getTimestamp(){ return this.timestamp; } + public void setEventNum(int evnum) { this.evnum=evnum; } + public int getEventNum() { return this.evnum; } /** * Get seconds between two dates assuming the differ by not more than 24 hours. diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index 8ef9f71ec4..3fb1462f50 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -149,11 +149,14 @@ public boolean add(Event event){ event.read(runConfigBank); if (runScalerBank.getRows() > 0) { long timestamp=0; + int evnum=0; if (runConfigBank.getRows()>0) { timestamp=runConfigBank.getLong("timestamp",0); + evnum=runConfigBank.getInt("event",0); } DaqScalers ds=DaqScalers.create(runScalerBank); ds.setTimestamp(timestamp); + ds.setEventNum(evnum); return add(ds); } return false; From b1eb7d079938491d237c90083cb0390fd3cf7b37 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Wed, 27 Aug 2025 16:52:02 -0400 Subject: [PATCH 09/29] feat: QADB binning classes --- .../java/org/jlab/detector/qadb/QadbBin.java | 91 +++++++++++++++++++ .../jlab/detector/qadb/QadbBinSequence.java | 63 +++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java create mode 100644 common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java new file mode 100644 index 0000000000..b32fd58f93 --- /dev/null +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -0,0 +1,91 @@ +package org.jlab.detector.qadb; + +import java.util.List; +import org.jlab.detector.scalers.DaqScalers; +import org.jlab.detector.scalers.DaqScalersSequence; + +/** + * A single bin for the Quality Assurance Database (QADB). + * It may hold arbitrary data, such as a class instance, accessible by {@link getData} and {@link setData}. + * A bin contains a (sub)sequence of scaler readouts, and therefore extends {@link DaqScalersSequence}. + * @see QadbBinSequence + * @author dilks + */ +public class QadbBin extends DaqScalersSequence { + + private int binNum; + private int evnumMin; + private int evnumMax; + private long timestampMin; + private long timestampMax; + + private T binData; + + /** + * construct a single bin + * @param binNum the bin number, in the {@link QaBinSequence} which contains this bin + * @param inputScalers the scaler sequence for this bin + */ + public QadbBin(int binNum, List inputScalers) { + super(inputScalers); + this.binNum = binNum; + this.timestampMin = this.scalers.get(0).getTimestamp(); + this.timestampMax = this.scalers.get(scalers.size()-1).getTimestamp(); + this.evnumMin = this.scalers.get(0).getEventNum(); + this.evnumMax = this.scalers.get(scalers.size()-1).getEventNum(); + } + + /** @param binData the data to be associated with this bin */ + public void setData(T binData) { + this.binData = binData; + } + + /** @return the data associated with this bin */ + public T getData() { + return binData; + } + + /** + * print a QA bin + * @param printNames if {@code true}, print the variable names too + */ + public void print(boolean printNames) { + if(printNames) + System.out.printf("%15s %15s %15s\n", + "bin", + "q_gated", + "q_corrected" + ); + System.out.printf("%15d %15.5f %15.5f\n", + this.binNum, + this.getInterval().getBeamChargeGated(), + this.getBeamChargeLivetimeWeighted() + ); + } + + /** print a QA bin; include header if bin 0 */ + public void print() { + this.print(this.binNum==0); + } + + /** @return minimum timestamp for this bin */ + public long getTimestampMin() { return this.timestampMin; } + + /** @return maximum timestamp for this bin */ + public long getTimestampMax() { return this.timestampMax; } + + /** @return minimum event number for this bin */ + public long getEventNumMin() { return this.evnumMin; } + + /** @return maximum event number for this bin */ + public long getEventNumMax() { return this.evnumMax; } + + /** + * @param timestamp the timestamp + * @return {@code true} if the bin contains this timestamp + */ + public boolean containsTimestamp(long timestamp) { + return timestamp >= this.timestampMin && timestamp <= this.timestampMax; + } + +} diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java new file mode 100644 index 0000000000..7c9272071e --- /dev/null +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -0,0 +1,63 @@ +package org.jlab.detector.qadb; + +import java.util.List; +import java.util.ArrayList; +import java.util.Optional; + +import org.jlab.detector.scalers.DaqScalersSequence; + +/** + * A sequence of bins for the Quality Assurance Database (QADB). + * The bins may hold generic data, such as a class instance, accessible by {@link QadbBin#getData} and {@link QadbBin#setData}. + * @see QadbBin + * @author dilks + */ +public class QadbBinSequence extends DaqScalersSequence { + + /** sequence of QA bins */ + private final List> qaBins = new ArrayList<>(); + + /** + * @param filenames list of HIPO files to read + * @param binWidth the number of scaler readouts in each bin + */ + public QadbBinSequence(List filenames, int binWidth) { + if(binWidth <= 0) + throw new RuntimeException("binWidth must be greater than 0"); + this.readFiles(filenames); + System.out.println("DEBUG: original size = " + this.scalers.size()); // FIXME: remove + if(this.scalers.isEmpty()) + throw new RuntimeException("scalers is empty"); + List keep = new ArrayList<>(); + keep.add(0); + for(int i=0; i(this.qaBins.size(), this.scalers.subList(i, end))); + keep.add(end); + } + System.out.println("DEBUG: keep indices = " + keep); // FIXME: remove + for (int i=this.scalers.size()-1; i>=0; i--) { + if (!keep.contains(i)) + this.scalers.remove(i); + } + System.out.println("DEBUG: sampled size = " + this.scalers.size()); // FIXME: remove + System.out.println("DEBUG: num qaBins = " + this.qaBins.size()); // FIXME: remove + } + + /** print the QA bins */ + public void print() { + System.out.println("QA BINS:"); + for(var qaBin : this.qaBins) + qaBin.print(); + } + + /** + * @return the bin which contains the timestamp + * @param timestamp the timestamp + */ + public Optional> find(long timestamp) { + var idx = this.findIndex(timestamp); + return Optional.ofNullable(this.qaBins.get(idx)); + } + +} From 2f9e177404ee89b2d1cd5a7ea44d35a52b391c00 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Wed, 27 Aug 2025 18:49:55 -0400 Subject: [PATCH 10/29] feat: some example code --- bin/test-QadbBinSequence | 8 ++ .../java/org/jlab/detector/qadb/QadbBin.java | 59 +++++----- .../jlab/detector/qadb/QadbBinSequence.java | 108 +++++++++++++++--- 3 files changed, 129 insertions(+), 46 deletions(-) create mode 100755 bin/test-QadbBinSequence diff --git a/bin/test-QadbBinSequence b/bin/test-QadbBinSequence new file mode 100755 index 0000000000..ae2685c687 --- /dev/null +++ b/bin/test-QadbBinSequence @@ -0,0 +1,8 @@ +#!/bin/bash + +. `dirname $0`/../libexec/env.sh + +java -Xms1024m \ + -cp ${COATJAVA_CLASSPATH:-''} \ + org.jlab.detector.qadb.QadbBinSequence \ + $* diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index b32fd58f93..b50f108d3f 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -6,66 +6,59 @@ /** * A single bin for the Quality Assurance Database (QADB). - * It may hold arbitrary data, such as a class instance, accessible by {@link getData} and {@link setData}. + * It may hold arbitrary data, such as a class instance, accessible by public member {@link QadbBin#data}. * A bin contains a (sub)sequence of scaler readouts, and therefore extends {@link DaqScalersSequence}. * @see QadbBinSequence * @author dilks */ public class QadbBin extends DaqScalersSequence { + /** lambda type to print each bin's generic data as a string */ + public interface DataPrinter { + String run(T data); + } + private int binNum; private int evnumMin; private int evnumMax; private long timestampMin; private long timestampMax; - private T binData; + /** arbitrary data that may be held by this bin; it is just public so the user can do anything with it */ + public T data; /** * construct a single bin - * @param binNum the bin number, in the {@link QaBinSequence} which contains this bin + * @param binNum the bin number, in the {@link QadbBinSequence} which contains this bin * @param inputScalers the scaler sequence for this bin + * @param initData the initial data for this bin */ - public QadbBin(int binNum, List inputScalers) { + public QadbBin(int binNum, List inputScalers, T initData) { super(inputScalers); this.binNum = binNum; + this.data = initData; this.timestampMin = this.scalers.get(0).getTimestamp(); this.timestampMax = this.scalers.get(scalers.size()-1).getTimestamp(); this.evnumMin = this.scalers.get(0).getEventNum(); this.evnumMax = this.scalers.get(scalers.size()-1).getEventNum(); } - /** @param binData the data to be associated with this bin */ - public void setData(T binData) { - this.binData = binData; - } - - /** @return the data associated with this bin */ - public T getData() { - return binData; - } - /** - * print a QA bin - * @param printNames if {@code true}, print the variable names too + * print a QA bin and its data + * @param verbose if {@code true}, print more + * @param dataPrinter a lambda which resolves {@link data} as a {@code String} */ - public void print(boolean printNames) { - if(printNames) - System.out.printf("%15s %15s %15s\n", - "bin", - "q_gated", - "q_corrected" - ); - System.out.printf("%15d %15.5f %15.5f\n", - this.binNum, - this.getInterval().getBeamChargeGated(), - this.getBeamChargeLivetimeWeighted() - ); - } - - /** print a QA bin; include header if bin 0 */ - public void print() { - this.print(this.binNum==0); + public void print(boolean verbose, DataPrinter dataPrinter) { + System.out.printf("BIN %d", this.binNum); + if(verbose) { + System.out.printf("\n"); + System.out.printf("event number range: %d to %d\n", this.evnumMin, this.evnumMax); + System.out.printf("timestamp range: %d to %d\n", this.timestampMin, this.timestampMax); + // FIXME: add charges etc. + } else { + System.out.printf(" :: "); + } + System.out.println(dataPrinter.run(this.data)); } /** @return minimum timestamp for this bin */ diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index 7c9272071e..0a9e8237b0 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -1,54 +1,89 @@ package org.jlab.detector.qadb; import java.util.List; +import java.util.Arrays; import java.util.ArrayList; import java.util.Optional; import org.jlab.detector.scalers.DaqScalersSequence; +import org.jlab.jnp.hipo4.io.HipoReader; +import org.jlab.jnp.hipo4.data.Event; +import org.jlab.jnp.hipo4.data.Bank; +import org.jlab.jnp.hipo4.data.SchemaFactory; + /** * A sequence of bins for the Quality Assurance Database (QADB). - * The bins may hold generic data, such as a class instance, accessible by {@link QadbBin#getData} and {@link QadbBin#setData}. + * The bins may hold generic data, such as a class instance, accessible by {@link QadbBin#data}. * @see QadbBin * @author dilks */ public class QadbBinSequence extends DaqScalersSequence { + /** lambda type to initialize each bin's generic data */ + public interface DataInitializer { + T run(int n); + } + /** sequence of QA bins */ private final List> qaBins = new ArrayList<>(); /** + * Read a list of HIPO files for a run and generate a sequence of QADB bins. + * The original sequence of scalers ({@link DaqScalersSequence}) is sampled: + *
    + *
  • bin boundaries are set such that each bin contains {@code binWidth} consecutive scaler readouts (excluding the first); the last bin may contain less
  • + *
  • {@link QadbBin} objects are defined between each pair of consecutive bin boundaries
  • + *
  • an initial (final) {@link QadbBin} object is also defined, for events which occur before (after) the first (last) scaler readout
  • + *
  • the {@code private} list of scalers becomes filled with ONLY the scaler readouts at the bin boundaries
  • + *
  • each bin's scaler subsequence is stored within its {@link QadbBin}
  • + *
* @param filenames list of HIPO files to read - * @param binWidth the number of scaler readouts in each bin + * @param binWidth the number of consecutive scaler-readout intervals in each bin + * @param initDataFunction a lambda to create the initial data for each bin; must be of the form {@code (binNumber) -> { return initData object }} */ - public QadbBinSequence(List filenames, int binWidth) { + public QadbBinSequence(List filenames, int binWidth, DataInitializer initDataFunction) { + // construct the full, sorted scaler sequence + this.readFiles(filenames); + // sanity checks if(binWidth <= 0) throw new RuntimeException("binWidth must be greater than 0"); - this.readFiles(filenames); System.out.println("DEBUG: original size = " + this.scalers.size()); // FIXME: remove if(this.scalers.isEmpty()) throw new RuntimeException("scalers is empty"); - List keep = new ArrayList<>(); - keep.add(0); + // add an initial, empty bin; its scaler sequence just contains the first scaler readout + this.qaBins.add(new QadbBin(0, this.scalers.subList(0, 1), initDataFunction.run(0))); + // sample the original scaler sequence: make a new `QadbBin` for each subsequence + List scalersToKeep = new ArrayList<>(); // list of `scalers` indices to keep, i.e., the ones at the bin boundaries + scalersToKeep.add(0); for(int i=0; i(this.qaBins.size(), this.scalers.subList(i, end))); - keep.add(end); + int binNum = this.qaBins.size(); + this.qaBins.add(new QadbBin(binNum, this.scalers.subList(i, end), initDataFunction.run(binNum))); + scalersToKeep.add(end); } - System.out.println("DEBUG: keep indices = " + keep); // FIXME: remove + System.out.println("DEBUG: scalersToKeep indices = " + scalersToKeep); // FIXME: remove + // add a final, empty bin; its scaler sequence just contains the last scaler readout + int binNum = this.qaBins.size(); + this.qaBins.add(new QadbBin(binNum, this.scalers.subList(this.scalers.size()-1, this.scalers.size()), initDataFunction.run(binNum))); + // remove all `scalers` elements which are not on bin boundaries for (int i=this.scalers.size()-1; i>=0; i--) { - if (!keep.contains(i)) + if (!scalersToKeep.contains(i)) this.scalers.remove(i); } System.out.println("DEBUG: sampled size = " + this.scalers.size()); // FIXME: remove System.out.println("DEBUG: num qaBins = " + this.qaBins.size()); // FIXME: remove } - /** print the QA bins */ - public void print() { + /** + * print the QA bins + * @param verbose if {@code true}, print more + * @param dataPrinter a lambda which resolves each bin's {@link QadbBin#data} as a {@code String} + */ + public void print(boolean verbose, QadbBin.DataPrinter dataPrinter) { System.out.println("QA BINS:"); for(var qaBin : this.qaBins) - qaBin.print(); + qaBin.print(verbose, dataPrinter); } /** @@ -60,4 +95,51 @@ public Optional> find(long timestamp) { return Optional.ofNullable(this.qaBins.get(idx)); } + /** + * Demonstrate how to use this class + * @param args command-line arguments + */ + public static void main(String[] args) { + + if(args.length == 0) + throw new RuntimeException("arguments must be HIPO file(s)"); + List filenames = new ArrayList<>(); + filenames.addAll(Arrays.asList(args)); + + // define a QADB bin sequence + // - as an example, we have each bin store an integer, which we will use to count the number of events in the bin + // - each bin's integer is initialized to zero; the lambda argument `n` represents the bin number, and is unused here + // - in practice, we can use any data type instead of an integer, such as a class full of histograms + QadbBinSequence seq = new QadbBinSequence<>(filenames, 2000, (n)->0); + + // read the list of HIPO files + for(String filename : filenames) { + HipoReader reader = new HipoReader(); + reader.setTags(0); + reader.open(filename); + SchemaFactory schema = reader.getSchemaFactory(); + + // event loop + while(reader.hasNext()) { + Bank configBank = new Bank(schema.getSchema("RUN::config")); + Event event=new Event(); + reader.nextEvent(event); + event.read(configBank); + + // increment each QADB bin's counter + if(configBank.getRows()>0) { + var thisBin = seq.find(configBank.getLong("timestamp",0)); + if(thisBin.isPresent()) + thisBin.get().data++; + } + } + + reader.close(); + } + + // print the results: the bin number along with its number of events + seq.print(false, (data)->Integer.toString(data)); + + } + } From 3cd9b27ed216f1c4ca77f798a48ade801d8b8739 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Thu, 28 Aug 2025 10:46:01 -0400 Subject: [PATCH 11/29] feat: general `coatjava` script --- bin/coatjava | 25 +++++++++++++++++++++++++ bin/test-QadbBinSequence | 8 -------- 2 files changed, 25 insertions(+), 8 deletions(-) create mode 100755 bin/coatjava delete mode 100755 bin/test-QadbBinSequence diff --git a/bin/coatjava b/bin/coatjava new file mode 100755 index 0000000000..7ead11b390 --- /dev/null +++ b/bin/coatjava @@ -0,0 +1,25 @@ +#!/bin/bash + +. `dirname $0`/../libexec/env.sh + +if [ $# -eq 0 ]; then + echo """Run the \`main\` method of any \`coatjava\` class + + USAGE: $(basename $0) [COATJAVA_CLASS] [CLASS_ARGS] -- [JVM_ARGS] + + COATJAVA_CLASS the full class name + CLASS_ARGS arguments for its \`main\` method + JVM_ARGS arguments for the JVM, overriding defaults + """ + exit 2 +fi + +class_name=$1 +shift + +split_cli $@ + +exec java -Xmx1536m -Xms1024m -XX:+UseSerialGC ${jvm_options[@]} \ + -cp ${COATJAVA_CLASSPATH:-''} \ + $class_name \ + ${class_options[@]} diff --git a/bin/test-QadbBinSequence b/bin/test-QadbBinSequence deleted file mode 100755 index ae2685c687..0000000000 --- a/bin/test-QadbBinSequence +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -. `dirname $0`/../libexec/env.sh - -java -Xms1024m \ - -cp ${COATJAVA_CLASSPATH:-''} \ - org.jlab.detector.qadb.QadbBinSequence \ - $* From 45e89f35cb519e37a3916254d208507c42e4b7c4 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Thu, 28 Aug 2025 12:13:00 -0400 Subject: [PATCH 12/29] fix: start convincing this stuff to work --- bin/coatjava | 5 +++ .../java/org/jlab/detector/qadb/QadbBin.java | 44 ++++++++++++++++--- .../jlab/detector/qadb/QadbBinSequence.java | 31 ++++++++----- 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/bin/coatjava b/bin/coatjava index 7ead11b390..50e2422293 100755 --- a/bin/coatjava +++ b/bin/coatjava @@ -10,6 +10,11 @@ if [ $# -eq 0 ]; then COATJAVA_CLASS the full class name CLASS_ARGS arguments for its \`main\` method JVM_ARGS arguments for the JVM, overriding defaults + + EXAMPLE: + $(basename $0) org.jlab.detector.qadb.QadbBinSequence \\ + skim_file.hipo -- \\ + -Djava.util.logging.config.file=common-tools/clas-logging/src/main/resources/org/jlab/logging/fine.properties """ exit 2 fi diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index b50f108d3f..8ebc111081 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -18,7 +18,18 @@ public interface DataPrinter { String run(T data); } + /** bin type */ + public enum BinType { + /** the first bin, for events before the first scaler readout */ + FIRST, + /** any bin between two scaler readouts */ + INTERMEDIATE, + /** the last bin, for events after the last scaler readout */ + LAST + } + private int binNum; + private BinType binType; private int evnumMin; private int evnumMax; private long timestampMin; @@ -33,14 +44,35 @@ public interface DataPrinter { * @param inputScalers the scaler sequence for this bin * @param initData the initial data for this bin */ - public QadbBin(int binNum, List inputScalers, T initData) { + public QadbBin(int binNum, BinType binType, List inputScalers, T initData) { super(inputScalers); this.binNum = binNum; + this.binType = binType; this.data = initData; - this.timestampMin = this.scalers.get(0).getTimestamp(); - this.timestampMax = this.scalers.get(scalers.size()-1).getTimestamp(); - this.evnumMin = this.scalers.get(0).getEventNum(); - this.evnumMax = this.scalers.get(scalers.size()-1).getEventNum(); + switch(this.binType) { + case INTERMEDIATE -> { + this.timestampMin = this.scalers.get(0).getTimestamp(); + this.timestampMax = this.scalers.get(scalers.size()-1).getTimestamp(); + this.evnumMin = this.scalers.get(0).getEventNum(); + this.evnumMax = this.scalers.get(scalers.size()-1).getEventNum(); + } + case FIRST -> { + if(this.scalers.size() != 1) + throw new RuntimeException("a FIRST bin may only have ONE scaler readout"); + this.timestampMin = 0; + this.timestampMax = this.scalers.get(0).getTimestamp(); + this.evnumMin = 0; + this.evnumMax = this.scalers.get(0).getEventNum(); + } + case LAST -> { + if(this.scalers.size() != 1) + throw new RuntimeException("a LAST bin may only have ONE scaler readout"); + this.timestampMin = this.scalers.get(0).getTimestamp(); + this.timestampMax = 10 * this.timestampMin; + this.evnumMin = this.scalers.get(0).getEventNum(); + this.evnumMax = 10 * this.evnumMin; + } + } } /** @@ -51,7 +83,7 @@ public QadbBin(int binNum, List inputScalers, T initData) { public void print(boolean verbose, DataPrinter dataPrinter) { System.out.printf("BIN %d", this.binNum); if(verbose) { - System.out.printf("\n"); + System.out.printf(" -----------\n"); System.out.printf("event number range: %d to %d\n", this.evnumMin, this.evnumMax); System.out.printf("timestamp range: %d to %d\n", this.timestampMin, this.timestampMax); // FIXME: add charges etc. diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index 0a9e8237b0..01503e6eb9 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.ArrayList; import java.util.Optional; +import java.util.logging.Logger; import org.jlab.detector.scalers.DaqScalersSequence; @@ -28,6 +29,9 @@ public interface DataInitializer { /** sequence of QA bins */ private final List> qaBins = new ArrayList<>(); + /** logger instance */ + private static final Logger logger = Logger.getLogger(QadbBinSequence.class.getName()); + /** * Read a list of HIPO files for a run and generate a sequence of QADB bins. * The original sequence of scalers ({@link DaqScalersSequence}) is sampled: @@ -44,35 +48,37 @@ public interface DataInitializer { */ public QadbBinSequence(List filenames, int binWidth, DataInitializer initDataFunction) { // construct the full, sorted scaler sequence + logger.info("QadbBinSequence:: constructing DAQ scalers sequence"); this.readFiles(filenames); + logger.fine("...done, now constructing QADB bin sequence..."); // sanity checks if(binWidth <= 0) throw new RuntimeException("binWidth must be greater than 0"); - System.out.println("DEBUG: original size = " + this.scalers.size()); // FIXME: remove + logger.fine(" initial sequence size = " + this.scalers.size()); if(this.scalers.isEmpty()) - throw new RuntimeException("scalers is empty"); + throw new RuntimeException("scalers sequence is empty"); // add an initial, empty bin; its scaler sequence just contains the first scaler readout - this.qaBins.add(new QadbBin(0, this.scalers.subList(0, 1), initDataFunction.run(0))); + this.qaBins.add(new QadbBin(0, QadbBin.BinType.FIRST, this.scalers.subList(0, 1), initDataFunction.run(0))); // sample the original scaler sequence: make a new `QadbBin` for each subsequence List scalersToKeep = new ArrayList<>(); // list of `scalers` indices to keep, i.e., the ones at the bin boundaries scalersToKeep.add(0); for(int i=0; i(binNum, this.scalers.subList(i, end), initDataFunction.run(binNum))); + this.qaBins.add(new QadbBin(binNum, QadbBin.BinType.INTERMEDIATE, this.scalers.subList(i, end), initDataFunction.run(binNum))); scalersToKeep.add(end); } - System.out.println("DEBUG: scalersToKeep indices = " + scalersToKeep); // FIXME: remove + logger.fine(" scalers to keep = " + scalersToKeep); // add a final, empty bin; its scaler sequence just contains the last scaler readout int binNum = this.qaBins.size(); - this.qaBins.add(new QadbBin(binNum, this.scalers.subList(this.scalers.size()-1, this.scalers.size()), initDataFunction.run(binNum))); + this.qaBins.add(new QadbBin(binNum, QadbBin.BinType.LAST, this.scalers.subList(this.scalers.size()-1, this.scalers.size()), initDataFunction.run(binNum))); // remove all `scalers` elements which are not on bin boundaries for (int i=this.scalers.size()-1; i>=0; i--) { if (!scalersToKeep.contains(i)) this.scalers.remove(i); } - System.out.println("DEBUG: sampled size = " + this.scalers.size()); // FIXME: remove - System.out.println("DEBUG: num qaBins = " + this.qaBins.size()); // FIXME: remove + logger.fine(" sampled sequence size = " + this.scalers.size()); + logger.fine(" number of QADB bins = " + this.qaBins.size()); } /** @@ -92,7 +98,8 @@ public void print(boolean verbose, QadbBin.DataPrinter dataPrinter) { */ public Optional> find(long timestamp) { var idx = this.findIndex(timestamp); - return Optional.ofNullable(this.qaBins.get(idx)); + logger.finest(" -> QadbBinSequence.find(" + timestamp + ") -> idx=" + idx); + return idx>=0 && idx seq = new QadbBinSequence<>(filenames, 2000, (n)->0); // read the list of HIPO files + logger.info("===== begin event loop ===="); for(String filename : filenames) { HipoReader reader = new HipoReader(); reader.setTags(0); @@ -128,7 +136,7 @@ public static void main(String[] args) { // increment each QADB bin's counter if(configBank.getRows()>0) { - var thisBin = seq.find(configBank.getLong("timestamp",0)); + var thisBin = seq.find(configBank.getLong("timestamp", 0)); if(thisBin.isPresent()) thisBin.get().data++; } @@ -136,9 +144,10 @@ public static void main(String[] args) { reader.close(); } + logger.info("===== end event loop ===="); // print the results: the bin number along with its number of events - seq.print(false, (data)->Integer.toString(data)); + seq.print(true, (data) -> "number of events: " + data); } From 853c17edcb5922da1305c2ab64b6d4981146f33c Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Thu, 28 Aug 2025 16:51:10 -0400 Subject: [PATCH 13/29] fix: fencepost error, and missing event number --- .../src/main/java/org/jlab/detector/qadb/QadbBin.java | 4 ++-- .../main/java/org/jlab/detector/qadb/QadbBinSequence.java | 7 ++++--- .../java/org/jlab/detector/scalers/DaqScalersSequence.java | 4 ++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index 8ebc111081..2ff6ce1e87 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -84,8 +84,8 @@ public void print(boolean verbose, DataPrinter dataPrinter) { System.out.printf("BIN %d", this.binNum); if(verbose) { System.out.printf(" -----------\n"); - System.out.printf("event number range: %d to %d\n", this.evnumMin, this.evnumMax); - System.out.printf("timestamp range: %d to %d\n", this.timestampMin, this.timestampMax); + System.out.printf("%30s %d to %d, range %d\n", "event number interval:", this.evnumMin, this.evnumMax, this.evnumMax - this.evnumMin); + System.out.printf("%30s %d to %d, range %d\n", "timestamp interval:", this.timestampMin, this.timestampMax, this.timestampMax - this.timestampMin); // FIXME: add charges etc. } else { System.out.printf(" :: "); diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index 01503e6eb9..1f7d4b77f3 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -97,8 +97,9 @@ public void print(boolean verbose, QadbBin.DataPrinter dataPrinter) { * @param timestamp the timestamp */ public Optional> find(long timestamp) { - var idx = this.findIndex(timestamp); - logger.finest(" -> QadbBinSequence.find(" + timestamp + ") -> idx=" + idx); + logger.finest(" -> QadbBinSequence.find(" + timestamp + ")"); + var idx = this.findIndex(timestamp) + 1; // add 1, to account for the `FIRST` bin + logger.finest(" -> found QADB bin at idx = " + idx); return idx>=0 && idx "number of events: " + data); + seq.print(true, (data) -> String.format("%30s %d", "number of events:", data)); } diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index 3fb1462f50..9fd422ae26 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -103,6 +103,7 @@ protected int findIndex(long timestamp) { ds.setTimestamp(timestamp); final int index=Collections.binarySearch(this.scalers,ds,new DaqScalersSequence()); final int n = index<0 ? -index-2 : index; + logger.finest(" -> DaqScalersSequence.findIndex(" + timestamp + ") -> index = " + index + " -> return " + n); return n; } @@ -269,14 +270,17 @@ protected void readFiles(List filenames) { event.read(configBank); long timestamp=0; + int evnum=0; if (scalerBank.getRows()<1) continue; if (configBank.getRows()>0) { timestamp=configBank.getLong("timestamp",0); + evnum=configBank.getInt("event",0); } DaqScalers ds=DaqScalers.create(scalerBank); ds.setTimestamp(timestamp); + ds.setEventNum(evnum); this.add(ds); } From a1339be57d0656932c965aa5b909935ef36da7fe Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Thu, 28 Aug 2025 19:29:42 -0400 Subject: [PATCH 14/29] feat: handle charge --- .../java/org/jlab/detector/qadb/QadbBin.java | 116 ++++++++++++++++-- .../jlab/detector/qadb/QadbBinSequence.java | 73 +++++++---- .../detector/scalers/DaqScalersSequence.java | 7 +- 3 files changed, 162 insertions(+), 34 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index 2ff6ce1e87..9179e32dd2 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -25,7 +25,15 @@ public enum BinType { /** any bin between two scaler readouts */ INTERMEDIATE, /** the last bin, for events after the last scaler readout */ - LAST + LAST, + } + + /** charge correction method */ + public enum ChargeCorrectionMethod { + /** interchange the DAQ-gated and ungated charges */ + BY_FLIP, + /** calculate the DAQ-gated charge as the mean livetime multiplied by the ungated charge */ + BY_MEAN_LIVETIME, } private int binNum; @@ -34,6 +42,8 @@ public enum BinType { private int evnumMax; private long timestampMin; private long timestampMax; + private double charge; // ungated + private double chargeGated; /** arbitrary data that may be held by this bin; it is just public so the user can do anything with it */ public T data; @@ -46,33 +56,99 @@ public enum BinType { */ public QadbBin(int binNum, BinType binType, List inputScalers, T initData) { super(inputScalers); - this.binNum = binNum; - this.binType = binType; - this.data = initData; + this.binNum = binNum; + this.binType = binType; + this.data = initData; switch(this.binType) { case INTERMEDIATE -> { this.timestampMin = this.scalers.get(0).getTimestamp(); this.timestampMax = this.scalers.get(scalers.size()-1).getTimestamp(); this.evnumMin = this.scalers.get(0).getEventNum(); this.evnumMax = this.scalers.get(scalers.size()-1).getEventNum(); + this.charge = this.getInterval().getBeamCharge(); + this.chargeGated = this.getInterval().getBeamChargeGated(); } case FIRST -> { if(this.scalers.size() != 1) throw new RuntimeException("a FIRST bin may only have ONE scaler readout"); - this.timestampMin = 0; + this.timestampMin = 0; // user may correct this using `correctLowerBound` this.timestampMax = this.scalers.get(0).getTimestamp(); - this.evnumMin = 0; + this.evnumMin = 0; // user may correct this using `correctLowerBound` this.evnumMax = this.scalers.get(0).getEventNum(); + this.charge = 0; // since no lower bound + this.chargeGated = 0; // since no lower bound } case LAST -> { if(this.scalers.size() != 1) throw new RuntimeException("a LAST bin may only have ONE scaler readout"); this.timestampMin = this.scalers.get(0).getTimestamp(); - this.timestampMax = 10 * this.timestampMin; + this.timestampMax = 10 * this.timestampMin; // user may correct this using `correctUpperBound` this.evnumMin = this.scalers.get(0).getEventNum(); - this.evnumMax = 10 * this.evnumMin; + this.evnumMax = 10 * this.evnumMin; // user may correct this using `correctUpperBound` + this.charge = 0; // since no upper bound + this.chargeGated = 0; // since no upper bound + } + } + } + + /** + * correct the beam charge for this bin + * @param method the correction method to use + * @see ChargeCorrectionMethod + */ + public void correctCharge(ChargeCorrectionMethod method) { + logger.fine("correcting beam charge for bin " + this.binNum + " using method " + method); + logger.fine(" before: gated = " + this.chargeGated); + logger.fine(" ungated = " + this.charge); + switch(method) { + case BY_FLIP -> { // interchange the gated and ungated charge + var tmp = this.charge; + this.charge = this.chargeGated; + this.chargeGated = tmp; + } + case BY_MEAN_LIVETIME -> { // gated = * ungated + double meanLivetime = 0; + int numLivetimeEvents = 0; + for(int i=1; i= 0) { // filter out livetime = -1 + meanLivetime += livetime; + numLivetimeEvents++; + } + } + meanLivetime = numLivetimeEvents > 0 ? meanLivetime / numLivetimeEvents : 0; + logger.fine(" mean livetime = " + meanLivetime); + this.chargeGated = meanLivetime * this.charge; } } + logger.fine(" after: gated = " + this.chargeGated); + logger.fine(" ungated = " + this.charge); + } + + /** + * correct the first bin's lower bound, if you know it from tag-0 events + * @param evnumMin the correct minimum event number + * @param timestampMin the correct minimum timestamp + */ + public void correctLowerBound(int evnumMin, long timestampMin) { + if(this.binType == BinType.FIRST) { + this.evnumMin = evnumMin; + this.timestampMin = timestampMin; + } + else logger.warning("not allowed to correct the lower bound of a bin with type " + this.binType); + } + + /** + * correct the last bin's upper bound, if you know it from tag-0 events + * @param evnumMax the correct maximum event number + * @param timestampMax the correct maximum timestamp + */ + public void correctUpperBound(int evnumMax, long timestampMax) { + if(this.binType == BinType.LAST) { + this.evnumMax = evnumMax; + this.timestampMax = timestampMax; + } + else logger.warning("not allowed to correct the upper bound of a bin with type " + this.binType); } /** @@ -81,18 +157,23 @@ public QadbBin(int binNum, BinType binType, List inputScalers, T ini * @param dataPrinter a lambda which resolves {@link data} as a {@code String} */ public void print(boolean verbose, DataPrinter dataPrinter) { - System.out.printf("BIN %d", this.binNum); + System.out.printf("BIN %d", this.getBinNum()); if(verbose) { System.out.printf(" -----------\n"); - System.out.printf("%30s %d to %d, range %d\n", "event number interval:", this.evnumMin, this.evnumMax, this.evnumMax - this.evnumMin); - System.out.printf("%30s %d to %d, range %d\n", "timestamp interval:", this.timestampMin, this.timestampMax, this.timestampMax - this.timestampMin); - // FIXME: add charges etc. + System.out.printf("%30s %d to %d\n", "timestamp interval:", this.getTimestampMin(), this.getTimestampMax()); + System.out.printf("%30s %d to %d\n", "event number interval:", this.getEventNumMin(), this.getEventNumMax()); + System.out.printf("%30s %f s\n", "duration:", this.getDuration()); + System.out.printf("%30s %d events\n", "event number range:", this.getEventNumMax() - this.getEventNumMin()); + System.out.printf("%30s %f / %f\n", "beam charge gated / ungated:", this.getBeamChargeGated(), this.getBeamCharge()); } else { System.out.printf(" :: "); } System.out.println(dataPrinter.run(this.data)); } + /** @return the bin number for this bin */ + public int getBinNum() { return this.binNum; } + /** @return minimum timestamp for this bin */ public long getTimestampMin() { return this.timestampMin; } @@ -105,6 +186,17 @@ public void print(boolean verbose, DataPrinter dataPrinter) { /** @return maximum event number for this bin */ public long getEventNumMax() { return this.evnumMax; } + /** @return the beam charge, not gated by DAQ, for this bin */ + public double getBeamCharge() { return this.charge; } + + /** @return the beam charge, gated by DAQ, for this bin */ + public double getBeamChargeGated() { return this.chargeGated; } + + /** @return the duration of the bin, in seconds */ + public double getDuration() { + return (this.getTimestampMax() - this.getTimestampMin()) * 4e-9; // convert timestamp units [4ns] -> [s] + } + /** * @param timestamp the timestamp * @return {@code true} if the bin contains this timestamp diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index 1f7d4b77f3..69f5b82735 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -4,7 +4,7 @@ import java.util.Arrays; import java.util.ArrayList; import java.util.Optional; -import java.util.logging.Logger; +import java.util.Iterator; import org.jlab.detector.scalers.DaqScalersSequence; @@ -19,7 +19,7 @@ * @see QadbBin * @author dilks */ -public class QadbBinSequence extends DaqScalersSequence { +public class QadbBinSequence extends DaqScalersSequence implements Iterable> { /** lambda type to initialize each bin's generic data */ public interface DataInitializer { @@ -29,9 +29,6 @@ public interface DataInitializer { /** sequence of QA bins */ private final List> qaBins = new ArrayList<>(); - /** logger instance */ - private static final Logger logger = Logger.getLogger(QadbBinSequence.class.getName()); - /** * Read a list of HIPO files for a run and generate a sequence of QADB bins. * The original sequence of scalers ({@link DaqScalersSequence}) is sampled: @@ -81,15 +78,24 @@ public QadbBinSequence(List filenames, int binWidth, DataInitializer logger.fine(" number of QADB bins = " + this.qaBins.size()); } + /** iterable interface implementation */ + @Override + public Iterator> iterator() { + return this.qaBins.iterator(); + } + + /** @return the number of bins in this sequence */ + @Override + public int size() { + return this.qaBins.size(); + } + /** - * print the QA bins - * @param verbose if {@code true}, print more - * @param dataPrinter a lambda which resolves each bin's {@link QadbBin#data} as a {@code String} + * @param idx bin index + * @return a bin for a given index */ - public void print(boolean verbose, QadbBin.DataPrinter dataPrinter) { - System.out.println("QA BINS:"); - for(var qaBin : this.qaBins) - qaBin.print(verbose, dataPrinter); + public QadbBin getBin(int idx) { + return this.qaBins.get(idx); } /** @@ -109,17 +115,28 @@ public Optional> find(long timestamp) { */ public static void main(String[] args) { + // parse arguments, which must be a list of HIPO files if(args.length == 0) throw new RuntimeException("arguments must be HIPO file(s)"); List filenames = new ArrayList<>(); filenames.addAll(Arrays.asList(args)); // define a QADB bin sequence - // - as an example, we have each bin store an integer, which we will use to count the number of events in the bin + // - as an example, we have each bin store an integer, which we will use to count the number of tag-0 events in the bin // - each bin's integer is initialized to zero; the lambda argument `n` represents the bin number, and is unused here // - in practice, we can use any data type instead of an integer, such as a class full of histograms QadbBinSequence seq = new QadbBinSequence<>(filenames, 2000, (n)->0); + // apply a charge correction method + // for(var bin : seq) + // bin.correctCharge(QadbBin.ChargeCorrectionMethod.BY_MEAN_LIVETIME); + + // initialize a minimum and maximum event number and timestamp for tag-0 events + int evnumMin = -1; + int evnumMax = -1; + long timestampMin = -1; + long timestampMax = -1; + // read the list of HIPO files logger.info("===== begin event loop ===="); for(String filename : filenames) { @@ -128,18 +145,26 @@ public static void main(String[] args) { reader.open(filename); SchemaFactory schema = reader.getSchemaFactory(); - // event loop + // tag-0 event loop while(reader.hasNext()) { Bank configBank = new Bank(schema.getSchema("RUN::config")); - Event event=new Event(); + Event event = new Event(); reader.nextEvent(event); event.read(configBank); - // increment each QADB bin's counter + // find the bin which contains this event if(configBank.getRows()>0) { - var thisBin = seq.find(configBank.getLong("timestamp", 0)); - if(thisBin.isPresent()) - thisBin.get().data++; + var timestamp = configBank.getLong("timestamp", 0); + var evnum = configBank.getInt("event", 0); + var thisBin = seq.find(timestamp); + if(thisBin.isPresent()) { + thisBin.get().data++; // increment the counter for tag-0 events + evnumMin = evnumMin == -1 ? evnum : Math.min(evnum, evnumMin); // set event number and timestamp extrema + evnumMax = evnumMax == -1 ? evnum : Math.max(evnum, evnumMax); + timestampMin = timestampMin == -1 ? timestamp : Math.min(timestamp, timestampMin); + timestampMax = timestampMax == -1 ? timestamp : Math.max(timestamp, timestampMax); + } + else logger.warning("WARNING: failed to find a bin containing timestamp " + timestamp); } } @@ -147,9 +172,15 @@ public static void main(String[] args) { } logger.info("===== end event loop ===="); - // print the results: the bin number along with its number of events - seq.print(true, (data) -> String.format("%30s %d", "number of events:", data)); + // correct the first and last bin with the tag-0 event number and timestamp extrema; + // this is done such that the event number and timestamp ranges are correct + seq.getBin(0).correctLowerBound(evnumMin, timestampMin); + seq.getBin(seq.size()-1).correctUpperBound(evnumMax, timestampMax); + // print the results: the bin number along with its number of events + System.out.println(">>> QA BINS <<<"); + for(var bin : seq) + bin.print(true, (data) -> String.format("%30s %d", "counted tag-0 events:", data)); } } diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index 9fd422ae26..823086008d 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -30,7 +30,7 @@ public class DaqScalersSequence implements Comparator { private Bank runConfigBank=null; private Bank runScalerBank=null; - static final Logger logger = Logger.getLogger(DaqScalersSequence.class.getName()); + protected static final Logger logger = Logger.getLogger(DaqScalersSequence.class.getName()); protected DaqScalersSequence(){}; @@ -92,6 +92,11 @@ public int compare(DaqScalers o1, DaqScalers o2) { if (o1.getTimestamp() > o2.getTimestamp()) return +1; return 0; } + + /** @return the number of scalers in this sequence */ + public int size() { + return scalers.size(); + } protected int findIndex(long timestamp) { if (this.scalers.isEmpty()) return -1; From c2afbd3b87902eee0313e715fa64c0eec4fa606b Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Fri, 29 Aug 2025 10:57:53 -0400 Subject: [PATCH 15/29] feat: get charge extrema --- .../java/org/jlab/detector/qadb/QadbBin.java | 113 ++++++++++++++---- .../jlab/detector/qadb/QadbBinSequence.java | 16 ++- 2 files changed, 102 insertions(+), 27 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index 9179e32dd2..8fe785a7b9 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -13,6 +13,20 @@ */ public class QadbBin extends DaqScalersSequence { + private int binNum; + private BinType binType; + private int evnumMin; + private int evnumMax; + private long timestampMin; + private long timestampMax; + private double charge; // ungated + private double chargeGated; + + /** arbitrary data that may be held by this bin; it is just public so the user can do anything with it */ + public T data; + + // ---------------------------------------------------------------------------------- + /** lambda type to print each bin's generic data as a string */ public interface DataPrinter { String run(T data); @@ -28,25 +42,15 @@ public enum BinType { LAST, } - /** charge correction method */ - public enum ChargeCorrectionMethod { - /** interchange the DAQ-gated and ungated charges */ - BY_FLIP, - /** calculate the DAQ-gated charge as the mean livetime multiplied by the ungated charge */ - BY_MEAN_LIVETIME, + /** charge type */ + public enum ChargeType { + /** full charge, DAQ-ungated */ + UNGATED, + /** DAQ-gated charge */ + GATED, } - private int binNum; - private BinType binType; - private int evnumMin; - private int evnumMax; - private long timestampMin; - private long timestampMax; - private double charge; // ungated - private double chargeGated; - - /** arbitrary data that may be held by this bin; it is just public so the user can do anything with it */ - public T data; + // ---------------------------------------------------------------------------------- /** * construct a single bin @@ -91,6 +95,16 @@ public QadbBin(int binNum, BinType binType, List inputScalers, T ini } } + // ---------------------------------------------------------------------------------- + + /** charge correction method */ + public enum ChargeCorrectionMethod { + /** interchange the DAQ-gated and ungated charges */ + BY_FLIP, + /** calculate the DAQ-gated charge as the mean livetime multiplied by the ungated charge */ + BY_MEAN_LIVETIME, + } + /** * correct the beam charge for this bin * @param method the correction method to use @@ -125,6 +139,8 @@ public void correctCharge(ChargeCorrectionMethod method) { logger.fine(" ungated = " + this.charge); } + // ---------------------------------------------------------------------------------- + /** * correct the first bin's lower bound, if you know it from tag-0 events * @param evnumMin the correct minimum event number @@ -151,6 +167,8 @@ public void correctUpperBound(int evnumMax, long timestampMax) { else logger.warning("not allowed to correct the upper bound of a bin with type " + this.binType); } + // ---------------------------------------------------------------------------------- + /** * print a QA bin and its data * @param verbose if {@code true}, print more @@ -171,6 +189,8 @@ public void print(boolean verbose, DataPrinter dataPrinter) { System.out.println(dataPrinter.run(this.data)); } + // ---------------------------------------------------------------------------------- + /** @return the bin number for this bin */ public int getBinNum() { return this.binNum; } @@ -186,23 +206,68 @@ public void print(boolean verbose, DataPrinter dataPrinter) { /** @return maximum event number for this bin */ public long getEventNumMax() { return this.evnumMax; } + /** @return the duration of the bin, in seconds */ + public double getDuration() { + return (this.getTimestampMax() - this.getTimestampMin()) * 4e-9; // convert timestamp units [4ns] -> [s] + } + /** @return the beam charge, not gated by DAQ, for this bin */ public double getBeamCharge() { return this.charge; } /** @return the beam charge, gated by DAQ, for this bin */ public double getBeamChargeGated() { return this.chargeGated; } - /** @return the duration of the bin, in seconds */ - public double getDuration() { - return (this.getTimestampMax() - this.getTimestampMin()) * 4e-9; // convert timestamp units [4ns] -> [s] + /** + * @return the beam charge, gated or ungated + * @param chargeType the type of charge + */ + public double getBeamCharge(ChargeType chargeType) { + return switch(chargeType) { + case UNGATED -> this.getBeamCharge(); + case GATED -> this.getBeamChargeGated(); + }; + } + + // ---------------------------------------------------------------------------------- + + /** extremum type, used with {@link getChargeExtrumum} */ + public enum ExtremumType { + /** from the first scaler readout */ + FIRST, + /** from the last scaler readout */ + LAST, + /** the maximum */ + MAX, + /** the minimum */ + MIN, } /** - * @param timestamp the timestamp - * @return {@code true} if the bin contains this timestamp + * Get the min/max or initial/final charge. + * NOTE: this is likely NOT corrected by {@link correctCharge} + * @param extremumType the type of extremum + * @param chargeType the type of charge + * @return the charge for the given extremum + */ + public double getChargeExtrumum(ExtremumType extremumType, ChargeType chargeType) { + return switch(extremumType) { + case FIRST -> getDsc2Charge(this.scalers.get(0), chargeType); + case LAST -> getDsc2Charge(this.scalers.get(this.scalers.size()-1), chargeType); + case MIN -> getDsc2Charge(this.scalers.stream().reduce((a,b) -> getDsc2Charge(a, chargeType) < getDsc2Charge(b, chargeType) ? a : b).get(), chargeType); + case MAX -> getDsc2Charge(this.scalers.stream().reduce((a,b) -> getDsc2Charge(a, chargeType) > getDsc2Charge(b, chargeType) ? a : b).get(), chargeType); + }; + } + + /** helper method for {@link getChargeExtrumum} + * @param ds the scaler readout + * @param chargeType the charge type + * @return the charge from this scaler object */ - public boolean containsTimestamp(long timestamp) { - return timestamp >= this.timestampMin && timestamp <= this.timestampMax; + private static double getDsc2Charge(DaqScalers ds, ChargeType chargeType) { + return switch(chargeType) { + case UNGATED -> ds.dsc2.getBeamCharge(); + case GATED -> ds.dsc2.getBeamChargeGated(); + }; } } diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index 69f5b82735..501e33ec0c 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -21,16 +21,20 @@ */ public class QadbBinSequence extends DaqScalersSequence implements Iterable> { + /** sequence of QA bins */ + private final List> qaBins = new ArrayList<>(); + + // ---------------------------------------------------------------------------------- + /** lambda type to initialize each bin's generic data */ public interface DataInitializer { T run(int n); } - /** sequence of QA bins */ - private final List> qaBins = new ArrayList<>(); + // ---------------------------------------------------------------------------------- /** - * Read a list of HIPO files for a run and generate a sequence of QADB bins. + * Constructor: read a list of HIPO files for a run and generate a sequence of QADB bins. * The original sequence of scalers ({@link DaqScalersSequence}) is sampled: *
    *
  • bin boundaries are set such that each bin contains {@code binWidth} consecutive scaler readouts (excluding the first); the last bin may contain less
  • @@ -78,6 +82,8 @@ public QadbBinSequence(List filenames, int binWidth, DataInitializer logger.fine(" number of QADB bins = " + this.qaBins.size()); } + // ---------------------------------------------------------------------------------- + /** iterable interface implementation */ @Override public Iterator> iterator() { @@ -98,6 +104,8 @@ public QadbBin getBin(int idx) { return this.qaBins.get(idx); } + // ---------------------------------------------------------------------------------- + /** * @return the bin which contains the timestamp * @param timestamp the timestamp @@ -109,6 +117,8 @@ public Optional> find(long timestamp) { return idx>=0 && idx Date: Fri, 29 Aug 2025 11:03:34 -0400 Subject: [PATCH 16/29] feat: support RG-D charge override --- .../java/org/jlab/detector/qadb/QadbBin.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index 8fe785a7b9..b806823cab 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -106,7 +106,7 @@ public enum ChargeCorrectionMethod { } /** - * correct the beam charge for this bin + * correct the beam charge for this bin, using a correction method * @param method the correction method to use * @see ChargeCorrectionMethod */ @@ -139,6 +139,21 @@ public void correctCharge(ChargeCorrectionMethod method) { logger.fine(" ungated = " + this.charge); } + /** + * correct the beam charge for this bin, using specific values from the caller + * @param charge the charge, not gated by the DAQ + * @param chargeGated the DAQ-gated charge + */ + public void correctCharge(double charge, double chargeGated) { + logger.fine("correcting beam charge for bin " + this.binNum + " using user-specified values"); + logger.fine(" before: gated = " + this.chargeGated); + logger.fine(" ungated = " + this.charge); + this.charge = charge; + this.chargeGated = chargeGated; + logger.fine(" after: gated = " + this.chargeGated); + logger.fine(" ungated = " + this.charge); + } + // ---------------------------------------------------------------------------------- /** From e5a017d780850ecaad1e42468e52a06862bf2058 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Fri, 29 Aug 2025 12:09:41 -0400 Subject: [PATCH 17/29] feat: sugar --- .../jlab/detector/qadb/QadbBinSequence.java | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index 501e33ec0c..cd3aa7f3b2 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -119,6 +119,26 @@ public Optional> find(long timestamp) { // ---------------------------------------------------------------------------------- + /** + * correct the first bin's lower bound, if you know it from tag-0 events + * @param evnumMin the correct minimum event number + * @param timestampMin the correct minimum timestamp + */ + public void correctLowerBound(int evnumMin, long timestampMin) { + this.getBin(0).correctLowerBound(evnumMin, timestampMin); + } + + /** + * correct the last bin's upper bound, if you know it from tag-0 events + * @param evnumMax the correct maximum event number + * @param timestampMax the correct maximum timestamp + */ + public void correctUpperBound(int evnumMax, long timestampMax) { + this.getBin(this.size()-1).correctUpperBound(evnumMax, timestampMax); + } + + // ---------------------------------------------------------------------------------- + /** * Demonstrate how to use this class * @param args command-line arguments @@ -133,9 +153,10 @@ public static void main(String[] args) { // define a QADB bin sequence // - as an example, we have each bin store an integer, which we will use to count the number of tag-0 events in the bin - // - each bin's integer is initialized to zero; the lambda argument `n` represents the bin number, and is unused here + // - each bin's integer is initialized to zero; the lambda argument `binNum` represents the bin number, and is unused here // - in practice, we can use any data type instead of an integer, such as a class full of histograms - QadbBinSequence seq = new QadbBinSequence<>(filenames, 2000, (n)->0); + // - the lambda argument `binNum` can be used, for example, as part of the histogram titles + QadbBinSequence seq = new QadbBinSequence<>(filenames, 2000, (binNum)->0); // apply a charge correction method // for(var bin : seq) @@ -184,8 +205,8 @@ public static void main(String[] args) { // correct the first and last bin with the tag-0 event number and timestamp extrema; // this is done such that the event number and timestamp ranges are correct - seq.getBin(0).correctLowerBound(evnumMin, timestampMin); - seq.getBin(seq.size()-1).correctUpperBound(evnumMax, timestampMax); + seq.correctLowerBound(evnumMin, timestampMin); + seq.correctUpperBound(evnumMax, timestampMax); // print the results: the bin number along with its number of events System.out.println(">>> QA BINS <<<"); From c93eaa2f588265187857663901a46d239ef1b9db Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Fri, 29 Aug 2025 17:20:43 -0400 Subject: [PATCH 18/29] refactor: organize methods --- .../java/org/jlab/detector/qadb/QadbBin.java | 149 +++++++++--------- 1 file changed, 78 insertions(+), 71 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index b806823cab..d5eb6fc9a6 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -97,6 +97,61 @@ public QadbBin(int binNum, BinType binType, List inputScalers, T ini // ---------------------------------------------------------------------------------- + /** @return the bin number for this bin */ + public int getBinNum() { return this.binNum; } + + /** @return minimum timestamp for this bin */ + public long getTimestampMin() { return this.timestampMin; } + + /** @return maximum timestamp for this bin */ + public long getTimestampMax() { return this.timestampMax; } + + /** @return minimum event number for this bin */ + public long getEventNumMin() { return this.evnumMin; } + + /** @return maximum event number for this bin */ + public long getEventNumMax() { return this.evnumMax; } + + /** @return the beam charge, not gated by DAQ, for this bin */ + public double getBeamCharge() { return this.charge; } + + /** @return the beam charge, gated by DAQ, for this bin */ + public double getBeamChargeGated() { return this.chargeGated; } + + /** + * @return the beam charge, gated or ungated + * @param chargeType the type of charge + */ + public double getBeamCharge(ChargeType chargeType) { + return switch(chargeType) { + case UNGATED -> this.getBeamCharge(); + case GATED -> this.getBeamChargeGated(); + }; + } + + // ---------------------------------------------------------------------------------- + + /** @return the mean livetime for this bin */ + public double getMeanLivetime() { + double sumLivetime = 0; + int numLivetimeEvents = 0; + for(int i=1; i= 0) { // filter out livetime = -1 + sumLivetime += livetime; + numLivetimeEvents++; + } + } + return numLivetimeEvents > 0 ? sumLivetime / numLivetimeEvents : 0; + } + + /** @return the duration of the bin, in seconds */ + public double getDuration() { + return (this.getTimestampMax() - this.getTimestampMin()) * 4e-9; // convert timestamp units [4ns] -> [s] + } + + // ---------------------------------------------------------------------------------- + /** charge correction method */ public enum ChargeCorrectionMethod { /** interchange the DAQ-gated and ungated charges */ @@ -121,16 +176,7 @@ public void correctCharge(ChargeCorrectionMethod method) { this.chargeGated = tmp; } case BY_MEAN_LIVETIME -> { // gated = * ungated - double meanLivetime = 0; - int numLivetimeEvents = 0; - for(int i=1; i= 0) { // filter out livetime = -1 - meanLivetime += livetime; - numLivetimeEvents++; - } - } - meanLivetime = numLivetimeEvents > 0 ? meanLivetime / numLivetimeEvents : 0; + var meanLivetime = this.getMeanLivetime(); logger.fine(" mean livetime = " + meanLivetime); this.chargeGated = meanLivetime * this.charge; } @@ -184,67 +230,6 @@ public void correctUpperBound(int evnumMax, long timestampMax) { // ---------------------------------------------------------------------------------- - /** - * print a QA bin and its data - * @param verbose if {@code true}, print more - * @param dataPrinter a lambda which resolves {@link data} as a {@code String} - */ - public void print(boolean verbose, DataPrinter dataPrinter) { - System.out.printf("BIN %d", this.getBinNum()); - if(verbose) { - System.out.printf(" -----------\n"); - System.out.printf("%30s %d to %d\n", "timestamp interval:", this.getTimestampMin(), this.getTimestampMax()); - System.out.printf("%30s %d to %d\n", "event number interval:", this.getEventNumMin(), this.getEventNumMax()); - System.out.printf("%30s %f s\n", "duration:", this.getDuration()); - System.out.printf("%30s %d events\n", "event number range:", this.getEventNumMax() - this.getEventNumMin()); - System.out.printf("%30s %f / %f\n", "beam charge gated / ungated:", this.getBeamChargeGated(), this.getBeamCharge()); - } else { - System.out.printf(" :: "); - } - System.out.println(dataPrinter.run(this.data)); - } - - // ---------------------------------------------------------------------------------- - - /** @return the bin number for this bin */ - public int getBinNum() { return this.binNum; } - - /** @return minimum timestamp for this bin */ - public long getTimestampMin() { return this.timestampMin; } - - /** @return maximum timestamp for this bin */ - public long getTimestampMax() { return this.timestampMax; } - - /** @return minimum event number for this bin */ - public long getEventNumMin() { return this.evnumMin; } - - /** @return maximum event number for this bin */ - public long getEventNumMax() { return this.evnumMax; } - - /** @return the duration of the bin, in seconds */ - public double getDuration() { - return (this.getTimestampMax() - this.getTimestampMin()) * 4e-9; // convert timestamp units [4ns] -> [s] - } - - /** @return the beam charge, not gated by DAQ, for this bin */ - public double getBeamCharge() { return this.charge; } - - /** @return the beam charge, gated by DAQ, for this bin */ - public double getBeamChargeGated() { return this.chargeGated; } - - /** - * @return the beam charge, gated or ungated - * @param chargeType the type of charge - */ - public double getBeamCharge(ChargeType chargeType) { - return switch(chargeType) { - case UNGATED -> this.getBeamCharge(); - case GATED -> this.getBeamChargeGated(); - }; - } - - // ---------------------------------------------------------------------------------- - /** extremum type, used with {@link getChargeExtrumum} */ public enum ExtremumType { /** from the first scaler readout */ @@ -285,4 +270,26 @@ private static double getDsc2Charge(DaqScalers ds, ChargeType chargeType) { }; } + // ---------------------------------------------------------------------------------- + + /** + * print a QA bin and its data + * @param verbose if {@code true}, print more + * @param dataPrinter a lambda which resolves {@link data} as a {@code String} + */ + public void print(boolean verbose, DataPrinter dataPrinter) { + System.out.printf("BIN %d", this.getBinNum()); + if(verbose) { + System.out.printf(" -----------\n"); + System.out.printf("%30s %d to %d\n", "timestamp interval:", this.getTimestampMin(), this.getTimestampMax()); + System.out.printf("%30s %d to %d\n", "event number interval:", this.getEventNumMin(), this.getEventNumMax()); + System.out.printf("%30s %f s\n", "duration:", this.getDuration()); + System.out.printf("%30s %d events\n", "event number range:", this.getEventNumMax() - this.getEventNumMin()); + System.out.printf("%30s %f / %f\n", "beam charge gated / ungated:", this.getBeamChargeGated(), this.getBeamCharge()); + } else { + System.out.printf(" :: "); + } + System.out.println(dataPrinter.run(this.data)); + } + } From 9551c00fcb4853b78792e82dd36decdce3d2ad28 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Fri, 29 Aug 2025 17:37:44 -0400 Subject: [PATCH 19/29] fix: spelling --- .../src/main/java/org/jlab/detector/qadb/QadbBin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index d5eb6fc9a6..53f9c7ba6c 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -230,7 +230,7 @@ public void correctUpperBound(int evnumMax, long timestampMax) { // ---------------------------------------------------------------------------------- - /** extremum type, used with {@link getChargeExtrumum} */ + /** extremum type, used with {@link getChargeExtremum} */ public enum ExtremumType { /** from the first scaler readout */ FIRST, @@ -249,7 +249,7 @@ public enum ExtremumType { * @param chargeType the type of charge * @return the charge for the given extremum */ - public double getChargeExtrumum(ExtremumType extremumType, ChargeType chargeType) { + public double getChargeExtremum(ExtremumType extremumType, ChargeType chargeType) { return switch(extremumType) { case FIRST -> getDsc2Charge(this.scalers.get(0), chargeType); case LAST -> getDsc2Charge(this.scalers.get(this.scalers.size()-1), chargeType); @@ -258,7 +258,7 @@ public double getChargeExtrumum(ExtremumType extremumType, ChargeType chargeType }; } - /** helper method for {@link getChargeExtrumum} + /** helper method for {@link getChargeExtremum} * @param ds the scaler readout * @param chargeType the charge type * @return the charge from this scaler object From 0a12c48b9d07494b8ad9d353968ee4f683c50ed4 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Fri, 29 Aug 2025 18:03:05 -0400 Subject: [PATCH 20/29] fix: +1 --- .../src/main/java/org/jlab/detector/qadb/QadbBinSequence.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index cd3aa7f3b2..172db1df95 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -66,7 +66,7 @@ public QadbBinSequence(List filenames, int binWidth, DataInitializer for(int i=0; i(binNum, QadbBin.BinType.INTERMEDIATE, this.scalers.subList(i, end), initDataFunction.run(binNum))); + this.qaBins.add(new QadbBin(binNum, QadbBin.BinType.INTERMEDIATE, this.scalers.subList(i, end+1), initDataFunction.run(binNum))); scalersToKeep.add(end); } logger.fine(" scalers to keep = " + scalersToKeep); From 22a9a2b032fd1810575073ff21e83f4e980d0060 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Fri, 29 Aug 2025 19:51:46 -0400 Subject: [PATCH 21/29] fix: if on boundary take earlier bin to be consistent with the current QADB convention --- .../java/org/jlab/detector/qadb/QadbBinSequence.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index 172db1df95..a3d34e4c20 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -110,9 +110,12 @@ public QadbBin getBin(int idx) { * @return the bin which contains the timestamp * @param timestamp the timestamp */ - public Optional> find(long timestamp) { - logger.finest(" -> QadbBinSequence.find(" + timestamp + ")"); - var idx = this.findIndex(timestamp) + 1; // add 1, to account for the `FIRST` bin + public Optional> findBin(long timestamp) { + logger.finest(" -> QadbBinSequence.findBin(" + timestamp + ")"); + var idx = this.findIndex(timestamp); + if(idx>=0 && idx found QADB bin at idx = " + idx); return idx>=0 && idx0) { var timestamp = configBank.getLong("timestamp", 0); var evnum = configBank.getInt("event", 0); - var thisBin = seq.find(timestamp); + var thisBin = seq.findBin(timestamp); if(thisBin.isPresent()) { thisBin.get().data++; // increment the counter for tag-0 events evnumMin = evnumMin == -1 ? evnum : Math.min(evnum, evnumMin); // set event number and timestamp extrema From f4a5b4a0177a8a54c6f7a3e5e03fd2c621801871 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Tue, 2 Sep 2025 18:21:45 -0400 Subject: [PATCH 22/29] fix: remove bin/coatjava --- bin/coatjava | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100755 bin/coatjava diff --git a/bin/coatjava b/bin/coatjava deleted file mode 100755 index 50e2422293..0000000000 --- a/bin/coatjava +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -. `dirname $0`/../libexec/env.sh - -if [ $# -eq 0 ]; then - echo """Run the \`main\` method of any \`coatjava\` class - - USAGE: $(basename $0) [COATJAVA_CLASS] [CLASS_ARGS] -- [JVM_ARGS] - - COATJAVA_CLASS the full class name - CLASS_ARGS arguments for its \`main\` method - JVM_ARGS arguments for the JVM, overriding defaults - - EXAMPLE: - $(basename $0) org.jlab.detector.qadb.QadbBinSequence \\ - skim_file.hipo -- \\ - -Djava.util.logging.config.file=common-tools/clas-logging/src/main/resources/org/jlab/logging/fine.properties - """ - exit 2 -fi - -class_name=$1 -shift - -split_cli $@ - -exec java -Xmx1536m -Xms1024m -XX:+UseSerialGC ${jvm_options[@]} \ - -cp ${COATJAVA_CLASSPATH:-''} \ - $class_name \ - ${class_options[@]} From 0516ee5a137827d2be4bf183d47975be53806702 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Tue, 2 Sep 2025 18:51:04 -0400 Subject: [PATCH 23/29] fix: cleanup diff --- .../detector/scalers/DaqScalersSequence.java | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index 823086008d..e72e5cf9af 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -26,7 +26,7 @@ public class DaqScalersSequence implements Comparator { public static final double TI_CLOCK_FREQ = 250e6; // Hz protected final List scalers=new ArrayList<>(); - + private Bank runConfigBank=null; private Bank runScalerBank=null; @@ -37,10 +37,6 @@ public class DaqScalersSequence implements Comparator { public static class Interval { private DaqScalers previous = null; private DaqScalers next = null; - public Interval(DaqScalers previous, DaqScalers next) { - this.previous = previous; - this.next = next; - } public Interval(DaqScalersSequence seq) { if (!seq.scalers.isEmpty()) { this.previous = seq.scalers.get(0); @@ -78,12 +74,6 @@ public double getBeamCurrent() { } return 0; } - public double getLivetime() { - if (next!=null) - return this.next.dsc2.getLivetime(); - return 0; - } - } @Override @@ -324,21 +314,7 @@ public static DaqScalersSequence rebuildSequence(int tags, ConstantsManager conm } return seq; } - - /** - * @return DAQ-gated charge from the livetime-weighted sum of the ungated charge - */ - public double getBeamChargeLivetimeWeighted() { - double result = 0; - for(int i=1; i Date: Tue, 2 Sep 2025 19:28:06 -0400 Subject: [PATCH 24/29] doc: cleanup --- .../java/org/jlab/detector/qadb/QadbBin.java | 16 ++++++++++++---- .../org/jlab/detector/qadb/QadbBinSequence.java | 14 ++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index 53f9c7ba6c..08c5ee54e3 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -6,7 +6,9 @@ /** * A single bin for the Quality Assurance Database (QADB). - * It may hold arbitrary data, such as a class instance, accessible by public member {@link QadbBin#data}. + * It may hold arbitrary data, such as a class instance, accessible by public member {@link QadbBin#data}; + * its type is set by a generic type parameter. + *

    * A bin contains a (sub)sequence of scaler readouts, and therefore extends {@link DaqScalersSequence}. * @see QadbBinSequence * @author dilks @@ -29,6 +31,10 @@ public class QadbBin extends DaqScalersSequence { /** lambda type to print each bin's generic data as a string */ public interface DataPrinter { + /** + * @param data the public member {@link QadbBin#data} + * @return a String representation of {@link QadbBin#data} + */ String run(T data); } @@ -55,8 +61,9 @@ public enum ChargeType { /** * construct a single bin * @param binNum the bin number, in the {@link QadbBinSequence} which contains this bin + * @param binType the bin type (see {@link BinType}) * @param inputScalers the scaler sequence for this bin - * @param initData the initial data for this bin + * @param initData the initial data for this bin (sets public member {@link data}) */ public QadbBin(int binNum, BinType binType, List inputScalers, T initData) { super(inputScalers); @@ -230,7 +237,7 @@ public void correctUpperBound(int evnumMax, long timestampMax) { // ---------------------------------------------------------------------------------- - /** extremum type, used with {@link getChargeExtremum} */ + /** extremum type, used with {@link QadbBin#getChargeExtremum} */ public enum ExtremumType { /** from the first scaler readout */ FIRST, @@ -244,7 +251,8 @@ public enum ExtremumType { /** * Get the min/max or initial/final charge. - * NOTE: this is likely NOT corrected by {@link correctCharge} + *

    + * WARNING: this is likely NOT corrected by {@link correctCharge} * @param extremumType the type of extremum * @param chargeType the type of charge * @return the charge for the given extremum diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index a3d34e4c20..29ae80a282 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -15,7 +15,9 @@ /** * A sequence of bins for the Quality Assurance Database (QADB). - * The bins may hold generic data, such as a class instance, accessible by {@link QadbBin#data}. + *

    + * The bins may hold generic data, such as a class instance, accessible by {@link QadbBin#data}; the data + * type is set by a generic type parameter, and all bins will hold the same type of data. * @see QadbBin * @author dilks */ @@ -28,17 +30,21 @@ public class QadbBinSequence extends DaqScalersSequence implements Iterable { + /** + * @param n the bin number + * @return the initial public member {@link QadbBin#data} for bin number {@code n} + */ T run(int n); } // ---------------------------------------------------------------------------------- /** - * Constructor: read a list of HIPO files for a run and generate a sequence of QADB bins. + * read a list of HIPO files for a run and generate a sequence of QADB bins. * The original sequence of scalers ({@link DaqScalersSequence}) is sampled: *

      *
    • bin boundaries are set such that each bin contains {@code binWidth} consecutive scaler readouts (excluding the first); the last bin may contain less
    • - *
    • {@link QadbBin} objects are defined between each pair of consecutive bin boundaries
    • + *
    • {@link QadbBin} objects are defined for each pair of consecutive bin boundaries
    • *
    • an initial (final) {@link QadbBin} object is also defined, for events which occur before (after) the first (last) scaler readout
    • *
    • the {@code private} list of scalers becomes filled with ONLY the scaler readouts at the bin boundaries
    • *
    • each bin's scaler subsequence is stored within its {@link QadbBin}
    • @@ -107,7 +113,7 @@ public QadbBin getBin(int idx) { // ---------------------------------------------------------------------------------- /** - * @return the bin which contains the timestamp + * @return the bin which contains the timestamp, if found * @param timestamp the timestamp */ public Optional> findBin(long timestamp) { From a2ef191dff07fbcd7b2eda2e04071caf2dd2d697 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Wed, 3 Sep 2025 12:26:18 -0400 Subject: [PATCH 25/29] feat: validate scaler ordering --- .../jlab/detector/qadb/QadbBinSequence.java | 3 ++ .../detector/scalers/DaqScalersSequence.java | 33 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index 29ae80a282..6ed1494a9e 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -57,6 +57,9 @@ public QadbBinSequence(List filenames, int binWidth, DataInitializer // construct the full, sorted scaler sequence logger.info("QadbBinSequence:: constructing DAQ scalers sequence"); this.readFiles(filenames); + logger.fine("...validating ordering..."); + if(!this.validateOrdering()) + logger.severe("ERROR: scaler readout ordering is NOT VALID!"); // continue anyway, since the user may still want to see the QADB results logger.fine("...done, now constructing QADB bin sequence..."); // sanity checks if(binWidth <= 0) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index e72e5cf9af..368dfc67e2 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -314,7 +314,38 @@ public static DaqScalersSequence rebuildSequence(int tags, ConstantsManager conm } return seq; } - + + /** + * Checks if the scalers list is sorted such that the scalers' timestamp and event number orderings are consistent and monotonically increasing. + *

      + * @param scalers the list of DaqScalers to validate + * @return {@code true} if timestamp and event number orderings are consistent + */ + public boolean validateOrdering() { + if (scalers.size() <= 1) return true; // trivial case + boolean result = true; + for (int i = 0; i < scalers.size() - 1; i++) { + var current = scalers.get(i); + var next = scalers.get(i + 1); + var timestampComparison = Long.compare(current.getTimestamp(), next.getTimestamp()); + var evnumComparison = Integer.compare(current.getEventNum(), next.getEventNum()); + if (timestampComparison == 0 || evnumComparison == 0) { + logger.warning("WARNING: found possible duplicate scaler readout: evnum=" + current.getEventNum() + " timestamp=" + current.getTimestamp() + " i=" + i); + logger.warning(" next readout has: evnum=" + next.getEventNum() + " timestamp=" + next.getTimestamp()); + result = false; + } + // if neither is equal, they must have the same sign: negative, i.e., increasing monotonically + if (timestampComparison != 0 && evnumComparison != 0) { + if (Integer.signum(timestampComparison) != -1 || Integer.signum(evnumComparison) != -1) { + logger.warning("WARNING: found non-monotonic scaler ordering: evnum=" + current.getEventNum() + " timestamp=" + current.getTimestamp() + " i=" + i); + logger.warning(" next readout has: evnum=" + next.getEventNum() + " timestamp=" + next.getTimestamp()); + result = false; + } + } + } + return result; + } + public static void main(String[] args) { final String dir="/Users/baltzell/data/CLAS12/rg-a/decoded/6b.2.0/"; From 0206be471a59d92efb94e784328760e6abbb1b4d Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Mon, 8 Sep 2025 16:32:00 -0400 Subject: [PATCH 26/29] fix: docstrings --- .../main/java/org/jlab/detector/scalers/DaqScalersSequence.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index 368dfc67e2..f04785b908 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -317,8 +317,6 @@ public static DaqScalersSequence rebuildSequence(int tags, ConstantsManager conm /** * Checks if the scalers list is sorted such that the scalers' timestamp and event number orderings are consistent and monotonically increasing. - *

      - * @param scalers the list of DaqScalers to validate * @return {@code true} if timestamp and event number orderings are consistent */ public boolean validateOrdering() { From b9873ee6d692d5148a48fb1bf290b6133dfa4f8b Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Mon, 8 Sep 2025 16:39:57 -0400 Subject: [PATCH 27/29] refactor: DeMorgan law --- .../jlab/detector/scalers/DaqScalersSequence.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java index f04785b908..87fcd61c49 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/scalers/DaqScalersSequence.java @@ -323,19 +323,19 @@ public boolean validateOrdering() { if (scalers.size() <= 1) return true; // trivial case boolean result = true; for (int i = 0; i < scalers.size() - 1; i++) { - var current = scalers.get(i); - var next = scalers.get(i + 1); - var timestampComparison = Long.compare(current.getTimestamp(), next.getTimestamp()); - var evnumComparison = Integer.compare(current.getEventNum(), next.getEventNum()); + var prev = scalers.get(i); + var next = scalers.get(i + 1); + var timestampComparison = Long.compare(prev.getTimestamp(), next.getTimestamp()); + var evnumComparison = Integer.compare(prev.getEventNum(), next.getEventNum()); if (timestampComparison == 0 || evnumComparison == 0) { - logger.warning("WARNING: found possible duplicate scaler readout: evnum=" + current.getEventNum() + " timestamp=" + current.getTimestamp() + " i=" + i); + logger.warning("WARNING: found possible duplicate scaler readout: evnum=" + prev.getEventNum() + " timestamp=" + prev.getTimestamp() + " i=" + i); logger.warning(" next readout has: evnum=" + next.getEventNum() + " timestamp=" + next.getTimestamp()); result = false; } // if neither is equal, they must have the same sign: negative, i.e., increasing monotonically - if (timestampComparison != 0 && evnumComparison != 0) { + else { if (Integer.signum(timestampComparison) != -1 || Integer.signum(evnumComparison) != -1) { - logger.warning("WARNING: found non-monotonic scaler ordering: evnum=" + current.getEventNum() + " timestamp=" + current.getTimestamp() + " i=" + i); + logger.warning("WARNING: found non-monotonic scaler ordering: evnum=" + prev.getEventNum() + " timestamp=" + prev.getTimestamp() + " i=" + i); logger.warning(" next readout has: evnum=" + next.getEventNum() + " timestamp=" + next.getTimestamp()); result = false; } From dd7f6e30fa501bea8c7512d65c23e2a234c327b6 Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Tue, 9 Sep 2025 09:51:19 -0400 Subject: [PATCH 28/29] feat: instantiation without `data` creation --- .../java/org/jlab/detector/qadb/QadbBin.java | 40 +++++++++++++------ .../jlab/detector/qadb/QadbBinSequence.java | 18 ++++++++- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java index 08c5ee54e3..ec8dd97c33 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBin.java @@ -280,24 +280,38 @@ private static double getDsc2Charge(DaqScalers ds, ChargeType chargeType) { // ---------------------------------------------------------------------------------- + /** print a QA bin, and some basic information */ + public void print() { + System.out.printf("BIN %d", this.getBinNum()); + System.out.printf(" -----------\n"); + System.out.printf("%30s %d to %d\n", "timestamp interval:", this.getTimestampMin(), this.getTimestampMax()); + System.out.printf("%30s %d to %d\n", "event number interval:", this.getEventNumMin(), this.getEventNumMax()); + System.out.printf("%30s %f s\n", "duration:", this.getDuration()); + System.out.printf("%30s %d events\n", "event number range:", this.getEventNumMax() - this.getEventNumMin()); + System.out.printf("%30s %f / %f\n", "beam charge gated / ungated:", this.getBeamChargeGated(), this.getBeamCharge()); + } + /** - * print a QA bin and its data - * @param verbose if {@code true}, print more + * print a QA bin's stored {@link data} * @param dataPrinter a lambda which resolves {@link data} as a {@code String} */ - public void print(boolean verbose, DataPrinter dataPrinter) { - System.out.printf("BIN %d", this.getBinNum()); + public void print(DataPrinter dataPrinter) { + System.out.printf("BIN %d :: ", this.getBinNum()); + System.out.println(dataPrinter.run(this.data)); + } + + /** + * print a QA bin's stored {@link data}, and optionally the bin's basic information + * @param dataPrinter a lambda which resolves {@link data} as a {@code String} + * @param verbose if {@code true}, print more + */ + public void print(DataPrinter dataPrinter, boolean verbose) { if(verbose) { - System.out.printf(" -----------\n"); - System.out.printf("%30s %d to %d\n", "timestamp interval:", this.getTimestampMin(), this.getTimestampMax()); - System.out.printf("%30s %d to %d\n", "event number interval:", this.getEventNumMin(), this.getEventNumMax()); - System.out.printf("%30s %f s\n", "duration:", this.getDuration()); - System.out.printf("%30s %d events\n", "event number range:", this.getEventNumMax() - this.getEventNumMin()); - System.out.printf("%30s %f / %f\n", "beam charge gated / ungated:", this.getBeamChargeGated(), this.getBeamCharge()); - } else { - System.out.printf(" :: "); + this.print(); + System.out.println(dataPrinter.run(this.data)); } - System.out.println(dataPrinter.run(this.data)); + else + this.print(dataPrinter); } } diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index 6ed1494a9e..f337569dcf 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -91,6 +91,17 @@ public QadbBinSequence(List filenames, int binWidth, DataInitializer logger.fine(" number of QADB bins = " + this.qaBins.size()); } + /** + * alternative constructor, with no {@link QadbBin#data} initialization parameter + *

      + * {@link QadbBin#data} will be initialized to {@code null} + * @param filenames list of HIPO files to read + * @param binWidth the number of consecutive scaler-readout intervals in each bin + */ + public QadbBinSequence(List filenames, int binWidth) { + this(filenames, binWidth, (binNum)->null); + } + // ---------------------------------------------------------------------------------- /** iterable interface implementation */ @@ -169,6 +180,11 @@ public static void main(String[] args) { // - in practice, we can use any data type instead of an integer, such as a class full of histograms // - the lambda argument `binNum` can be used, for example, as part of the histogram titles QadbBinSequence seq = new QadbBinSequence<>(filenames, 2000, (binNum)->0); + /* alternatively, if you do not want to store data with this class instance, use `Object` as the type, and no initializer lambda: + QadbBinSequence seeq = new QadbBinSequence<>(filenames, 2000); + for(var bin : seeq) bin.print(); + System.exit(0); + */ // apply a charge correction method // for(var bin : seq) @@ -223,7 +239,7 @@ public static void main(String[] args) { // print the results: the bin number along with its number of events System.out.println(">>> QA BINS <<<"); for(var bin : seq) - bin.print(true, (data) -> String.format("%30s %d", "counted tag-0 events:", data)); + bin.print((data) -> String.format("%30s %d", "counted tag-0 events:", data), true); } } From cf124d5cd363783664b83f60cf304c123b0d432b Mon Sep 17 00:00:00 2001 From: Christopher Dilks Date: Tue, 9 Sep 2025 12:02:38 -0400 Subject: [PATCH 29/29] refactor: cleanup --- .../jlab/detector/qadb/QadbBinSequence.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java index f337569dcf..c839f96260 100644 --- a/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java +++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/qadb/QadbBinSequence.java @@ -54,37 +54,38 @@ public interface DataInitializer { * @param initDataFunction a lambda to create the initial data for each bin; must be of the form {@code (binNumber) -> { return initData object }} */ public QadbBinSequence(List filenames, int binWidth, DataInitializer initDataFunction) { + if(binWidth <= 0) + throw new RuntimeException("binWidth must be greater than 0"); // construct the full, sorted scaler sequence logger.info("QadbBinSequence:: constructing DAQ scalers sequence"); this.readFiles(filenames); + if(this.scalers.isEmpty()) + throw new RuntimeException("scalers sequence is empty"); + // validate ordering: currently, QADBs use event number for lookups, so event number vs. timestamp should monotonically increase logger.fine("...validating ordering..."); if(!this.validateOrdering()) logger.severe("ERROR: scaler readout ordering is NOT VALID!"); // continue anyway, since the user may still want to see the QADB results logger.fine("...done, now constructing QADB bin sequence..."); - // sanity checks - if(binWidth <= 0) - throw new RuntimeException("binWidth must be greater than 0"); logger.fine(" initial sequence size = " + this.scalers.size()); - if(this.scalers.isEmpty()) - throw new RuntimeException("scalers sequence is empty"); // add an initial, empty bin; its scaler sequence just contains the first scaler readout - this.qaBins.add(new QadbBin(0, QadbBin.BinType.FIRST, this.scalers.subList(0, 1), initDataFunction.run(0))); + int binNum = 0; + this.qaBins.add(new QadbBin(binNum, QadbBin.BinType.FIRST, this.scalers.subList(0, 1), initDataFunction.run(binNum))); // sample the original scaler sequence: make a new `QadbBin` for each subsequence List scalersToKeep = new ArrayList<>(); // list of `scalers` indices to keep, i.e., the ones at the bin boundaries scalersToKeep.add(0); for(int i=0; i(binNum, QadbBin.BinType.INTERMEDIATE, this.scalers.subList(i, end+1), initDataFunction.run(binNum))); scalersToKeep.add(end); } logger.fine(" scalers to keep = " + scalersToKeep); // add a final, empty bin; its scaler sequence just contains the last scaler readout - int binNum = this.qaBins.size(); + binNum = this.qaBins.size(); this.qaBins.add(new QadbBin(binNum, QadbBin.BinType.LAST, this.scalers.subList(this.scalers.size()-1, this.scalers.size()), initDataFunction.run(binNum))); // remove all `scalers` elements which are not on bin boundaries - for (int i=this.scalers.size()-1; i>=0; i--) { - if (!scalersToKeep.contains(i)) + for(int i=this.scalers.size()-1; i>=0; i--) { + if(!scalersToKeep.contains(i)) this.scalers.remove(i); } logger.fine(" sampled sequence size = " + this.scalers.size()); @@ -170,7 +171,7 @@ public static void main(String[] args) { // parse arguments, which must be a list of HIPO files if(args.length == 0) - throw new RuntimeException("arguments must be HIPO file(s)"); + throw new RuntimeException("argument(s) must be HIPO file(s)"); List filenames = new ArrayList<>(); filenames.addAll(Arrays.asList(args)); @@ -232,7 +233,7 @@ public static void main(String[] args) { logger.info("===== end event loop ===="); // correct the first and last bin with the tag-0 event number and timestamp extrema; - // this is done such that the event number and timestamp ranges are correct + // this is done such that the event number and timestamp ranges are correct for these bins seq.correctLowerBound(evnumMin, timestampMin); seq.correctUpperBound(evnumMax, timestampMax);