ATLAS Offline Software
Loading...
Searching...
No Matches
OutputConditionsAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5// OutputConditionsAlg.cxx
6// Algorithm to provide simple writing of conditions data to outputstream
7// and optional registration in IOV database
8// Richard Hawkings, started 1/9/05, from skeleton by Walter Lampl
9// Added Crest output, September 2026, Walter Lampl
10
11#include "OutputConditionsAlg.h"
12
13
17#include "SGTools/DataProxy.h"
18
19#include <chai/Container.h>
20#include <chai/Database.h>
21#include <chai/GlobalTag.h>
22#include <chai/PayloadSpec.h>
23
24#include <exception>
25#include <ranges>
26
27
30
31
33 ATH_MSG_DEBUG ("in initialize()");
34
35 // get pointer to ClassIDSvc
36 if (StatusCode::SUCCESS!= p_clidsvc.retrieve()) {
37 ATH_MSG_FATAL ("ClassIDSvc not found");
38 return StatusCode::FAILURE;
39 }
40 if (m_par_writeIOV && m_par_crestDir.empty()) {
41 // get pointer to IOVRegistrationSvc
42 if (StatusCode::SUCCESS!=p_regsvc.retrieve()) {
43 ATH_MSG_FATAL ("IOVRegistrationSvc not found");
44 return StatusCode::FAILURE;
45 }
46 }
47
48 StatusCode sc = m_streamer.retrieve();
49 if (sc.isFailure()) {
50 ATH_MSG_ERROR ("Unable to retrieve AthenaOutputStreamTool " << m_streamer);
51 return StatusCode::FAILURE;
52 }
53
54 if (!m_par_crestDir.empty()) {
55 //CREST mode:
56 ATH_CHECK(m_persSvc.retrieve());
57 //Sanity check of properties, m_object list and m_par_iovtags need to be index parallel
58 if (m_objectList.size() > m_par_iovtags.size()) {
59 ATH_MSG_ERROR("CREST mode: Database tag required for each object");
60 for (size_t i= m_objectList.size()-m_par_iovtags.size();i<m_objectList.size();++i) {
61 ATH_MSG_ERROR (" No database tag set for object " << m_objectList[i]);
62 }
63 return StatusCode::FAILURE;
64 }
65 }
66
67 return StatusCode::SUCCESS;
68}
69
70
71StatusCode OutputConditionsAlg::execute(const EventContext& /*ctx*/) {
72
73 return StatusCode::SUCCESS;
74}
75
77 ATH_MSG_INFO ("Finalize: preparing to write conditions objects ");
78
79 StatusCode sc = m_streamer->connectOutput();
80 if (sc.isFailure()) {
81 ATH_MSG_ERROR ("Could not connect stream to output");
82 return( StatusCode::FAILURE);
83 }
84 // create list of objects
85
86 struct obj_t{
87 std::string type;
88 std::string key;
89 std::string folder;
90 std::string tag;
91 std::string tagDescr;
92 SG::DataProxy* proxy=nullptr;
93 };
94
95 std::vector<obj_t> objs;
96 //Use old-style indexed-based loop because the string-vector properties m_objectList, m_par_iovtags and are index-parallel
97 for (unsigned int iobj = 0; iobj < m_objectList.size(); ++iobj) {
98 //add a new element and keep reference to it
99 obj_t& objt=objs.emplace_back();
100 const std::string& objStr = m_objectList[iobj];
101 objt.tag = iobj < m_par_iovtags.size() ? m_par_iovtags[iobj] : "";
102 objt.tagDescr = iobj < m_par_tagDescr.size() ? m_par_tagDescr[iobj] : "";
103
104 //Object-string definition: typename#key#folder (where key and folder are optional)
105 //split object-string by '#'
106 auto hashSplit = std::ranges::views::split(objStr,std::string_view("#"));
107 for (const auto [iHash, ss] : std::views::enumerate(hashSplit)) {
108 switch(iHash) {
109 case 0:
110 objt.type=std::string_view(ss);
111 break;
112 case 1:
113 objt.key=std::string_view(ss);
114 break;
115 case 2:
116 objt.folder=std::string_view(ss);
117 break;
118 default:
119 ATH_MSG_ERROR("Ill-formed object list " << objStr);
120 ATH_MSG_ERROR("Expect format typename#key#folder (where key and folder are optional)");
121 return StatusCode::FAILURE;
122 }
123 }
124
125 CLID clid;
126 ATH_CHECK(p_clidsvc->getIDOfTypeName(objt.type, clid));
127 if (objt.key.empty()) {
128 //Try to get Key from proxy:
129 objt.proxy = detStore()->proxy(clid);
130 if (!objt.proxy) {
131 ATH_MSG_ERROR("Could not get default proxy for CLID {}, typename {}",clid,objt.type);
132 return StatusCode::FAILURE;
133 }
134 objt.key = objt.proxy->name();
135 }
136 else {
137 objt.proxy = detStore()->proxy(clid, objt.key);
138 if (!objt.proxy) {
139 ATH_MSG_ERROR("Could not get proxy for CLID {}, typename {}, key {}",clid,objt.type,objt.key);
140 return StatusCode::FAILURE;
141 }
142
143 }
144
145 if (objt.folder.empty()) {
146 objt.folder=objt.key;
147 }
148
149 } //end loop over m_objectList
150
151 // list out all typename/key pairs to be written and construct TypeKeyPairs for the streamer
152 const size_t nObjects = objs.size();
154 ATH_MSG_INFO("Identified a total of " << nObjects << " objects to write out:");
155 // leave now if nothing to write
156 if (nObjects == 0)
157 return StatusCode::SUCCESS;
158
159 for (unsigned i=1;const obj_t& objt : objs) {
160 typeKeys.emplace_back(std::make_pair(objt.type, objt.key));
161 ATH_MSG_INFO("{}: {}#{}#{}",i++,objt.type,objt.key,objt.folder);
162 }
163
164
165 // stream output (write objects)
166 sc = m_streamer->streamObjects(typeKeys);
167 if (sc.isFailure()) {
168 ATH_MSG_ERROR("Could not stream out objects");
169 return StatusCode::FAILURE;
170 }
171 // commit output
172 sc = m_streamer->commitOutput();
173 if (sc.isFailure()) {
174 ATH_MSG_ERROR("Could not commit output stream");
175 return StatusCode::FAILURE;
176 }
177 ATH_MSG_INFO("Written {} objects to output stream",nObjects);
178
179 if (m_par_writeIOV) {
180 // ======== COOL writing part (legacy) ===========:
181
182 if (m_par_crestDir.empty()) {
183 msg() << MSG::INFO << "Register objects in IOV database, interval of validity ";
184 if (m_par_timestamp) {
185 msg() << std::format("[time] from [ {} ] to [ {} ]", m_par_time1.value(), m_par_time2.value()) << endmsg;
186 } else {
187 msg() << std::format("[run,LB] from [ {}, {} ] to [ {}, {} ]",m_par_run1.value(),m_par_lumib1.value(),m_par_run2.value(), m_par_lumib2.value()) << endmsg;
188 }
189 int nreg = 0;
190 for (const obj_t& objt : objs) {
191 ATH_MSG_INFO("Register object {}#{} in IOV database folder {} {}", objt.type, objt.key, objt.folder,
192 objt.tag.size() ? "with tag " + objt.tag : "without tagging");
193 if (m_par_timestamp) {
194 sc = p_regsvc->registerIOV(objt.type, objt.key, objt.folder, objt.tag, timeToNano(m_par_time1), timeToNano(m_par_time2));
195 } else {
196 sc = p_regsvc->registerIOV(objt.type, objt.key, objt.folder, objt.tag, m_par_run1, m_par_run2, m_par_lumib1, m_par_lumib2);
197 }
198 if (sc == StatusCode::SUCCESS) {
199 ++nreg;
200 } else {
201 ATH_MSG_ERROR("Registration failed!");
202 }
203 }
204 ATH_MSG_INFO("Registered " << nreg << " objects in COOL IOV database");
205 } else {
206 // ======== CREST writing part ===========:
207
208 ATH_MSG_INFO("Opening crest local directroy " << m_par_crestDir);
209 chai::Database db = chai::Database("crest_fs:" + m_par_crestDir);
210 // Create a global tag. Simplifes reading this local crest dir by athena
211 const std::string globalTagName = "GlobalTestTag";
212 chai::GlobalTagPtr gt;
213 std::vector<std::string> existingGlobalTags;
214 try {
215 existingGlobalTags = db.findGlobalTags(globalTagName);
216 }
217 catch (std::exception& e) {
218 //no global tag exists, not a problem
219 }
220 if (existingGlobalTags.empty()) {
221 gt = db.createGlobalTag("GlobalTestTag", "test", "test");
222 ATH_MSG_INFO("Created Global Tag for testing (" << globalTagName << ") in " << m_par_crestDir);
223 } else {
224 gt = db.getGlobalTag(globalTagName);
225 ATH_MSG_INFO("Global Tag for testing (" << globalTagName << ") exists already in " << m_par_crestDir);
226 }
227
228 // Some of the code here is taken from RegistrationSvc. Once we deprecate COOL, we can also delete (I)RegistrationSvc
229 std::string address_data;
230 // Loop over objects ....
231 for (const obj_t& objt : objs) {
232 chai::TagIovType iovType;
233 uint64_t since;
234 if (m_par_timestamp) {
235 iovType = chai::Tag::IovType::Time;
236 since = timeToNano(m_par_time1);
237 } else {
238 iovType = chai::Tag::IovType::RunNumberLumiBlock;
239 since = (static_cast<std::uint64_t>(m_par_run1.value()) << 32 )+ m_par_lumib1;
240 }
241 const CLID clid = objt.proxy->clID();
242
243 //ATH_MSG_INFO("Working on object clid " << clid << ", " << objt.folder << " " << objt.type << " " << objt.key);
244 ATH_MSG_INFO("Working on object clid {}, type {}, folder {}",clid, objt.type, objt.folder);
245 // First, deal with teh folder description:
246 std::string description;
247 if (clid == 40774348 || clid == 1238547719) {
248 // AthenaAttibuteList or CondAttrListCollection. The description can be build by chai ...
249 description = chai::Tag::buildNodeDescription(iovType, objt.type, clid);
250 } else {
251 // Pool referenced storage, need to build the node description by ourselves
252 IOpaqueAddress* addr = objt.proxy->address();
253 if (!addr) {
254 ATH_MSG_ERROR("No IOpaqueAddress from Type/Key [{}/{}]",objt.type,objt.key);
255 return StatusCode::FAILURE;
256 }
257 std::string saddr;
258 ATH_CHECK(m_persSvc->convertAddress(addr, saddr));
259 // Split the string address into header and data parts
260 std::string address_header;
261
262 if (splitAddress(saddr, address_header, address_data).isFailure()) {
263 ATH_MSG_ERROR("Could not split address: {}\n hdr: {}\n data: {}",saddr,address_header,address_data);
264 return StatusCode::FAILURE;
265 }
266 ATH_MSG_DEBUG("split address: {}\n hdr: {}\n data: {}",saddr,address_header,address_data);
267 // We store extra information in the folder description.
268 // This info is:
269 // typeName - required information
270 // symlinks - the extra StoreGate keys, if any
271 // key - optional, only needed if key != folder name
272 // timeStamp - either run-lumi (default) or time
273 // address_header - added by convention
274 //
275 //
276 // The convention is that the address_header is stored
277 // in the description, and the IOV interval data
278 // payload is just the pool reference in string form.
279 // The address_header and address_data can be obtained
280 // from the string address returned from the
281 // persistency service, using splitAddress
282
283 // Add symlinks (actually base-class types):
284 std::string symLinkTypes;
285 for (const CLID& c : objt.proxy->transientID()) {
286 if (c != clid) {
287 std::string symType;
288 ATH_CHECK(p_clidsvc->getTypeNameOfID(c, symType));
289 if (!symLinkTypes.empty())
290 symLinkTypes += ":"; // Separate type names by colons
291 symLinkTypes.append(symType);
292 }
293 } // end loop over transientIDs
294 if (!symLinkTypes.empty()) {
295 buildDescription("symLink", symLinkTypes, description);
296 }
297
298 // IOV type:
299 if (iovType == chai::Tag::IovType::Time) {
300 buildDescription("timeStamp", "time", description);
301 } else {
302 buildDescription("timeStamp", "run-lumi", description);
303 }
304
305 // Address header:
306 buildDescription("addrHeader", address_header, description);
307
308 // SG key if needed:
309 if (objt.key != objt.folder) {
310 buildDescription("key", objt.key, description);
311 }
312 } // end if POOL referenced storage
313 static const std::string crestObjTypeStr{"crest-json-single-iov"};
314 chai::Tag::Metadata chaiMD{.iovType = iovType,
315 .objectType = crestObjTypeStr,
316 .synchronization = chai::Tag::Synchronization::All,
317 .status = chai::Tag::Status::Unlocked,
318 .nodeDescription = std::move(description)};
319
320
321
322 // Description and metadat ready, now deal with the payload
323 IOpaqueAddress* addr = objt.proxy->address();
324
325 CondAttrListCollAddress* collAddr = dynamic_cast<CondAttrListCollAddress*>(addr);
326 if (collAddr) {
327
328 // Multi-channel inline storage or multi-channel POOL storage
329 const CondAttrListCollection* attrListColl = collAddr->attrListColl();
330 // convert to multi-channel chai::container and ....
331 chai::Container chaiCont=ChaiCoralConverter::toContainer(*attrListColl);
332 ATH_MSG_INFO("Created chai container with " << chaiCont.numChannels() << " channels.");
333 const chai::PayloadSpec& chaiSpec=chaiCont.payloadSpec();
334 auto chaiTag=db.createTag(objt.tag, objt.tagDescr, chaiSpec,chaiMD);
335 chaiTag->addPayload(chaiCont,since);
336
337
338 } else if (clid == 40774348) {
339 // Attribute list (single-channel inline).. retrieve from detStore
340
341 const AthenaAttributeList* attrList;
342 ATH_CHECK(detStore()->retrieve(attrList, objt.key));
343 // convert to single-channel chai::container
344 chai::Container chaiCont=ChaiCoralConverter::toContainer(*attrList);
345 const chai::PayloadSpec& chaiSpec=chaiCont.payloadSpec();
346 auto chaiTag=db.createTag(objt.tag, objt.tagDescr, chaiSpec,chaiMD);
347 gt->addTag(objt.folder,objt.tag);
348 chaiTag->addPayload(chaiCont,since);
349 } else {
350
351 //Single channel pool storage case:
352 chai::PayloadSpec spec(chai::FieldSpec({{"PoolRef", chai::Type::String}}), chai::ChannelSpec({{0, ""}}));
353 auto tag = db.createTag(objt.tag, objt.tagDescr, spec,chaiMD);
354 gt->addTag(objt.folder, objt.tag);
355 chai::Container container = tag->buildContainer();
356 container[0].push(address_data);
357 //With chai 3.0.0 the line above becomes:
358 //container.set(0,"PoolRef",address_data);
359 tag->addPayload(container, since);
360
361 } // end else single-channel pool storage
362 } // end loop over objects
363 ATH_MSG_INFO("Registered " << objs.size() << " objects in CREST IOV database");
364 } // end if write crest
365 } // end doWriteIOV
366 else {
367 ATH_MSG_INFO("Objects NOT registered in IOV database");
368 }
369 return StatusCode::SUCCESS;
370}
371
372uint64_t OutputConditionsAlg::timeToNano(unsigned long int timesec) const
373{
374 // convert time specified in seconds to ns used by COOL
375 // use the magic value MAXEVENT to signal full range
376 if (timesec==IOVTime::MAXEVENT) {
378 } else {
379 return static_cast<uint64_t>(timesec)*1000000000;
380 }
381}
382
383void OutputConditionsAlg::buildDescription(const std::string& identifier, const std::string& value, std::string& description) const {
384
385 std::string fragment="<"+identifier+">"+value+"<\\"+identifier+">";
386 description.insert(0,fragment);
387 return;
388}
389
390StatusCode OutputConditionsAlg::splitAddress(const std::string& address, std::string& address_header, std::string& address_data) const {
391 // Deals with address of form
392 // <address_header service_type="256" clid="1238547719" /> POOLContainer_CondAttrListCollection][CLID=x
393 // return header as part up to and including />, trailer as rest
394
395 std::string::size_type p1 = address.find(" />");
396 if (p1 != std::string::npos) {
397 address_header = address.substr(0, p1 + 3);
398 address_data = address.substr(p1 + 4);
399 return StatusCode::SUCCESS;
400 } else {
401 return StatusCode::FAILURE;
402 }
403}
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_INFO(x,...)
#define ATH_MSG_FATAL(x,...)
Converts between CHAI payload containers and CORAL objects.
This file contains the class definition for the CondAttrListCollAddress class.
uint32_t CLID
The Class ID type.
static Double_t ss
static Double_t sc
An AttributeList represents a logical row of attributes in a metadata table.
const ServiceHandle< StoreGateSvc > & detStore() const
An AttributeList represents a logical row of attributes in a metadata table.
static chai::Container toContainer(const AthenaAttributeList &attrList, uint64_t channelId=0, const std::string &channelName="")
Convert an AthenaAttributeList to a single-channel chai::Container.
This class provides an IOpaqueAddress/GenericAddress which can hold a pointer to a CondAttrListCollec...
CondAttrListCollection * attrListColl()
Access to AttributeList.
This class is a collection of AttributeLists where each one is associated with a channel number.
std::vector< TypeKeyPair > TypeKeyPairs
static constexpr uint64_t MAXTIMESTAMP
Definition IOVTime.h:59
static constexpr uint32_t MAXEVENT
Definition IOVTime.h:52
uint64_t timeToNano(unsigned long int timesec) const
UnsignedLongProperty m_par_time2
ServiceHandle< IClassIDSvc > p_clidsvc
StatusCode execute(const EventContext &ctx)
Execute method.
Gaudi::Property< std::vector< std::string > > m_par_tagDescr
Gaudi::Property< bool > m_par_writeIOV
void buildDescription(const std::string &identifier, const std::string &value, std::string &description) const
ServiceHandle< IIOVRegistrationSvc > p_regsvc
Gaudi::Property< unsigned int > m_par_lumib2
Gaudi::Property< std::vector< std::string > > m_par_iovtags
Gaudi::Property< bool > m_par_timestamp
ToolHandle< IAthenaOutputStreamTool > m_streamer
StatusCode splitAddress(const std::string &address, std::string &address_header, std::string &address_data) const
ServiceHandle< IAddressCreator > m_persSvc
Gaudi::Property< std::vector< std::string > > m_objectList
Gaudi::Property< unsigned int > m_par_run2
UnsignedLongProperty m_par_time1
Gaudi::Property< unsigned int > m_par_lumib1
Gaudi::Property< unsigned int > m_par_run1
Gaudi::Property< std::string > m_par_crestDir
std::string description
glabal timer - how long have I taken so far?
Definition hcg.cxx:93