Skip to content

Commit d218dec

Browse files
committed
Added CMV workflow documentation to README and fixed CMV packet size mismatch handling
1 parent 27b73e3 commit d218dec

File tree

3 files changed

+195
-5
lines changed

3 files changed

+195
-5
lines changed

Detectors/TPC/workflow/README.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,191 @@ To directly dump the digits to file for inspection use for the reco workflow
274274
```bash
275275
| o2-tpc-reco-workflow --input-type digitizer --output-type digits --disable-mc
276276
```
277+
278+
## TPC Common Mode Value (CMV) Workflows
279+
280+
The CMV workflows parse raw TPC data, buffer Common Mode Values per CRU on FLPs, then merge and aggregate them on a calibration node before serializing the CMVContainer in a TTree. The resulting object can be uploaded to the CCDB or written to the disk.
281+
282+
### Workflow components
283+
284+
| Executable | Output | Description |
285+
|---|---|---|
286+
| `o2-tpc-cmv-to-vector` | `TPC/CMVVECTOR` | Parses raw TPC data and creates vectors of CMVs per CRU |
287+
| `o2-tpc-cmv-flp` | `TPC/CMVGROUP` | Buffers N TFs per CRU on the FLP and groups them for forwarding |
288+
| `o2-tpc-cmv-distribute` | TTree / CCDB payload | Merges CRUs over N TFs on the calibration node, serializes the CMVContainer into a TTree, and either writes it to disk (`--dump-cmvs`) or forwards it as a CCDB object (`--enable-CCDB-output`) |
289+
290+
#### `o2-tpc-cmv-to-vector`
291+
292+
| Option | Default | Description |
293+
|---|---|---|
294+
| `--input-spec` | `A:TPC/RAWDATA` | DPL input spec for raw TPC data |
295+
| `--crus` | `0-359` | CRU range to process, comma-separated ranges |
296+
| `--write-debug` | false | Write a debug output tree every TF |
297+
| `--write-debug-on-error` | false | Write a debug output tree only when decoding errors occur |
298+
| `--debug-file-name` | `/tmp/cmv_vector_debug.{run}.root` | Name of the debug output ROOT file |
299+
| `--write-raw-data-on-error` | false | Dump raw data to file when decoding errors occur |
300+
| `--raw-file-name` | `/tmp/cmv_debug.{run}.{raw_type}` | Name of the raw debug output file |
301+
| `--raw-data-type` | 0 | Raw data format to dump on error: 0 = full TPC with DPL header, 1 = full TPC with DPL header (skip empty), 2 = full TPC no DPL header, 3 = full TPC no DPL header (skip empty), 4 = IDC raw only, 5 = CMV raw only |
302+
| `--check-incomplete-hbf` | false | Check and report incomplete HBFs in the raw parser |
303+
304+
#### `o2-tpc-cmv-flp`
305+
306+
| Option | Default | Description |
307+
|---|---|---|
308+
| `--crus` | `0-359` | CRU range handled by this FLP |
309+
| `--lanes` | hw_concurrency/2 | Parallel processing lanes (CRUs split per lane) |
310+
| `--time-lanes` | 1 | Parallel lanes for time-frame splitting |
311+
| `--n-TFs-buffer` | 1 | Number of TFs to buffer before forwarding |
312+
| `--dump-cmvs-flp` | false | Dump raw CMV vectors per CRU to a ROOT file each TF (for debugging) |
313+
314+
#### `o2-tpc-cmv-distribute`
315+
316+
| Option | Default | Description |
317+
|---|---|---|
318+
| `--crus` | `0-359` | CRU range expected from upstream |
319+
| `--timeframes` | 2000 | Number of TFs aggregated per calibration interval |
320+
| `--firstTF` | -1 | First time frame index; -1 = auto-detect from first incoming TF; values < -1 set an offset of `\|firstTF\|+1` TFs before the first interval begins |
321+
| `--lanes` | 1 | Number of parallel lanes (CRUs are split evenly across lanes) |
322+
| `--n-TFs-buffer` | 1 | Number of TFs buffered per group in the upstream `o2-tpc-cmv-flp` (must match that workflow's setting) |
323+
| `--enable-CCDB-output` | false | Forward the CMVContainer TTree as a CCDB object to `o2-calibration-ccdb-populator-workflow` |
324+
| `--use-precise-timestamp` | false | Fetch orbit-reset and GRPECS from CCDB to compute a precise CCDB validity timestamp |
325+
| `--dump-cmvs` | false | Write the CMVContainer TTree to a local ROOT file on disk |
326+
| `--use-sparse` | false | Sparse encoding: skip zero time bins (raw uint16 values; combine with `--use-compression-varint` or `--use-compression-huffman` for compressed sparse output) |
327+
| `--use-compression-varint` | false | Delta + zigzag + varint compression over all values; combined with `--use-sparse`: varint-encoded exact values at non-zero positions |
328+
| `--use-compression-huffman` | false | Huffman encoding over all values; combined with `--use-sparse`: Huffman-encoded exact values at non-zero positions |
329+
| `--cmv-zero-threshold` | 0 | Zero out CMV values whose magnitude is below this threshold (ADC) after optional rounding and before compression; 0 disables |
330+
| `--cmv-round-integers-threshold` | 0 | Round values to nearest integer ADC for \|v\| ≤ N ADC before compression; 0 disables |
331+
| `--cmv-dynamic-precision-mean` | 1.0 | Gaussian centre in \|CMV\| (ADC) where the strongest fractional-bit trimming is applied |
332+
| `--cmv-dynamic-precision-sigma` | 0 | Gaussian width (ADC) for smooth CMV fractional-bit trimming; 0 disables |
333+
| `--drop-data-after-nTFs` | 0 | Drop data for a relative TF slot after this many TFs have passed without receiving all CRUs; 0 uses the default derived from `--check-data-every-n` |
334+
| `--check-data-every-n` | 0 | Check for missing CRU data every N invocations of the run function; -1 disables checking, 0 uses the default (timeframes/2) |
335+
| `--nFactorTFs` | 1000 | Number of TFs to skip before flushing the oldest incomplete aggregation interval |
336+
337+
### Example 1 — Simple usage for testing
338+
339+
```bash
340+
#!/bin/bash
341+
342+
hash="test"
343+
MAX_TFS=1
344+
CRUS="0-359"
345+
346+
ARGS_ALL="-b --session ${USER}.${hash} --shm-segment-size $((8<<30))"
347+
348+
o2-raw-tf-reader-workflow $ARGS_ALL \
349+
--input-data tf.subset.list \
350+
--max-tf ${MAX_TFS} |
351+
o2-tfidinfo-writer-workflow $ARGS_ALL \
352+
--early-forward-policy noraw \
353+
--fairmq-rate-logging 0 \
354+
--timeframes-rate-limit ${MAX_TFS} \
355+
--timeframes-rate-limit-ipcid 583693664 |
356+
o2-tpc-cmv-to-vector $ARGS_ALL \
357+
--input-spec "A:TPC/RAWDATA" \
358+
--write-debug-on-error \
359+
--crus ${CRUS} |
360+
o2-tpc-cmv-flp $ARGS_ALL \
361+
--crus ${CRUS} |
362+
o2-tpc-cmv-distribute $ARGS_ALL \
363+
--crus ${CRUS} \
364+
--dump-cmvs \
365+
--enable-CCDB-output \
366+
--cmv-zero-threshold 1.0 \
367+
--cmv-dynamic-precision-mean 1.0 \
368+
--cmv-dynamic-precision-sigma 8.0 \
369+
--use-sparse \
370+
--use-compression-huffman |
371+
o2-calibration-ccdb-populator-workflow $ARGS_ALL \
372+
--ccdb-path ccdb-test.cern.ch:8080
373+
```
374+
375+
### Example 2 — Bash scripts for more realistic testing
376+
377+
In a real online setup, multiple FLPs each process their own CRU subset and forward compressed CMV groups to a central aggregator node via ZeroMQ.
378+
379+
**FLP side (`Send.sh`)** — run one instance per FLP (pass `N_FLPs` as first argument):
380+
381+
```bash
382+
#!/bin/bash
383+
384+
# Number of FLPs (passed as first argument, default 1)
385+
N_FLPs=${1:-1}
386+
387+
hash="test"
388+
MAX_TFS=1
389+
390+
minCRU=0
391+
maxCRU=360
392+
393+
ARGS_ALL="-b --shm-segment-size $((8<<30))"
394+
395+
for ((i = 0; i < ${N_FLPs}; i++)); do
396+
xpos_start=100
397+
xpos=$((xpos_start + 1000 * $i))
398+
399+
let diff=${maxCRU}-${minCRU}
400+
let Start=${minCRU}+$i*${diff}/${N_FLPs}
401+
let End=$Start+${diff}/${N_FLPs}-1
402+
403+
crus="$Start-$End"
404+
echo "FLP $i: crus $crus"
405+
406+
xterm -hold -geometry 150x41+$xpos+300 -e bash -c "unset PYTHONHOME PYTHONPATH; echo FLP $i;
407+
o2-raw-tf-reader-workflow $ARGS_ALL \
408+
--session ${USER}.${hash}.send.$i \
409+
--input-data tf.subset.list \
410+
--max-tf ${MAX_TFS} |
411+
o2-tfidinfo-writer-workflow $ARGS_ALL \
412+
--session ${USER}.${hash}.send.$i \
413+
--early-forward-policy noraw \
414+
--fairmq-rate-logging 0 \
415+
--timeframes-rate-limit ${MAX_TFS} \
416+
--timeframes-rate-limit-ipcid $((583693664 + $i)) |
417+
o2-tpc-cmv-to-vector $ARGS_ALL \
418+
--session ${USER}.${hash}.send.$i \
419+
--input-spec 'A:TPC/RAWDATA' \
420+
--write-debug-on-error \
421+
--crus ${crus} |
422+
o2-tpc-cmv-flp $ARGS_ALL \
423+
--session ${USER}.${hash}.send.$i \
424+
--crus ${crus} |
425+
o2-dpl-output-proxy $ARGS_ALL \
426+
--session ${USER}.${hash}.send.$i \
427+
--sporadic-inputs \
428+
--channel-config 'name=downstream,method=connect,address=tcp://localhost:30453,type=push,transport=zeromq' \
429+
--dataspec 'downstream:TPC/CMVGROUP;downstream:TPC/CMVORBITINFO'; exec bash" &
430+
done
431+
```
432+
433+
Each FLP connects to the aggregator's pull socket on port `30453` and pushes `TPC/CMVGROUP` and `TPC/CMVORBITINFO` messages. The CRU range is automatically split evenly across `N_FLPs`.
434+
435+
**Aggregator side (`Receive.sh`)**:
436+
437+
```bash
438+
#!/bin/bash
439+
440+
hash="test"
441+
CRUS="0-359"
442+
443+
ARGS_ALL="-b --session ${USER}.${hash}.receive --shm-segment-size $((8<<30))"
444+
445+
# ZeroMQ proxy: pull from all FLPs connecting on port 30453
446+
configProxy="name=readout-proxy,type=pull,method=bind,address=tcp://localhost:30453,rateLogging=1,transport=zeromq"
447+
448+
o2-dpl-raw-proxy $ARGS_ALL \
449+
--channel-config "${configProxy}" \
450+
--dataspec "A:TPC/CMVGROUP;A:TPC/CMVORBITINFO" |
451+
o2-tpc-cmv-distribute $ARGS_ALL \
452+
--crus ${CRUS} \
453+
--dump-cmvs \
454+
--enable-CCDB-output \
455+
--cmv-zero-threshold 1.0 \
456+
--cmv-dynamic-precision-mean 1.0 \
457+
--cmv-dynamic-precision-sigma 8.0 \
458+
--use-sparse \
459+
--use-compression-huffman |
460+
o2-calibration-ccdb-populator-workflow $ARGS_ALL \
461+
--ccdb-path ccdb-test.cern.ch:8080
462+
```
463+
464+
The aggregator binds the ZeroMQ pull socket and waits for all FLPs to connect. Once `TPC/CMVGROUP` and `TPC/CMVORBITINFO` data arrive, `o2-tpc-cmv-distribute` merges them, applies compression, writes the object to the disk and uploads to the CCDB.

Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,9 +458,11 @@ class TPCDistributeCMVSpec : public o2::framework::Task
458458
mNFactorTFs = 0;
459459
// ToDo: Find better fix
460460
auto& deviceProxy = pc.services().get<FairMQDeviceProxy>();
461-
auto& state = deviceProxy.getOutputChannelState({0});
462-
size_t oldest = std::numeric_limits<size_t>::max() - 1; // just set to really large value
463-
state.oldestForChannel = {oldest};
461+
if (deviceProxy.getNumOutputChannels() > 0) {
462+
auto& state = deviceProxy.getOutputChannelState({0});
463+
size_t oldest = std::numeric_limits<size_t>::max() - 1; // just set to really large value
464+
state.oldestForChannel = {oldest};
465+
}
464466
}
465467

