The Gaudi Framework  master (e68eea06)
Loading...
Searching...
No Matches
RCWNTupleCnv.cpp
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 1998-2025 CERN for the benefit of the LHCb and ATLAS collaborations *
3* *
4* This software is distributed under the terms of the Apache version 2 licence, *
5* copied verbatim in the file "LICENSE". *
6* *
7* In applying this licence, CERN does not waive the privileges and immunities *
8* granted to it by virtue of its status as an Intergovernmental Organization *
9* or submit itself to any jurisdiction. *
10\***********************************************************************************/
11#define ROOTHISTCNV_RCWNTUPLECNV_CPP
12
13// Include files
16#include <GaudiKernel/NTuple.h>
17
18// Compiler include files
19#include <cstdio>
20#include <cstring>
21#include <list>
22#include <type_traits>
23#include <utility>
24#include <vector>
25
26#include "RCWNTupleCnv.h"
27
28#include <TLeafD.h>
29#include <TLeafF.h>
30#include <TLeafI.h>
31#include <TTree.h>
32
33namespace {
34 template <typename T>
35 size_t saveItem( char* target, const NTuple::_Data<T>& src ) {
36 static_assert( std::is_trivially_copyable_v<T>, "T must be trivally copyable" );
37 std::memcpy( target, src.buffer(), sizeof( T ) * src.length() );
38 return sizeof( T ) * src.length();
39 }
40
41 template <typename T>
42 size_t loadItem( const char* src, NTuple::_Data<T>& target ) {
43 static_assert( std::is_trivially_copyable_v<T>, "T must be trivally copyable" );
44 std::memcpy( const_cast<void*>( target.buffer() ), src, sizeof( T ) * target.length() );
45 return sizeof( T ) * target.length();
46 }
47
48 template <typename POD>
49 decltype( auto ) downcast_item( const INTupleItem& i ) {
50 return dynamic_cast<const NTuple::_Data<POD>&>( i );
51 }
52 template <typename POD>
53 decltype( auto ) downcast_item( INTupleItem& i ) {
54 return dynamic_cast<NTuple::_Data<POD>&>( i );
55 }
56 template <typename POD, typename T>
57 void downcast_item( T&& ) = delete;
58
59 template <typename Item, typename F>
60 decltype( auto ) visit( Item& i, F&& f ) {
61 switch ( i.type() ) {
63 return f( downcast_item<int>( i ) );
65 return f( downcast_item<char>( i ) );
67 return f( downcast_item<short>( i ) );
69 return f( downcast_item<long>( i ) );
71 return f( downcast_item<long long>( i ) );
73 return f( downcast_item<unsigned char>( i ) );
75 return f( downcast_item<unsigned short>( i ) );
77 return f( downcast_item<unsigned int>( i ) );
79 return f( downcast_item<unsigned long>( i ) );
81 return f( downcast_item<unsigned long long>( i ) );
83 return f( downcast_item<double>( i ) );
85 return f( downcast_item<float>( i ) );
87 return f( downcast_item<bool>( i ) );
88 }
89 throw std::runtime_error( "RCWNTupleCnv::visit: unknown INTupleItem::type()" );
90 }
91
92 //-----------------------------------------------------------------------------
93 template <class T>
94 void analyzeItem( const std::string& typ, const NTuple::_Data<T>* it, std::string& desc, std::string& block_name,
95 std::string& var_name, long& lowerRange, long& upperRange, long& size )
96 //-----------------------------------------------------------------------------
97 {
98
99 RootHistCnv::parseName( it->name(), block_name, var_name );
100
101 // long item_size = (sizeof(T) < 4) ? 4 : sizeof(T);
102 long item_size = sizeof( T );
103 long dimension = it->length();
104 long ndim = it->ndim() - 1;
105 desc += var_name;
106 if ( it->hasIndex() || it->length() > 1 ) { desc += '['; }
107 if ( it->hasIndex() ) {
108 std::string ind_blk, ind_var;
109 RootHistCnv::parseName( it->index(), ind_blk, ind_var );
110 if ( ind_blk != block_name ) {
111 std::cerr << "ERROR: Index for CWNT variable " << ind_var << " is in a different block: " << ind_blk
112 << std::endl;
113 }
114 desc += ind_var;
115 } else if ( it->dim( ndim ) > 1 ) {
116 desc += std::to_string( it->dim( ndim ) );
117 }
118
119 for ( int i = ndim - 1; i >= 0; i-- ) {
120 desc += "][";
121 desc += std::to_string( it->dim( i ) );
122 }
123 if ( it->hasIndex() || it->length() > 1 ) { desc += ']'; }
124
125 // 0 and -1 are used to mark that the range is not defined
126 lowerRange = 0;
127 upperRange = -1;
128 if constexpr ( std::is_integral_v<T> ) {
129 // An explicit range makes sense only for integral types so we check only in that case if it is defined.
130 // Note that later a range is only taken into account for int32_t.
131 if ( it->range().lower() != it->range().min() && it->range().upper() != it->range().max() ) {
132 lowerRange = it->range().lower();
133 upperRange = it->range().upper();
134 }
135 }
136
137 desc += typ;
138 size += item_size * dimension;
139 }
140} // namespace
141
142//-----------------------------------------------------------------------------
143StatusCode RootHistCnv::RCWNTupleCnv::book( const std::string& desc, INTuple* nt, TTree*& rtree )
144//-----------------------------------------------------------------------------
145{
146 MsgStream log( msgSvc(), "RCWNTupleCnv" );
147 rtree = new TTree( desc.c_str(), nt->title().c_str() );
148 log << MSG::VERBOSE << "created tree id: " << rtree->GetName() << " title: " << nt->title() << " desc: " << desc
149 << endmsg;
150
151 // Loop over the items
152
153 std::string block_name, var_name;
154 long lowerRange, upperRange;
155 long size = 0;
156 long cursize, oldsize = 0;
157 std::vector<std::string> item_fullname;
158 // std::vector<long> item_size,item_size2;
159 std::vector<long> item_buf_pos, item_buf_len, item_buf_end;
160 std::vector<long> item_range_lower, item_range_upper;
161 std::vector<std::pair<std::string, std::string>> item_name;
162
163 for ( const auto& i : nt->items() ) {
164 std::string item;
165
166 visit( *i, [&]( const auto& data ) {
167 analyzeItem( this->rootVarType( data.type() ), &data, item, block_name, var_name, lowerRange, upperRange, size );
168 } );
169
170 item_name.emplace_back( block_name, item );
171 cursize = size - oldsize;
172
173 log << MSG::VERBOSE << "item: " << item << " type " << i->type() << " blk: " << block_name << " var: " << var_name
174 << " rng: " << lowerRange << " " << upperRange << " sz: " << size << " " << cursize
175 << " buf_pos: " << size - cursize << endmsg;
176
177 item_fullname.push_back( var_name );
178 item_buf_pos.push_back( size - cursize );
179 item_buf_len.push_back( cursize );
180 item_buf_end.push_back( size );
181 item_range_lower.push_back( lowerRange );
182 item_range_upper.push_back( upperRange );
183
184 oldsize = size;
185 }
186
187 // Make a new buffer, and tell the ntuple where it is
188 char* buff = nt->setBuffer( new char[size] );
189
190 log << MSG::VERBOSE << "Created buffer size: " << size << " at " << (void*)buff << endmsg;
191
192 // Zero out the buffer to make ROOT happy
193 std::fill_n( buff, size, 0 );
194
195 char* buf_pos = buff;
196
197 auto end = item_name.cend();
198
199 // Loop over items, creating a new branch for each one;
200 unsigned int i_item = 0;
201
202 Gaudi::Property<int> basket_size( "BasketSize", 32000 );
203 m_ntupleSvc.as<IProperty>()->getProperty( &basket_size ).ignore();
204
205 for ( auto itr = item_name.cbegin(); itr != end; ++itr, ++i_item ) {
206
207 buf_pos = buff + item_buf_pos[i_item];
208
209 // log << MSG::WARNING << "adding TBranch " << i_item << " "
210 // << item_fullname[i_item]
211 // << " format: " << itr->second.c_str() << " at "
212 // << (void*) buf_pos << " (" << (void*) buff << "+"
213 // << (void*)item_buf_pos[i_item] << ")"
214 // << endmsg;
215
216 auto br = new TBranch( rtree, item_fullname[i_item].c_str(), buf_pos, itr->second.c_str(), basket_size );
217 if ( itr->first != "AUTO_BLK" ) {
218 std::string title = itr->first;
219 title = itr->first + "::" + br->GetTitle();
220 br->SetTitle( title.c_str() );
221 }
222
223 log << MSG::DEBUG << "adding TBranch " << br->GetTitle() << " at " << (void*)buf_pos << endmsg;
224
225 // for index items with a limited range. Must be a TLeafI!
226 if ( item_range_lower[i_item] < item_range_upper[i_item] ) {
227 // log << MSG::VERBOSE << "\"" << item_fullname[i_item]
228 // << "\" is range limited " << item_range_lower[i_item] << " "
229 // << item_range_upper[i_item] << endmsg;
230 TLeafI* index = nullptr;
231 TObject* tobj = br->GetListOfLeaves()->FindObject( item_fullname[i_item].c_str() );
232 if ( tobj->IsA()->InheritsFrom( "TLeafI" ) ) {
233 index = dynamic_cast<TLeafI*>( tobj );
234
235 if ( index ) {
236 index->SetMaximum( item_range_upper[i_item] );
237 // FIXME -- add for next version of ROOT
238 // index->SetMinimum( item_range_lower[i_item] );
239 } else {
240 log << MSG::ERROR << "Could dynamic cast to TLeafI: " << item_fullname[i_item] << endmsg;
241 }
242 }
243 }
244
245 rtree->GetListOfBranches()->Add( br );
246 }
247
248 log << MSG::INFO << "Booked TTree with ID: " << desc << " \"" << nt->title() << "\" in directory " << getDirectory()
249 << endmsg;
250
251 return StatusCode::SUCCESS;
252}
253
254// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
255
256//-----------------------------------------------------------------------------
258//-----------------------------------------------------------------------------
259{
260 // Fill the tree;
261 const auto& items = nt->items();
262 std::accumulate( begin( items ), end( items ), nt->buffer(), []( char* dest, const INTupleItem* i ) {
263 return dest + visit( *i, [dest]( const auto& item ) { return saveItem( dest, item ); } );
264 } );
265
266 rtree->Fill();
267 nt->reset();
268 return StatusCode::SUCCESS;
269}
270
271//-----------------------------------------------------------------------------
273//-----------------------------------------------------------------------------
274{
275 if ( ievt >= rtree->GetEntries() ) {
276 MsgStream log( msgSvc(), "RCWNTupleCnv::readData" );
277 log << MSG::ERROR << "no more entries in tree to read. max: " << rtree->GetEntries() << " current: " << ievt
278 << endmsg;
279 return StatusCode::FAILURE;
280 }
281
282 rtree->GetEvent( ievt );
283 ievt++;
284
285 // copy data from ntup->buffer() to ntup->items()->buffer()
286 auto& items = ntup->items();
287 std::accumulate( begin( items ), end( items ), const_cast<const char*>( ntup->buffer() ),
288 []( const char* src, INTupleItem* i ) {
289 return src + visit( *i, [src]( auto& item ) { return loadItem( src, item ); } );
290 } );
291
292 return StatusCode::SUCCESS;
293}
294
295//-----------------------------------------------------------------------------
297//-----------------------------------------------------------------------------
298{
299 MsgStream log( msgSvc(), "RCWNTupleCnv::load" );
300
301 StatusCode status;
302
303 NTuple::Tuple* pObj = nullptr;
304
305 std::string title = tree->GetTitle();
306 log << MSG::VERBOSE << "loading CWNT " << title << " at: " << tree << endmsg;
307
308 status = m_ntupleSvc->create( CLID_ColumnWiseTuple, title, pObj );
309 INTuple* ntup = dynamic_cast<INTuple*>( pObj );
310 if ( !ntup ) { log << MSG::ERROR << "cannot dynamic cast to INTuple" << endmsg; }
311
312 INTupleItem* item = nullptr;
313
314 std::string itemName, indexName, item_type, itemTitle, blockName;
315 // long numEnt, numVar;
316 long size, totsize = 0;
317 std::vector<std::pair<TLeaf*, int>> itemList;
318
319 // numEnt = (int)tree->GetEntries();
320 // numVar = tree->GetNbranches();
321
322 // loop over all branches (==leaves)
323 TObjArray* lbr = tree->GetListOfBranches();
324 TIter bitr( lbr );
325 while ( TObject* tobjb = bitr() ) {
326
327 TBranch* br = dynamic_cast<TBranch*>( tobjb );
328 itemTitle = br->GetTitle();
329
330 int ipos = itemTitle.find( "::" );
331 if ( ipos >= 0 ) {
332 blockName = itemTitle.substr( 0, ipos );
333 } else {
334 blockName = "";
335 }
336
337 TObjArray* lf = br->GetListOfLeaves();
338
339 TIter litr( lf );
340 while ( TObject* tobj = litr() ) {
341
342 bool hasRange = false;
343 int indexRange = 0;
344 int itemSize;
345 item = nullptr;
346
347 TLeaf* tl = dynamic_cast<TLeaf*>( tobj );
348 if ( !tl ) {
349 log << MSG::ERROR << "cannot dynamic cast to TLeaf" << endmsg;
350 return StatusCode::FAILURE;
351 }
352 itemName = tl->GetName();
353
354 if ( blockName != "" ) {
355 log << MSG::DEBUG << "loading NTuple item " << blockName << "/" << itemName;
356 } else {
357 log << MSG::DEBUG << "loading NTuple item " << itemName;
358 }
359
360 int arraySize{ 0 };
361 TLeaf* indexLeaf = tl->GetLeafCounter( arraySize );
362
363 if ( arraySize == 0 ) { log << MSG::ERROR << "TLeaf counter size = 0. This should not happen!" << endmsg; }
364
365 if ( indexLeaf ) {
366 // index Arrays and Matrices
367
368 indexName = indexLeaf->GetName();
369 indexRange = indexLeaf->GetMaximum();
370 itemSize = indexRange * tl->GetLenType() * arraySize;
371
372 log << "[" << indexName;
373
374 // Just for Matrices
375 if ( arraySize != 1 ) { log << "][" << arraySize; }
376 log << "]";
377
378 } else {
379 itemSize = tl->GetLenType() * arraySize;
380
381 indexName = "";
382
383 if ( arraySize == 1 ) {
384 // Simple items
385 } else {
386 // Arrays of constant size
387 log << "[" << arraySize << "]";
388 }
389 }
390
391 log << endmsg;
392
393 size = itemSize;
394 totsize += size;
395
396 hasRange = tl->IsRange();
397
398 itemList.emplace_back( tl, itemSize );
399
400 // Integer
401 if ( tobj->IsA()->InheritsFrom( "TLeafI" ) ) {
402
403 TLeafI* tli = dynamic_cast<TLeafI*>( tobj );
404 if ( tli ) {
405 if ( tli->IsUnsigned() ) {
406 unsigned long min = 0, max = 0;
407 if ( hasRange ) {
408 min = tli->GetMinimum();
409 max = tli->GetMaximum();
410 }
411
412 item = createNTupleItem( itemName, blockName, indexName, indexRange, arraySize, min, max, ntup, hasRange );
413 } else {
414 long min = 0, max = 0;
415 if ( hasRange ) {
416 min = tli->GetMinimum();
417 max = tli->GetMaximum();
418 }
419
420 item = createNTupleItem( itemName, blockName, indexName, indexRange, arraySize, min, max, ntup, hasRange );
421 }
422 } else {
423 log << MSG::ERROR << "cannot dynamic cast to TLeafI" << endmsg;
424 }
425
426 // Float
427 } else if ( tobj->IsA()->InheritsFrom( "TLeafF" ) ) {
428 float min = 0., max = 0.;
429
430 TLeafF* tlf = dynamic_cast<TLeafF*>( tobj );
431 if ( tlf ) {
432 if ( hasRange ) {
433 min = float( tlf->GetMinimum() );
434 max = float( tlf->GetMaximum() );
435 }
436 } else {
437 log << MSG::ERROR << "cannot dynamic cast to TLeafF" << endmsg;
438 }
439
440 item = createNTupleItem( itemName, blockName, indexName, indexRange, arraySize, min, max, ntup, hasRange );
441
442 // Double
443 } else if ( tobj->IsA()->InheritsFrom( "TLeafD" ) ) {
444 double min = 0., max = 0.;
445
446 TLeafD* tld = dynamic_cast<TLeafD*>( tobj );
447 if ( tld ) {
448 if ( hasRange ) {
449 min = tld->GetMinimum();
450 max = tld->GetMaximum();
451 }
452 } else {
453 log << MSG::ERROR << "cannot dynamic cast to TLeafD" << endmsg;
454 }
455
456 item = createNTupleItem( itemName, blockName, indexName, indexRange, arraySize, min, max, ntup, hasRange );
457
458 } else {
459 log << MSG::ERROR << "Uknown data type" << endmsg;
460 }
461
462 if ( item ) {
463 ntup->add( item ).ignore();
464 } else {
465 log << MSG::ERROR << "Unable to create ntuple item \"" << itemName << "\"" << endmsg;
466 }
467
468 } // end litr
469 } // end bitr
470
471 log << MSG::DEBUG << "Total buffer size of NTuple: " << totsize << " Bytes." << endmsg;
472
473 char* buf = ntup->setBuffer( new char[totsize] );
474 char* bufpos = buf;
475
476 int ts = 0;
477 for ( const auto& iitr : itemList ) {
478 TLeaf* leaf = iitr.first;
479 int isize = iitr.second;
480
481 log << MSG::VERBOSE << "setting TBranch " << leaf->GetBranch()->GetName() << " buffer at " << (void*)bufpos
482 << endmsg;
483
484 leaf->GetBranch()->SetAddress( (void*)bufpos );
485
486 // //testing
487 // if (leaf->IsA()->InheritsFrom("TLeafI")) {
488 // for (int ievt=0; ievt<5; ievt++) {
489 // leaf->GetBranch()->GetEvent(ievt);
490 // int *idat = (int*)bufpos;
491 // log << MSG::WARNING << leaf->GetName() << ": " << ievt << " "
492 // << *idat << endmsg;
493
494 // }
495 // }
496
497 ts += isize;
498
499 bufpos += isize;
500 }
501
502 if ( totsize != ts ) { log << MSG::ERROR << "buffer size mismatch: " << ts << " " << totsize << endmsg; }
503
504 refpObject = ntup;
505
506 return StatusCode::SUCCESS;
507}
508
509// Instantiation of a static factory class used by clients to create
510// instances of this service
const char *PyHelper getProperty(IInterface *p, char *name)
#define DECLARE_CONVERTER(x)
Definition Converter.h:142
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition MsgStream.h:198
SmartIF< IMessageSvc > & msgSvc() const
Retrieve pointer to message service.
Implementation of property with value of concrete type.
Definition PropertyFwd.h:27
NTuple interface class definition.
Definition INTuple.h:86
virtual const char * buffer() const =0
Access data buffer (CONST)
virtual char * setBuffer(char *buff)=0
Attach data buffer.
virtual ItemContainer & items()=0
Access item container.
virtual StatusCode add(INTupleItem *item)=0
Add an item row to the N tuple.
virtual const std::string & title() const =0
Object title.
NTuple interface class definition.
Definition INTuple.h:32
virtual long ndim() const =0
Dimension.
virtual const void * buffer() const =0
Access data buffer (CONST)
virtual long dim(long i) const =0
Access individual dimensions.
virtual const std::string & index() const =0
Access the index _Item.
virtual long type() const =0
Type information of the item.
virtual long length() const =0
Access the buffer length.
virtual const std::string & name() const =0
Access _Item name.
virtual bool hasIndex() const =0
Is the tuple have an index item?
The IProperty is the basic interface for all components which have properties that can be set or get.
Definition IProperty.h:32
Definition of the MsgStream class used to transmit messages.
Definition MsgStream.h:29
Abstract class describing basic data in an Ntuple.
Definition NTuple.h:122
virtual const ItemRange & range() const =0
Access the range if specified.
static TYP min()
Minimal number of data.
Definition NTuple.h:84
TYP lower() const
Lower boundary of range.
Definition NTuple.h:78
TYP upper() const
Upper boundary of range.
Definition NTuple.h:80
static TYP max()
Maximal number of data.
Definition NTuple.h:86
Abstract base class which allows the user to interact with the actual N tuple implementation.
Definition NTuple.h:380
Converter of Column-wise NTuple into ROOT format.
StatusCode load(TTree *tree, INTuple *&refpObject) override
Create the transient representation of an object.
StatusCode book(const std::string &desc, INTuple *pObject, TTree *&tree) override
Book the N tuple.
StatusCode readData(TTree *rtree, INTuple *pObject, long ievt) override
Read N tuple data.
StatusCode writeData(TTree *rtree, INTuple *pObject) override
Write N tuple data.
std::string getDirectory()
virtual std::string rootVarType(int)
Return ROOT type info:
SmartIF< INTupleSvc > m_ntupleSvc
Reference to N tuple service.
Definition RNTupleCnv.h:63
This class is used for returning status codes from appropriate routines.
Definition StatusCode.h:64
const StatusCode & ignore() const
Allow discarding a StatusCode without warning.
Definition StatusCode.h:139
constexpr static const auto SUCCESS
Definition StatusCode.h:99
constexpr static const auto FAILURE
Definition StatusCode.h:100
@ DEBUG
Definition IMessageSvc.h:22
@ ERROR
Definition IMessageSvc.h:22
@ INFO
Definition IMessageSvc.h:22
@ VERBOSE
Definition IMessageSvc.h:22
bool parseName(const std::string &full, std::string &blk, std::string &var)
INTupleItem * createNTupleItem(const std::string &itemName, const std::string &blockName, const std::string &indexName, int indexRange, int arraySize, TYP min, TYP max, INTuple *ntup, bool hasRange)
Add an item of a given type to the N tuple.