diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0ca4817d84..a20b1924bc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -227,12 +227,12 @@ jobs:
tar xzf clara.tar.gz
tar xzf coatjava.tar.gz
- run: ls
- - name: run test
- run: ./bin/run-clara -y ./etc/services/rgd-clarode.yml -t 4 -n 500 -c ./clara -o ./tmp ./clas_018779.evio.00001
+ - name: run clara
+ run: ./coatjava/bin/run-clara -y ./etc/services/rgd-clarode.yml -t 4 -n 100 -c ./clara -o ./tmp ./clas_018779.evio.00001
+ - name: run mutil
+ run: ./coatjava/bin/recon-mutil -y ./etc/services/rgd-clarode.yml -t 4 -n 30 -o rec.hipo ./clas_018779.evio.00001
- name: ls tmp
- run: ls -lhtr tmp
- - name: rename
- run: mv -v tmp/rec_clas_018779.evio.00001.hipo rec.hipo
+ run: ls -lhtr . tmp
- uses: actions/upload-artifact@v7
with:
name: test_clara_result
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d42ca40d5f..a74a9cbd3c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -175,6 +175,14 @@ clara:
- run-clara -v -c $CLARA_HOME -t 4 -y ./etc/services/rgd-clarode.yml -n 30 -o out $EVIOFILE
- mv out/rec_$EVIOFILE.hipo claroded.hipo
+recon-mutil:
+ stage: test
+ needs: [build,download]
+ dependencies: [build,download]
+ script:
+ - tar -xzf coatjava.tar.gz
+ - recon-mutil -t 4 -y ./etc/services/rgd-clarode.yml -n 30 -o rec_$EVIOFILE.hipo $EVIOFILE
+
profile:
extends: .clon
allow_failure: true
diff --git a/bin/recon-mutil b/bin/recon-mutil
new file mode 100755
index 0000000000..3aaec2480b
--- /dev/null
+++ b/bin/recon-mutil
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+. `dirname $0`/../libexec/env.sh
+
+split_cli $@
+
+export MALLOC_ARENA_MAX=1
+
+java ${JAVA_OPTS-} -Xms10240m -XX:+UseParallelGC ${jvm_options[@]} \
+ -cp ${COATJAVA_CLASSPATH:-''} \
+ org.jlab.clas.reco.ReconMutil \
+ ${class_options[@]}
diff --git a/common-tools/clara-io/pom.xml b/common-tools/clara-io/pom.xml
index f1c3f6f0e1..3c9ddb5948 100644
--- a/common-tools/clara-io/pom.xml
+++ b/common-tools/clara-io/pom.xml
@@ -36,12 +36,6 @@
jnp-hipo4
-
- org.jlab.clas
- clas-io
- 14.2.0-SNAPSHOT
-
-
org.jlab.clas
clas-detector
@@ -50,7 +44,7 @@
org.jlab.clas
- clas-utils
+ clas-io
14.2.0-SNAPSHOT
diff --git a/common-tools/clara-io/src/main/java/org/jlab/io/clara/Clas12Writer.java b/common-tools/clara-io/src/main/java/org/jlab/io/clara/Clas12Writer.java
index e2d5aaa7a0..5d5a917add 100644
--- a/common-tools/clara-io/src/main/java/org/jlab/io/clara/Clas12Writer.java
+++ b/common-tools/clara-io/src/main/java/org/jlab/io/clara/Clas12Writer.java
@@ -1,22 +1,11 @@
package org.jlab.io.clara;
-import java.io.File;
import java.nio.file.Path;
-import java.util.List;
-import java.util.TreeMap;
-import java.util.TreeSet;
import org.jlab.clara.std.services.EventWriterException;
import org.jlab.detector.calib.utils.ConstantsManager;
-import org.jlab.detector.decode.CLASDecoder4;
-import org.jlab.detector.helicity.HelicitySequence;
-import org.jlab.detector.helicity.HelicitySequenceDelayed;
-import org.jlab.detector.helicity.HelicityState;
-import org.jlab.detector.scalers.DaqScalersSequence;
-import org.jlab.detector.serial.PostProcessor;
-import org.jlab.jnp.hipo4.data.Bank;
+import org.jlab.detector.serial.SerialHoncho;
import org.jlab.jnp.hipo4.data.Event;
import org.jlab.jnp.hipo4.data.SchemaFactory;
-import org.jlab.jnp.hipo4.io.HipoReader;
import org.jlab.jnp.hipo4.io.HipoWriterSorted;
import org.jlab.jnp.utils.file.FileUtils;
import org.json.JSONObject;
@@ -26,41 +15,24 @@
* 1. Copies certain banks on-the-fly to new tag-1 events
* 2. Caches helicity states, scaler readouts, and unix time
* 3. Writes HEL::flip, RUN/HEL::scaler, and RUN::unix to new tag-1 events
- * 4. Runs post-processing, writing tag-1 information to all events
* 5. Adds .hipo to the output filename, if necessary
*
* @author baltzell
*/
public class Clas12Writer extends HipoToHipoWriter {
- static final String[] TAG1BANKS = {"RUN::scaler","HEL::scaler","RAW::scaler","RAW::epics","HEL::flip","COAT::config"};
-
- Bank[] tag1banks;
- Bank runConfig;
- Bank helicityAdc;
+ SerialHoncho serial;
ConstantsManager conman;
- TreeMap eventUnix;
- TreeSet helicities;
- DaqScalersSequence scalers;
SchemaFactory fullSchema;
- boolean postprocess;
private void init(JSONObject opts) {
fullSchema = new SchemaFactory();
fullSchema.initFromDirectory(FileUtils.getEnvironmentPath("CLAS12DIR","etc/bankdefs/hipo4"));
- runConfig = new Bank(fullSchema.getSchema("RUN::config"));
- helicityAdc = new Bank(fullSchema.getSchema("HEL::adc"));
- helicities = new TreeSet<>();
- scalers = new DaqScalersSequence(fullSchema);
+ serial = new SerialHoncho(fullSchema);
conman = new ConstantsManager();
- eventUnix = new TreeMap<>();
conman.init("/runcontrol/hwp","/runcontrol/helicity");
- postprocess = opts.optBoolean("postprocess", false);
if (opts.has("variation")) conman.setVariation(opts.getString("variation"));
if (opts.has("timestamp")) conman.setTimeStamp(opts.getString("timestamp"));
- tag1banks = new Bank[TAG1BANKS.length];
- for (int i=0; i 0) {
- int unix = runConfig.getInt("unixtime",0);
- int evno = runConfig.getInt("event",0);
- if (unix > 0 && evno > 0) eventUnix.put(evno, unix);
- }
- helicities.add(HelicityState.createFromFadcBank(helicityAdc, runConfig, conman));
- Event t = CLASDecoder4.createTaggedEvent((Event)event, runConfig, tag1banks);
+ Event t = serial.read((Event)event);
if (!t.isEmpty()) writer.addEvent(t, 1);
super.writeEvent(event);
}
@Override
protected void closeWriter() {
- HelicitySequence.writeFlips(fullSchema, writer, helicities);
- writer.addEvent(getUnixEvent(runConfig),1);
+ serial.finish(writer);
super.closeWriter();
- if (postprocess) postprocess();
- // keep the latest helicity/scaler reading for the next file:
- while (helicities.size() > 60) helicities.pollFirst();
- scalers.clear(10);
- }
-
- /**
- * Get the first valid run number from a RUN::config bank.
- * @return run
- */
- private int getRunNumber() {
- Event e = new Event();
- HipoReader r = new HipoReader();
- r.open(filename);
- while (r.hasNext()) {
- r.nextEvent(e);
- e.read(runConfig);
- if (runConfig.getRows()>0 && runConfig.getInt("run",0)>0)
- return runConfig.getInt("run",0);
- }
- return 0;
+ serial.clear();
}
-
- /**
- * Get a new event with a RUN::unix bank containing event-timestamp mapping,
- * and the latest RUN::config bank.
- * @param config
- * @return
- */
- private Event getUnixEvent(Bank config) {
- Bank unix = new Bank(fullSchema.getSchema("RUN::unix"));
- unix.setRows(eventUnix.size());
- int row = 0;
- for (int evno : eventUnix.keySet()) {
- unix.putInt("event", row, evno);
- unix.putInt("unixtime",row, eventUnix.get(evno));
- row++;
- }
- Event e = new Event();
- e.write(config);
- e.write(unix);
- return e;
- }
-
- /**
- * Copy helicity/charge tag-1 information to all events.
- */
- private void postprocess() {
- int d = conman.getConstants(getRunNumber(), "/runcontrol/helicity").getIntValue("delay",0,0,0);
- HelicitySequenceDelayed helicity = new HelicitySequenceDelayed(d);
- helicity.addStream(helicities);
- PostProcessor p = new PostProcessor(List.of(filename), fullSchema, helicity, scalers);
- HipoReader r = new HipoReader();
- r.open(filename);
- Event e = new Event();
- writer.open("pp_"+filename);
- while (r.hasNext()) {
- r.nextEvent(e);
- p.processEvent(e);
- HipoToHipoWriter.writeEvent(writer, e, schemaBankList);
- }
- writer.close();
- new File(filename).delete();
- new File("pp_"+filename).renameTo(new File(filename));
- }
-
}
diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/decode/CLASDecoderPool.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/decode/CLASDecoderPool.java
index 80430583ee..a28135d906 100644
--- a/common-tools/clas-detector/src/main/java/org/jlab/detector/decode/CLASDecoderPool.java
+++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/decode/CLASDecoderPool.java
@@ -34,11 +34,11 @@ public CLASDecoderPool(int size, String variation, String timestamp) {
}
}
- public CLASDecoder take() throws InterruptedException {
+ public CLASDecoder take() {
return pool.poll();
}
- public void put(CLASDecoder decoder) throws InterruptedException {
+ public void put(CLASDecoder decoder) {
pool.offer(decoder);
}
diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/serial/PostProcessor.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/serial/PostProcessor.java
index 0d0f1b96bc..a245029e17 100644
--- a/common-tools/clas-detector/src/main/java/org/jlab/detector/serial/PostProcessor.java
+++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/serial/PostProcessor.java
@@ -231,6 +231,22 @@ public void processEvent(Event event) {
}
}
+ public void processFile(String input, String output) {
+ Event event = new Event();
+ HipoReader r = new HipoReader();
+ r.open(input);
+ HipoWriterSorted w = new HipoWriterSorted();
+ w.getSchemaFactory().initFromDirectory(ClasUtilsFile.getResourceDir("CLAS12DIR", "etc/bankdefs/hipo4"));
+ w.setCompressionType(2);
+ w.open(output);
+ while (r.hasNext()) {
+ r.nextEvent(event);
+ processEvent(event);
+ if (w != null) w.addEvent(event);
+ }
+ r.close();
+ }
+
/**
* The "postprocess" program.
* @param args
diff --git a/common-tools/clas-detector/src/main/java/org/jlab/detector/serial/SerialHoncho.java b/common-tools/clas-detector/src/main/java/org/jlab/detector/serial/SerialHoncho.java
new file mode 100644
index 0000000000..6fe28171ef
--- /dev/null
+++ b/common-tools/clas-detector/src/main/java/org/jlab/detector/serial/SerialHoncho.java
@@ -0,0 +1,182 @@
+package org.jlab.detector.serial;
+
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.jlab.detector.calib.utils.ConstantsManager;
+import org.jlab.detector.decode.CLASDecoder;
+import org.jlab.detector.helicity.HelicityBit;
+import org.jlab.detector.helicity.HelicitySequence;
+import org.jlab.detector.helicity.HelicitySequenceDelayed;
+import org.jlab.detector.helicity.HelicityState;
+import org.jlab.detector.scalers.DaqScalers;
+import org.jlab.detector.scalers.DaqScalersSequence;
+import org.jlab.jnp.hipo4.data.Bank;
+import org.jlab.jnp.hipo4.data.Event;
+import org.jlab.jnp.hipo4.data.SchemaFactory;
+import org.jlab.jnp.hipo4.io.HipoWriterSorted;
+
+/**
+ *
+ * @author baltzell
+ */
+public class SerialHoncho {
+
+ static final String[] TAG1BANKS = {"RUN::scaler","HEL::scaler","RAW::scaler","RAW::epics","HEL::flip","COAT::config"};
+ SchemaFactory schema;
+ Bank[] tag1banks;
+ Bank runConfig; // FIXME: store Schema for banks;
+ Bank helicityAdc;
+ ConstantsManager conman;
+ TreeMap eventUnix;
+ HelicitySequence helicitySequence;
+ TreeSet helicities;
+ DaqScalersSequence scalers;
+ int run;
+
+ public SerialHoncho(SchemaFactory schema) {
+ this.schema = schema;
+ conman = new ConstantsManager();
+ conman.init("/runcontrol/hwp","/runcontrol/helicity");
+ runConfig = new Bank(schema.getSchema("RUN::config"));
+ helicityAdc = new Bank(schema.getSchema("HEL::adc"));
+ scalers = new DaqScalersSequence(schema);
+ helicities = new TreeSet<>();
+ eventUnix = new TreeMap<>();
+ tag1banks = new Bank[TAG1BANKS.length];
+ for (int i=0; i 0) {
+ if (run <= 0 && runConfig.getInt("run", 0) > 0) {
+ run = runConfig.getInt("run",0);
+ helicitySequence = new HelicitySequenceDelayed(
+ conman.getConstants(run, "/runcontrol/helicity").getIntValue("delay",0,0,0));
+ }
+ int unix = runConfig.getInt("unixtime",0);
+ int evno = runConfig.getInt("event",0);
+ if (unix > 0 && evno > 0) eventUnix.put(evno, unix);
+ }
+ if (helicitySequence != null) {
+ HelicityState state = HelicityState.createFromFadcBank(helicityAdc, runConfig, conman);
+ helicities.add(state);
+ helicitySequence.addState(state);
+ }
+ return CLASDecoder.createTaggedEvent(event, runConfig, tag1banks);
+ }
+
+ public void process(Event event) {
+ Bank cfg = new Bank(schema.getSchema("RUN::config"));
+ Bank evt = new Bank(schema.getSchema("REC::Event"));
+ event.read(cfg);
+ event.read(evt);
+ if (cfg.getRows() > 0) {
+ processEventUnix(event, cfg);
+ if (evt.getRows() > 0) {
+ event.remove(evt.getSchema());
+ processHelicity(event, cfg, evt);
+ processScalers(cfg, evt);
+ event.write(evt);
+ }
+ }
+ }
+
+ public void finish(HipoWriterSorted writer) {
+ writer.addEvent(getUnixEvent(runConfig),1);
+ // FIXME: mark written flips and don't write them again
+ helicitySequence.writeFlips(writer, 1);
+ }
+
+ public void clear() {
+ eventUnix.clear();
+ helicities.clear();
+ scalers.clear();
+ }
+
+ public DaqScalersSequence getScalers() {
+ return scalers;
+ }
+
+ public HelicitySequence getHelicitySequence() {
+ return helicitySequence;
+ }
+
+ public ConstantsManager getConstantsManager() {
+ return conman;
+ }
+
+ public SchemaFactory getSchemaFactory() {
+ return schema;
+ }
+
+ public TreeSet getHelicities() {
+ return helicities;
+ }
+
+ HelicitySequence createHelicitySequence() {
+ HelicitySequence seq = new HelicitySequenceDelayed(
+ conman.getConstants(run, "/runcontrol/helicity").getIntValue("delay",0,0,0));
+ seq.addStream(helicities);
+ return seq;
+ }
+
+ Event getUnixEvent(Bank config) {
+ Bank unix = new Bank(schema.getSchema("RUN::unix"));
+ unix.setRows(eventUnix.size());
+ int row = 0;
+ for (int evno : eventUnix.keySet()) {
+ unix.putInt("event", row, evno);
+ unix.putInt("unixtime",row, eventUnix.get(evno));
+ row++;
+ }
+ Event e = new Event();
+ e.write(config);
+ e.write(unix);
+ return e;
+ }
+
+ int getUnixTime(Bank runConfig) {
+ if (runConfig.getRows() < 1) {
+ Integer key = eventUnix.floorKey(runConfig.getInt("event",0));
+ if (key != null) {
+ Integer unix = eventUnix.get(key);
+ if (unix != null) return unix;
+ }
+ }
+ return 0;
+ }
+
+ void processEventUnix(Event event, Bank runConfig) {
+ int ut = getUnixTime(runConfig);
+ event.remove(runConfig.getSchema());
+ runConfig.putInt("unixtime", 0, ut);
+ event.write(runConfig);
+ }
+
+ void processScalers(Bank runConfig, Bank recEvent) {
+ DaqScalers ds = scalers.get(runConfig.getLong("timestamp", 0));
+ if (ds != null) {
+ recEvent.putFloat("beamCharge",0, (float) ds.dsc2.getBeamChargeGated());
+ recEvent.putDouble("liveTime",0,ds.dsc2.getLivetime());
+ }
+ }
+
+ void processHelicity(Event event, Bank runConfig, Bank recEvent) {
+ HelicityBit hb = helicitySequence.search(runConfig.getLong("timestamp", 0));
+ HelicityBit hbraw = helicitySequence.getHalfWavePlate() ? HelicityBit.getFlipped(hb) : hb;
+ recEvent.putByte("helicity",0,hb.value());
+ recEvent.putByte("helicityRaw",0,hbraw.value());
+ Bank helScaler = new Bank(schema.getSchema("HEL::scaler"));
+ event.read(helScaler);
+ if (helScaler.getRows()>0) {
+ event.remove(schema.getSchema("HEL::scaler"));
+ SerialUtil.assignScalerHelicity(runConfig.getLong("timestamp",0), helScaler, helicitySequence);
+ event.write(helScaler);
+ }
+ }
+
+}
diff --git a/common-tools/clas-reco/src/main/java/org/jlab/clas/reco/ReconMutil.java b/common-tools/clas-reco/src/main/java/org/jlab/clas/reco/ReconMutil.java
new file mode 100644
index 0000000000..1ae7d07684
--- /dev/null
+++ b/common-tools/clas-reco/src/main/java/org/jlab/clas/reco/ReconMutil.java
@@ -0,0 +1,541 @@
+package org.jlab.clas.reco;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.jlab.clara.engine.EngineData;
+import org.jlab.clara.engine.EngineDataType;
+import org.jlab.coda.jevio.EvioException;
+import org.jlab.detector.decode.CLASDecoder;
+import org.jlab.detector.decode.CLASDecoderPool;
+import org.jlab.detector.serial.SerialHoncho;
+import org.jlab.io.evio.EvioDataEvent;
+import org.jlab.io.evio.EvioSource;
+import org.jlab.io.hipo.HipoDataEvent;
+import org.jlab.jnp.hipo4.data.Bank;
+import org.jlab.jnp.hipo4.data.Event;
+import org.jlab.jnp.hipo4.data.SchemaFactory;
+import org.jlab.jnp.hipo4.io.HipoReader;
+import org.jlab.jnp.hipo4.io.HipoWriterSorted;
+import org.jlab.utils.ClaraYaml;
+import org.jlab.utils.benchmark.Benchmark;
+import org.jlab.utils.benchmark.ProgressPrintout;
+import org.jlab.utils.options.OptionParser;
+import org.jlab.utils.system.ClasUtilsFile;
+import org.json.JSONObject;
+
+/**
+ * FIXME: add tagged bank counter for completino decision
+ *
+ * @author baltzell
+ */
+final class ReconMutil {
+
+ // Performance parameters:
+ final int BENCH_SECONDS = 30;
+ final int CHUNKS_PER_QUEUE = 100;
+ final int EVENTS_PER_CHUNK = 100;
+
+ // File I/O:
+ Object reader;
+ HipoWriterSorted writer;
+ List schemaBankList;
+ static final SchemaFactory schema = new SchemaFactory();
+ static { schema.initFromDirectory(ClasUtilsFile.getResourceDir("CLAS12DIR","etc/bankdefs/hipo4")); }
+
+ // Processors:
+ SerialHoncho serial;
+ CLASDecoderPool decoders = new CLASDecoderPool(64,"default",null);
+ Map engines = new LinkedHashMap<>();
+
+ // Threads:
+ CompletableFuture readerThread;
+ CompletableFuture writerThread;
+ CompletableFuture rethreadThread;
+ ConcurrentLinkedQueue decoThreads = new ConcurrentLinkedQueue<>();
+ ConcurrentLinkedQueue procThreads = new ConcurrentLinkedQueue<>();
+
+ // Queues:
+ ConcurrentLinkedQueue> readQueue = new ConcurrentLinkedQueue<>();
+ ConcurrentLinkedQueue> procQueue = new ConcurrentLinkedQueue<>();
+ ConcurrentLinkedQueue> writeQueue = new ConcurrentLinkedQueue<>();
+ boolean paused = false;
+
+ // Static parameters:
+ int maxEvents;
+ int skipEvents;
+ ClaraYaml yaml;
+ OptionParser parser;
+
+ // Progress counters:
+ int readEvents;
+ int writeEvents;
+ int failEvents;
+ int fileEvents;
+ int maxFileEvents;
+ AtomicInteger taggedEvents = new AtomicInteger();
+ ProgressPrintout progress = new ProgressPrintout();
+
+ ReconMutil(OptionParser parser) {
+ init(parser);
+ }
+
+ /**
+ * The thread launcher and collector.
+ * @param threads number of threads
+ * @param output name of output file to write
+ * @param input names of input files to read
+ */
+ void launch(int[] threads, String output, String... input) {
+
+ reset();
+
+ System.out.println(String.format("recon-mutil:: Spawning %d+++ Threads...",threads[0]));
+
+ // spawn all the threads:
+ readerThread = CompletableFuture.runAsync(() -> { read(threads[0], input); });
+ writerThread = CompletableFuture.runAsync(() -> { write(output); });
+ for (int i=0; i { decode(j); }));
+ procThreads.offer(CompletableFuture.runAsync(() -> { process(j); }));
+ }
+
+ // wait for the writer to be done:
+ while (!writerThread.isDone()) {
+ sleep(1000);
+
+ //System.out.println(String.format("recon-mutil:: read(%b)/[deco(%d)]/proc(%d)/tag/write(%b)",
+ // readerThread.isDone(), decoThreads.size(), procThreads.size(), writerThread.isDone()));
+ //System.out.println(String.format("recon-util:: %d-%d/%d/%d/%d", readEvents,
+ // readQueue.size(), procQueue.size(), taggedEvents.get(), writeQueue.size()));
+
+ // cleanup completed parallel threads:
+ for (CompletableFuture f : decoThreads)
+ if (f.isDone()) decoThreads.remove(f);
+ for (CompletableFuture f : procThreads)
+ if (f.isDone()) procThreads.remove(f);
+
+ // perform scaling test:
+ if (threads.length > 1 && rethreadThread == null && writeEvents > 100) {
+ rethreadThread = CompletableFuture.runAsync(() -> { rethread(BENCH_SECONDS,threads); });
+ rethreadThread.join();
+ reset();
+ }
+ }
+ }
+
+ /**
+ * The reader thread.
+ * @param input input filenames
+ */
+ void read(int threads, String... input) {
+
+ // convert input filenames to a list:
+ List inputs = new ArrayList<>(Arrays.asList(input));
+
+ // initialize the event chunk:
+ List