Skip to content

Commit ce4daee

Browse files
sawenzelclaude
andcommitted
Share the scaled TPC distortions between digitizers through a file
This adds the option --tpc-distortion-file to the TPC digitizer, so that the scaled distortions of a job are computed only once. - The first digitizer computes the scaled distortions and writes them to the file. - The other lanes and later timeframes read the file instead of recomputing. - The file is keyed on the ETags of the CCDB maps, TPCCorrMap.lumiInst and the mean-luminosity overrides. - A file written for other inputs is replaced. - A lock file serialises the digitizers of different processes. - Without a usable lock or ETag the distortions are computed as before. https://its.cern.ch/jira/browse/O2-7309 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent e070a1f commit ce4daee

6 files changed

Lines changed: 264 additions & 6 deletions

File tree

‎Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ class Digitizer
141141
/// in case of scaled distortions, the distortions can be recalculated to ensure consistent distortions and corrections
142142
void recalculateDistortions();
143143

144+
/// Use distortions that are already scaled to the instantaneous luminosity
145+
void setScaledSCDistortions(std::unique_ptr<SC> spaceCharge);
146+
147+
/// \return the space-charge object holding the distortions, or nullptr
148+
const SC* getSCDistortions() const { return mSpaceCharge.get(); }
149+
144150
private:
145151
DigitContainer mDigitContainer; ///< Container for the Digits
146152
std::unique_ptr<SC> mSpaceCharge; ///< Handler of full distortions (static + IR dependant)

‎Detectors/TPC/simulation/src/Digitizer.cxx‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,21 @@ void Digitizer::setStartTime(double time)
235235
mDigitContainer.setStartTime(starttimebin);
236236
}
237237

238+
void Digitizer::setScaledSCDistortions(std::unique_ptr<SC> spaceCharge)
239+
{
240+
mUseSCDistortions = true;
241+
mSpaceCharge = std::move(spaceCharge);
242+
mSpaceCharge->initAfterReadingFromFile();
243+
mSpaceCharge->printMetaData();
244+
mUseScaledDistortions = true;
245+
}
246+
238247
void Digitizer::setLumiScaleFactor()
239248
{
249+
if (!mSpaceCharge || !mSpaceChargeDer) {
250+
LOGP(info, "Average or derivative distortions not set");
251+
return;
252+
}
240253
mLumiScaleFactor = (CorrMapParam::Instance().lumiInst - mSpaceCharge->getMeanLumi()) / mSpaceChargeDer->getMeanLumi();
241254
LOGP(info, "Setting Lumi scale factor: lumiInst: {} lumi mean: {} lumi mean derivative: {} lumi scale factor: {}", CorrMapParam::Instance().lumiInst, mSpaceCharge->getMeanLumi(), mSpaceChargeDer->getMeanLumi(), mLumiScaleFactor);
242255
}