466468
LOGP(info, "All TFs {} for current buffer received. Clearing buffer", tf);

Detectors/TPC/workflow/src/CMVToVectorSpec.cxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,15 +149,15 @@ class CMVToVectorDevice : public o2::framework::Task
149149
LOGP(debug, "Processing firstTForbit {:9}, tfCounter {:5}, run {:6}, feeId {:6}, cruID {:3}, link {:2}", dh->firstTForbit, dh->tfCounter, dh->runNumber, feeId, cruID, link);
150150

151151
if (std::find(mCRUs.begin(), mCRUs.end(), cruID) == mCRUs.end()) {
152-
LOGP(error, "CMV CRU {:3} not configured in CRUs, skipping", cruID);
152+
LOGP(warning, "CMV CRU {:3} not configured in CRUs, skipping", cruID);
153153
continue;
154154
}
155155

156156
auto& cmvVec = mCMVvectors[cruID];
157157
auto& infoVec = mCMVInfos[cruID];
158158

159159
if (size != sizeof(cmv::Container)) {
160-
// LOGP(error, "CMV packet size mismatch: got {} bytes, expected {} bytes (sizeof cmv::Container). Skipping package.", size, sizeof(cmv::Container));
160+
LOGP(warning, "CMV packet size mismatch: got {} bytes, expected {} bytes (sizeof cmv::Container). Skipping package.", size, sizeof(cmv::Container));
161161
hasErrors = true;
162162
continue;
163163
}

0 commit comments

Comments
 (0)