‎Steer/DigitizerWorkflow/CMakeLists.txt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ o2_add_executable(digitizer-workflow
2525
src/SimReaderSpec.cxx
2626
src/SimpleDigitizerWorkflow.cxx
2727
src/TPCDigitizerSpec.cxx
28+
src/TPCDistortionMapFile.cxx
2829
src/ZDCDigitizerSpec.cxx
2930
src/TOFDigitizerSpec.cxx
3031
$<$<BOOL:${ENABLE_UPGRADES}>:src/ITS3DigitizerSpec.cxx>

‎Steer/DigitizerWorkflow/src/TPCDigitizerSpec.cxx‎

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <cassert>
1616
#include "Framework/RootSerializationSupport.h"
1717
#include "TPCDigitizerSpec.h"
18+
#include "TPCDistortionMapFile.h"
1819
#include "Framework/ControlService.h"
1920
#include "Framework/ParallelContext.h"
2021
#include "Framework/ConfigParamRegistry.h"
@@ -127,6 +128,13 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer
127128
mUseCalibrationsFromCCDB = ic.options().get<bool>("TPCuseCCDB");
128129
mMeanLumiDistortions = ic.options().get<float>("meanLumiDistortions");
129130
mMeanLumiDistortionsDerivative = ic.options().get<float>("meanLumiDistortionsDerivative");
131+
if (const auto file = ic.options().get<std::string>("tpc-distortion-file"); !file.empty()) {
132+
if (mDistortionType == 2 && mRecalcDistortions) {
133+
mDistortionMapFile = std::make_unique<TPCDistortionMapFile>(file);
134+
} else {
135+
LOG(info) << "TPC: --tpc-distortion-file is only used for distortion type 2 with recalculation";
136+
}
137+
}
130138

131139
LOG(info) << "TPC calibrations from CCDB: " << mUseCalibrationsFromCCDB;
132140

@@ -230,13 +238,20 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer
230238
pc.inputs().get<ParameterGas*>("gasparam");
231239
pc.inputs().get<ParameterGEM*>("gemparam");
232240
if (mDistortionType) {
233-
pc.inputs().get<SC*>("tpcdistortions");
234-
if (mDistortionType == 2) {
235-
pc.inputs().get<SC*>("tpcdistortionsderiv");
236-
mDigitizer.setLumiScaleFactor();
237-
if (mRecalcDistortions) {
238-
mDigitizer.recalculateDistortions();
241+
auto computeDistortions = [&]() {
242+
pc.inputs().get<SC*>("tpcdistortions");
243+
if (mDistortionType == 2) {
244+
pc.inputs().get<SC*>("tpcdistortionsderiv");
245+
mDigitizer.setLumiScaleFactor();
246+
if (mRecalcDistortions) {
247+
mDigitizer.recalculateDistortions();
248+
}
239249
}
250+
};
251+
if (mDistortionMapFile) {
252+
mDistortionMapFile->prepare(pc, mDigitizer, mMeanLumiDistortions, mMeanLumiDistortionsDerivative, computeDistortions);
253+
} else {
254+
computeDistortions();
240255
}
241256
}
242257

@@ -495,6 +510,7 @@ class TPCDPLDigitizerTask : public BaseDPLDigitizer
495510
int mDistortionType = 0;
496511
float mMeanLumiDistortions = -1;
497512
float mMeanLumiDistortionsDerivative = -1;
513+
std::unique_ptr<TPCDistortionMapFile> mDistortionMapFile; ///< file sharing the scaled distortions between digitizers
498514
bool mRecalcDistortions = false;
499515
};
500516

@@ -536,6 +552,7 @@ o2::framework::DataProcessorSpec getTPCDigitizerSpec(int channel, bool writeGRP,
536552
{"meanLumiDistortionsDerivative", VariantType::Float, -1.f, {"override lumi of derivative distortion object if >=0"}},
537553
{"do-not-recalculate-distortions", VariantType::Bool, false, {"Do not recalculate the distortions"}},
538554
{"n-threads-distortions", VariantType::Int, 4, {"Number of threads used for the calculation of the distortions"}},
555+
{"tpc-distortion-file", VariantType::String, "", {"File in which the digitizers of one job share the scaled distortions (distortion type 2 with recalculation), computed once; keep it in the job directory"}},
539556
}};
540557
}
541558

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
#include "TPCDistortionMapFile.h"
13+
#include "Framework/InputRecord.h"
14+
#include "Framework/Logger.h"
15+
#include "Framework/ProcessingContext.h"
16+
#include "TPCCalibration/CorrMapParam.h"
17+
#include "TPCSimulation/Digitizer.h"
18+
#include "TPCSpaceCharge/SpaceCharge.h"
19+
#include <Compression.h>
20+
#include <TFile.h>
21+
#include <TNamed.h>
22+
#include <TStopwatch.h>
23+
#include <fcntl.h>
24+
#include <sys/file.h>
25+
#include <unistd.h>
26+
#include <filesystem>
27+
#include <memory>
28+
#include <fmt/format.h>
29+
30+
using namespace o2::tpc;
31+
using SC = o2::tpc::SpaceCharge<float>;
32+
33+
namespace
34+
{
35+
constexpr const char* ObjectName = "ccdb_object";
36+
constexpr const char* KeyName = "tpc_distortion_key";
37+
constexpr int KeyFormat = 1; ///< version of the file content
38+
39+
/// lock on a file, shared or exclusive, released at destruction
40+
class FileLock
41+
{
42+
public:
43+
FileLock(const std::string& path, int operation)
44+
{
45+
mFD = ::open(path.c_str(), O_CREAT | O_RDWR, 0644);
46+
if (mFD >= 0 && ::flock(mFD, operation) != 0) {
47+
::close(mFD);
48+
mFD = -1;
49+
}
50+
}
51+
~FileLock()
52+
{
53+
if (mFD >= 0) {
54+
::flock(mFD, LOCK_UN);
55+
::close(mFD);
56+
}
57+
}
58+
bool ok() const { return mFD >= 0; }
59+
FileLock(const FileLock&) = delete;
60+
FileLock& operator=(const FileLock&) = delete;
61+
62+
private:
63+
int mFD = -1;
64+
};
65+
66+
std::string getETag(o2::framework::ProcessingContext& pc, const char* binding)
67+
{
68+
const auto& headers = pc.inputs().get<o2::framework::CCDBMetadataExtractor>(binding);
69+
const auto it = headers.find("ETag");
70+
return it == headers.end() ? std::string{} : it->second;
71+
}
72+
73+
std::unique_ptr<SC> readMap(const std::string& path, const std::string& key, bool warn)
74+
{
75+
if (!std::filesystem::exists(path)) {
76+
return nullptr;
77+
}
78+
TFile file(path.c_str(), "READ");
79+
std::unique_ptr<TNamed> storedKey(file.IsZombie() ? nullptr : file.Get<TNamed>(KeyName));
80+
if (!storedKey || key != storedKey->GetTitle()) {
81+
if (warn) {
82+
LOGP(warning, "TPC: {} was written for other distortion inputs and will be replaced", path);
83+
}
84+
return nullptr;
85+
}
86+
return std::unique_ptr<SC>(file.Get<SC>(ObjectName));
87+
}
88+
89+
bool writeMap(const std::string& path, const std::string& key, const SC& sc)
90+
{
91+
// written under another name and renamed, so that readers never see a partial file
92+
const auto tmpPath = fmt::format("{}.{}.tmp", path, ::getpid());
93+
bool ok = false;
94+
{
95+
TFile file(tmpPath.c_str(), "RECREATE", "", ROOT::RCompressionSetting::EDefaults::kUseAnalysis);
96+
if (!file.IsZombie()) {
97+
TNamed named(KeyName, key.c_str());
98+
ok = file.WriteObjectAny(&sc, "o2::tpc::SpaceCharge<float>", ObjectName) > 0 && file.WriteObject(&named, KeyName) > 0;
99+
file.Close();
100+
ok = ok && !file.TestBit(TFile::kWriteError);
101+
}
102+
}
103+
std::error_code ec;
104+
if (ok) {
105+
std::filesystem::rename(tmpPath, path, ec);
106+
} else {
107+
std::filesystem::remove(tmpPath, ec);
108+
}
109+
return ok && !ec;
110+
}
111+
} // namespace
112+
113+
void TPCDistortionMapFile::prepare(o2::framework::ProcessingContext& pc, Digitizer& digitizer, float meanLumi, float meanLumiDerivative,
114+
const std::function<void()>& compute)
115+
{
116+
const auto etagDist = getETag(pc, "tpcdistortions");
117+
const auto etagDeriv = getETag(pc, "tpcdistortionsderiv");
118+
if (etagDist.empty() || etagDeriv.empty()) {
119+
LOGP(warning, "TPC: no ETag for the distortion maps, the scaled distortions are computed and not shared through {}", mPath);
120+
compute();
121+
return;
122+
}
123+
const auto key = fmt::format("format {} dist {} deriv {} lumiInst {} meanLumi {} meanLumiDerivative {}", KeyFormat, etagDist, etagDeriv,
124+
CorrMapParam::Instance().lumiInst, meanLumi, meanLumiDerivative);
125+
if (key == mKey) {
126+
return; // the digitizer holds them already
127+
}
128+
129+
const auto lockPath = mPath + ".lock";
130+
TStopwatch timer;
131+
auto read = [&](bool warn) {
132+
auto sc = readMap(mPath, key, warn);
133+
if (sc) {
134+
digitizer.setScaledSCDistortions(std::move(sc));
135+
LOGP(info, "TPC: scaled distortions read from {} in {:.1f} s", mPath, timer.RealTime());
136+
mKey = key;
137+
}
138+
return mKey == key;
139+
};
140+
{
141+
// digitizers that find the file read it concurrently
142+
FileLock lock(lockPath, LOCK_SH);
143+
if (!lock.ok()) {
144+
LOGP(warning, "TPC: cannot lock {}, the scaled distortions are computed and not shared", lockPath);
145+
compute();
146+
return;
147+
}
148+
if (read(false)) {
149+
return;
150+
}
151+
}
152+
// one digitizer computes and writes, the others wait and then read
153+
FileLock lock(lockPath, LOCK_EX);
154+
if (!lock.ok()) {
155+
LOGP(warning, "TPC: cannot lock {}, the scaled distortions are computed and not shared", lockPath);
156+
compute();
157+
return;
158+
}
159+
if (!read(true)) {
160+
compute();
161+
timer.Start();
162+
if (writeMap(mPath, key, *digitizer.getSCDistortions())) {
163+
LOGP(info, "TPC: scaled distortions written to {} in {:.1f} s", mPath, timer.RealTime());
164+
mKey = key;
165+
} else {
166+
LOGP(warning, "TPC: cannot write the scaled distortions to {}", mPath);
167+
}
168+
}
169+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
#ifndef STEER_DIGITIZERWORKFLOW_TPCDISTORTIONMAPFILE_H_
13+
#define STEER_DIGITIZERWORKFLOW_TPCDISTORTIONMAPFILE_H_
14+
15+
#include <functional>
16+
#include <string>
17+
18+
namespace o2
19+
{
20+
namespace framework
21+
{
22+
class ProcessingContext;
23+
}
24+
namespace tpc
25+
{
26+
class Digitizer;
27+
28+
/// File with the TPC distortions scaled to the instantaneous luminosity, shared by the digitizers of a job
29+
/// so that the scaled distortions are computed only once.
30+
///
31+
/// The file is keyed on the ETags of the CCDB distortion maps, TPCCorrMap.lumiInst and the mean-luminosity
32+
/// overrides. A file written for other inputs is replaced. A lock file serialises the digitizers, so that
33+
/// one computes and writes while the others wait and then read.
34+
class TPCDistortionMapFile
35+
{
36+
public:
37+
explicit TPCDistortionMapFile(std::string path) : mPath(std::move(path)) {}
38+
39+
/// Give the digitizer the scaled distortions, from the file or, if it has none for these inputs,
40+
/// by calling compute, which must leave them in the digitizer; they are then written to the file
41+
void prepare(framework::ProcessingContext& pc, Digitizer& digitizer, float meanLumi, float meanLumiDerivative,
42+
const std::function<void()>& compute);
43+
44+
private:
45+
std::string mPath; ///< path of the file
46+
std::string mKey; ///< key of the distortions the digitizer holds
47+
};
48+
49+
} // namespace tpc
50+
} // namespace o2
51+
52+
#endif

0 commit comments

Comments
 (0)