ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/grimes/L1Menu/src/ReducedMenuSample.cpp
Revision: 1.13
Committed: Fri Jun 28 14:30:08 2013 UTC (11 years, 10 months ago) by grimes
Branch: MAIN
Changes since 1.12: +111 -50 lines
Log Message:
Added ICachedTrigger to speed up processing

File Contents

# Content
1 #include "l1menu/ReducedMenuSample.h"
2
3 #include <vector>
4 #include <stdexcept>
5 #include <fcntl.h>
6 #include <algorithm>
7 #include <iostream>
8 #include <sstream>
9 #include "l1menu/ReducedEvent.h"
10 #include "l1menu/MenuSample.h"
11 #include "l1menu/TriggerMenu.h"
12 #include "l1menu/ITrigger.h"
13 #include "l1menu/ICachedTrigger.h"
14 #include "l1menu/L1TriggerDPGEvent.h"
15 #include "l1menu/IEvent.h"
16 #include "l1menu/IMenuRate.h"
17 #include "l1menu/ITriggerRate.h"
18 #include "l1menu/tools/tools.h"
19 #include "protobuf/l1menu.pb.h"
20 #include <google/protobuf/io/zero_copy_stream_impl.h>
21 #include <google/protobuf/io/gzip_stream.h>
22
23 namespace // unnamed namespace
24 {
25 /** @brief Sentry that closes a Unix file descriptor when it goes out of scope.
26 * @author Mark Grimes (mark.grimes@bristol.ac.uk)
27 * @date 07/Jun/2013
28 */
29 class UnixFileSentry
30 {
31 public:
32 UnixFileSentry( int fileDescriptor ) : fileDescriptor_(fileDescriptor) {}
33 ~UnixFileSentry() { close(fileDescriptor_); }
34 private:
35 int fileDescriptor_;
36 };
37
38 class MenuRateImplementation;
39
40 class TriggerRateImplementation : public l1menu::ITriggerRate
41 {
42 public:
43 TriggerRateImplementation( const l1menu::ITrigger& trigger, float fraction, const MenuRateImplementation& menuRate );
44 TriggerRateImplementation& operator=( TriggerRateImplementation&& otherTriggerRate ); // Move assignment
45 virtual ~TriggerRateImplementation();
46
47 // Methods required by the l1menu::ITriggerRate interface
48 virtual const l1menu::ITrigger& trigger() const;
49 virtual float fraction() const;
50 virtual float rate() const;
51 protected:
52 std::unique_ptr<l1menu::ITrigger> pTrigger_;
53 float fraction_;
54 const MenuRateImplementation& menuRate_;
55 };
56
57 class MenuRateImplementation : public l1menu::IMenuRate
58 {
59 public:
60 void setTotalFraction( float fraction ) { totalFraction_=fraction; }
61 void setScaling( float scaling ) { scaling_=scaling; }
62 float scaling() const { return scaling_; }
63 void addTriggerRate( const l1menu::ITrigger& trigger, float fraction ) { triggerRates_.push_back( std::move(TriggerRateImplementation(trigger,fraction,*this)) ); }
64 // Methods required by the l1menu::IMenuRate interface
65 virtual float totalFraction() const { return totalFraction_; }
66 virtual float totalRate() const { return totalFraction_*scaling_; }
67 virtual const std::vector<const l1menu::ITriggerRate*>& triggerRates() const
68 {
69 // If the sizes are the same I'll assume nothing has changed and the references
70 // are still valid. I don't expect this method to be called until the triggerRates_
71 // vector is complete anyway.
72 if( triggerRates_.size()!=baseClassReferences_.size() )
73 {
74 baseClassReferences_.clear();
75 for( const auto& triggerRate : triggerRates_ )
76 {
77 baseClassReferences_.push_back( &triggerRate );
78 }
79 }
80
81 return baseClassReferences_;
82 }
83
84 protected:
85 float totalFraction_;
86 float scaling_;
87 std::vector<TriggerRateImplementation> triggerRates_;
88 mutable std::vector<const l1menu::ITriggerRate*> baseClassReferences_;
89 };
90
91 /** @brief An object that stores pointers to trigger parameters to avoid costly string comparisons.
92 *
93 * @author Mark Grimes (mark.grimes@bristol.ac.uk)
94 * @date 26/Jun/2013
95 */
96 class CachedTriggerImplementation : public l1menu::ICachedTrigger
97 {
98 public:
99 CachedTriggerImplementation( const l1menu::ReducedMenuSample& sample, const l1menu::ITrigger& trigger )
100 {
101 const auto& parameterIdentifiers=sample.getTriggerParameterIdentifiers(trigger);
102
103 for( const auto& identifier : parameterIdentifiers )
104 {
105 identifiers_.push_back( std::make_pair( identifier.second, &trigger.parameter(identifier.first) ) );
106 }
107 }
108 virtual bool apply( const l1menu::IEvent& event )
109 {
110 // Not happy using a static_cast, but this method is called in many, many loops.
111 // I should probably find out how much faster this is than a dynamic_cast, maybe
112 // it's not even worth it. Anyway, I'm banking that no one will ever pass an
113 // event that wasn't created with the same sample that this proxy was created
114 // with.
115 const l1menu::ReducedEvent* pEvent=static_cast<const l1menu::ReducedEvent*>(&event);
116 for( const auto& identifier : identifiers_ )
117 {
118 if( pEvent->parameterValue(identifier.first) < *identifier.second ) return false;
119 }
120
121 // If control got this far then all of the thresholds passed, and
122 // I can pass the event.
123 return true;
124 }
125 protected:
126 std::vector< std::pair<l1menu::ReducedEvent::ParameterID,const float*> > identifiers_;
127 }; // end of class ReducedSampleCachedTrigger
128
129 TriggerRateImplementation::TriggerRateImplementation( const l1menu::ITrigger& trigger, float fraction, const MenuRateImplementation& menuRate )
130 : fraction_(fraction), menuRate_(menuRate)
131 {
132 pTrigger_=std::move( l1menu::TriggerTable::instance().copyTrigger(trigger) );
133 }
134 TriggerRateImplementation& TriggerRateImplementation::operator=( TriggerRateImplementation&& otherTriggerRate )
135 {
136 pTrigger_=std::move( otherTriggerRate.pTrigger_ );
137 fraction_=otherTriggerRate.fraction_;
138 // I can't change the menuRate_ reference, but that should already be set to the right one anyway.
139 return *this;
140 }
141 TriggerRateImplementation::~TriggerRateImplementation() {}
142 const l1menu::ITrigger& TriggerRateImplementation::trigger() const { return *pTrigger_; }
143 float TriggerRateImplementation::fraction() const { return fraction_; }
144 float TriggerRateImplementation::rate() const { return fraction_*menuRate_.scaling(); }
145
146 float sumWeights( const l1menuprotobuf::Run& run )
147 {
148 float returnValue=0;
149 for( const auto& event : run.event() )
150 {
151 if( event.has_weight() ) returnValue+=event.weight();
152 else returnValue+=1;
153 }
154 return returnValue;
155 }
156 }
157
158 namespace l1menu
159 {
160 /** @brief Private members for the ReducedMenuSample class
161 *
162 * @author Mark Grimes (mark.grimes@bristol.ac.uk)
163 * @date 28/May/2013
164 */
165 class ReducedMenuSamplePrivateMembers
166 {
167 private:
168 l1menu::TriggerMenu mutableTriggerMenu_;
169 public:
170 ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const l1menu::TriggerMenu& newTriggerMenu );
171 ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const std::string& filename );
172 //void copyMenuToProtobufSample();
173 l1menu::ReducedEvent event;
174 const l1menu::TriggerMenu& triggerMenu; // External const access to mutableTriggerMenu_
175 float eventRate;
176 float sumOfWeights;
177 l1menuprotobuf::SampleHeader protobufSampleHeader;
178 // Protobuf doesn't implement move semantics so I'll use pointers
179 std::vector<std::unique_ptr<l1menuprotobuf::Run> > protobufRuns;
180 const static int EVENTS_PER_RUN;
181 const static char PROTOBUF_MESSAGE_DELIMETER;
182 const static std::string FILE_FORMAT_MAGIC_NUMBER;
183 };
184
185 const int ReducedMenuSamplePrivateMembers::EVENTS_PER_RUN=20000;
186 const char ReducedMenuSamplePrivateMembers::PROTOBUF_MESSAGE_DELIMETER='\n';
187 const std::string ReducedMenuSamplePrivateMembers::FILE_FORMAT_MAGIC_NUMBER="l1menuReducedMenuSample";
188 }
189
190 l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const l1menu::TriggerMenu& newTriggerMenu )
191 : mutableTriggerMenu_( newTriggerMenu ), event(thisObject), triggerMenu( mutableTriggerMenu_ ), eventRate(1), sumOfWeights(0)
192 {
193 GOOGLE_PROTOBUF_VERIFY_VERSION;
194
195 // I need to copy the details of the trigger menu into the protobuf storage.
196 // This means I'm holding a duplicate, but I need it to write the sample to a
197 // protobuf file, so I might as well do it now.
198 for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
199 {
200 const l1menu::ITrigger& trigger=triggerMenu.getTrigger(triggerNumber);
201
202 l1menuprotobuf::Trigger* pProtobufTrigger=protobufSampleHeader.add_trigger();
203 pProtobufTrigger->set_name( trigger.name() );
204 pProtobufTrigger->set_version( trigger.version() );
205
206 // Record all of the parameters. It's not strictly necessary to record the values
207 // of the parameters that are recorded for each event, but I might as well so that
208 // the trigger menu is loaded exactly as it was saved.
209 const auto parameterNames=trigger.parameterNames();
210 for( const auto& parameterName : parameterNames )
211 {
212 l1menuprotobuf::Trigger_TriggerParameter* pProtobufParameter=pProtobufTrigger->add_parameter();
213 pProtobufParameter->set_name(parameterName);
214 pProtobufParameter->set_value( trigger.parameter(parameterName) );
215 }
216
217 // Make a note of the names of the parameters that are recorded for each event. For this
218 // I'm just recording the parameters that refer to the thresholds.
219 const auto thresholdNames=l1menu::tools::getThresholdNames(trigger);
220 for( const auto& thresholdName : thresholdNames ) pProtobufTrigger->add_varying_parameter(thresholdName);
221
222 } // end of loop over triggers
223
224 // Always make sure there is at least one Run ready to be added to
225 std::unique_ptr<l1menuprotobuf::Run> pNewRun( new l1menuprotobuf::Run );
226 protobufRuns.push_back( std::move( pNewRun ) );
227
228 }
229
230 l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const std::string& filename )
231 : event(thisObject), triggerMenu(mutableTriggerMenu_), eventRate(1), sumOfWeights(0)
232 {
233 GOOGLE_PROTOBUF_VERIFY_VERSION;
234
235 // Open the file with read ability
236 int fileDescriptor = open( filename.c_str(), O_RDONLY );
237 if( fileDescriptor==0 ) throw std::runtime_error( "ReducedMenuSample initialise from file - couldn't open file" );
238 ::UnixFileSentry fileSentry( fileDescriptor ); // Use this as an exception safe way of closing the input file
239 google::protobuf::io::FileInputStream fileInput( fileDescriptor );
240
241 // First read the magic number at the start of the file and make sure it
242 // matches what I expect. This is uncompressed so I'll wrap it in a block
243 // to make sure the CodedInputStream is destructed before creating a new
244 // one with gzip input.
245 {
246 google::protobuf::io::CodedInputStream codedInput( &fileInput );
247
248 // As a read buffer, I'll create a string the correct size (filled with an arbitrary
249 // character) and read straight into that.
250 std::string readMagicNumber;
251 if( !codedInput.ReadString( &readMagicNumber, FILE_FORMAT_MAGIC_NUMBER.size() ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - error reading magic number" );
252 if( readMagicNumber!=FILE_FORMAT_MAGIC_NUMBER ) throw std::runtime_error( "ReducedMenuSample - tried to initialise with a file that is not the correct format" );
253
254 google::protobuf::uint32 fileformatVersion;
255 if( !codedInput.ReadVarint32( &fileformatVersion ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - error reading file format version" );
256 // So far I only have (and ever expect to have) one version of the file
257 // format, imaginatively versioned "1". You never know though...
258 if( fileformatVersion>1 ) std::cerr << "Warning: Attempting to read a ReducedMenuSample with version " << fileformatVersion << " with code that only knows up to version 1." << std::endl;
259 }
260
261 google::protobuf::io::GzipInputStream gzipInput( &fileInput );
262 google::protobuf::io::CodedInputStream codedInput( &gzipInput );
263
264 // Disable warnings on this input stream (second parameter, -1). The
265 // first parameter is the default. I'll change this if necessary in
266 // the loop later.
267 size_t totalBytesLimit=67108864;
268 codedInput.SetTotalBytesLimit( totalBytesLimit, -1 );
269
270 google::protobuf::uint64 messageSize;
271
272 // Read the size of the header message
273 if( !codedInput.ReadVarint64( &messageSize ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - error reading message size for header" );
274 google::protobuf::io::CodedInputStream::Limit readLimit=codedInput.PushLimit(messageSize);
275 if( !protobufSampleHeader.ParseFromCodedStream( &codedInput ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - some unknown error while reading header" );
276 codedInput.PopLimit(readLimit);
277
278 // Keep looping until there is nothing more to be read from the file.
279 while( codedInput.ReadVarint64( &messageSize ) )
280 {
281 readLimit=codedInput.PushLimit(messageSize);
282
283 // Make sure the CodedInputStream doesn't refuse to read the message because it's
284 // read too much already. I'll also add an arbitrary 50 on to always make sure
285 // I can read the next messageSize if there is one.
286 if( gzipInput.ByteCount()+messageSize+50 > totalBytesLimit )
287 {
288 totalBytesLimit+=messageSize*5; // Might as well set it a little higher than necessary while I'm at it.
289 codedInput.SetTotalBytesLimit( totalBytesLimit, -1 );
290 }
291 std::unique_ptr<l1menuprotobuf::Run> pNewRun( new l1menuprotobuf::Run );
292 if( !pNewRun->ParseFromCodedStream( &codedInput ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - some unknown error while reading run" );
293 protobufRuns.push_back( std::move( pNewRun ) );
294
295 codedInput.PopLimit(readLimit);
296 }
297
298
299 // Always make sure there is at least one Run ready to be added to. Later
300 // code assumes there is already a run there.
301 if( protobufRuns.empty() )
302 {
303 std::unique_ptr<l1menuprotobuf::Run> pNewRun( new l1menuprotobuf::Run );
304 protobufRuns.push_back( std::move( pNewRun ) );
305 }
306
307 // Count up the sum of the weights of all events
308 for( const auto& pRun : protobufRuns )
309 {
310 sumOfWeights+=sumWeights( *pRun );
311 }
312
313 // I have all of the information in the protobuf members, but I also need the trigger information
314 // in the form of l1menu::TriggerMenu. Copy out the required information.
315 for( int triggerNumber=0; triggerNumber<protobufSampleHeader.trigger_size(); ++triggerNumber )
316 {
317 const l1menuprotobuf::Trigger& inputTrigger=protobufSampleHeader.trigger(triggerNumber);
318
319 mutableTriggerMenu_.addTrigger( inputTrigger.name(), inputTrigger.version() );
320 // Get a reference to the trigger I just created
321 l1menu::ITrigger& trigger=mutableTriggerMenu_.getTrigger(triggerNumber);
322
323 // Run through all of the parameters and set them to what they were
324 // when the sample was made.
325 for( int parameterNumber=0; parameterNumber<inputTrigger.parameter_size(); ++parameterNumber )
326 {
327 const auto& inputParameter=inputTrigger.parameter(parameterNumber);
328 trigger.parameter(inputParameter.name())=inputParameter.value();
329 }
330
331 // I should probably check the threshold names exist. I'll do it another time.
332 }
333
334 }
335
336 l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::MenuSample& originalSample, const l1menu::TriggerMenu& triggerMenu )
337 : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
338 {
339 addSample( originalSample );
340 }
341
342 l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::TriggerMenu& triggerMenu )
343 : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
344 {
345 // No operation besides the initialiser list
346 }
347
348 l1menu::ReducedMenuSample::ReducedMenuSample( const std::string& filename )
349 : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, filename ) )
350 {
351 // No operation except the initialiser list
352 }
353
354 l1menu::ReducedMenuSample::~ReducedMenuSample()
355 {
356 // No operation. Just need one defined otherwise the default one messes up
357 // the unique_ptr deletion because ReducedMenuSamplePrivateMembers isn't
358 // defined elsewhere.
359 }
360
361 void l1menu::ReducedMenuSample::addSample( const l1menu::MenuSample& originalSample )
362 {
363 l1menuprotobuf::Run* pCurrentRun=pImple_->protobufRuns.back().get();
364
365 for( size_t eventNumber=0; eventNumber<originalSample.numberOfEvents(); ++eventNumber )
366 {
367 // Split the events up into groups in arbitrary numbers. This is to get around
368 // a protobuf aversion to long messages.
369 if( pCurrentRun->event_size() >= pImple_->EVENTS_PER_RUN )
370 {
371 // Gone over the arbitrary limit, so create a new protobuf Run and start
372 // using that instead.
373 std::unique_ptr<l1menuprotobuf::Run> pNewRun( new l1menuprotobuf::Run );
374 pImple_->protobufRuns.push_back( std::move( pNewRun ) );
375 pCurrentRun=pImple_->protobufRuns.back().get();
376 }
377
378 const l1menu::L1TriggerDPGEvent& event=originalSample.getEvent( eventNumber );
379 l1menuprotobuf::Event* pProtobufEvent=pCurrentRun->add_event();
380
381 // Loop over all of the triggers
382 for( size_t triggerNumber=0; triggerNumber<pImple_->triggerMenu.numberOfTriggers(); ++triggerNumber )
383 {
384 std::unique_ptr<l1menu::ITrigger> pTrigger=pImple_->triggerMenu.getTriggerCopy(triggerNumber);
385 std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(*pTrigger);
386
387 try
388 {
389 l1menu::tools::setTriggerThresholdsAsTightAsPossible( event, *pTrigger, 0.001 );
390 // Set all of the parameters to match the thresholds in the trigger
391 for( const auto& thresholdName : thresholdNames )
392 {
393 pProtobufEvent->add_threshold( pTrigger->parameter(thresholdName) );
394 }
395 }
396 catch( std::exception& error )
397 {
398 // setTriggerThresholdsAsTightAsPossible() couldn't find thresholds so record
399 // -1 for everything.
400 // Range based for loop gives me a warning because I don't use the thresholdName.
401 for( size_t index=0; index<thresholdNames.size(); ++index ) pProtobufEvent->add_threshold(-1);
402 } // end of try block that sets the trigger thresholds
403
404 } // end of loop over triggers
405
406 pImple_->sumOfWeights+=event.weight();
407 } // end of loop over events
408 }
409
410 void l1menu::ReducedMenuSample::saveToFile( const std::string& filename ) const
411 {
412 // Open the file. Parameters are filename, write ability and create, rw-r--r-- permissions.
413 int fileDescriptor = open( filename.c_str(), O_WRONLY | O_CREAT, 0644 );
414 if( fileDescriptor==0 ) throw std::runtime_error( "ReducedMenuSample save to file - couldn't open file" );
415 ::UnixFileSentry fileSentry( fileDescriptor ); // Use this as an exception safe way of closing the output file
416
417 // Setup the protobuf file handlers
418 google::protobuf::io::FileOutputStream fileOutput( fileDescriptor );
419
420 // I want the magic number and file format identifier uncompressed, so
421 // I'll write those before switching to using gzipped output.
422 { // Block to make sure codedOutput is destructed before the gzip version is created
423 google::protobuf::io::CodedOutputStream codedOutput( &fileOutput );
424
425 // Write a magic number at the start of all files
426 codedOutput.WriteString( pImple_->FILE_FORMAT_MAGIC_NUMBER );
427 // Write an integer that specifies what version of the file format I'm using. I
428 // have no intention of changing the format but I might as well keep the option
429 // open.
430 codedOutput.WriteVarint32( 1 );
431 }
432
433 google::protobuf::io::GzipOutputStream gzipOutput( &fileOutput );
434 google::protobuf::io::CodedOutputStream codedOutput( &gzipOutput );
435
436 // Write the size of the header message into the file...
437 codedOutput.WriteVarint64( pImple_->protobufSampleHeader.ByteSize() );
438 // ...and then write the header
439 pImple_->protobufSampleHeader.SerializeToCodedStream( &codedOutput );
440
441 // Now go through each of the runs and do the same for those
442 for( const auto& pRun : pImple_->protobufRuns )
443 {
444 codedOutput.WriteVarint64( pRun->ByteSize() );
445 pRun->SerializeToCodedStream( &codedOutput );
446 }
447
448 }
449
450 size_t l1menu::ReducedMenuSample::numberOfEvents() const
451 {
452 size_t numberOfEvents=0;
453 for( const auto& pRun : pImple_->protobufRuns ) numberOfEvents+=pRun->event_size();
454 return numberOfEvents;
455 }
456
457 const l1menu::TriggerMenu& l1menu::ReducedMenuSample::getTriggerMenu() const
458 {
459 return pImple_->triggerMenu;
460 }
461
462 bool l1menu::ReducedMenuSample::containsTrigger( const l1menu::ITrigger& trigger, bool allowOlderVersion ) const
463 {
464 // Loop over all of the triggers in the menu, and see if there is one
465 // where the name and version match.
466 for( size_t triggerNumber=0; triggerNumber<pImple_->triggerMenu.numberOfTriggers(); ++triggerNumber )
467 {
468 const l1menu::ITrigger& triggerInMenu=pImple_->triggerMenu.getTrigger(triggerNumber);
469 if( triggerInMenu.name()!=trigger.name() ) continue;
470 if( allowOlderVersion )
471 {
472 if( triggerInMenu.version()>trigger.version() ) continue;
473 }
474 else
475 {
476 if( triggerInMenu.version()!=trigger.version() ) continue;
477 }
478
479 // If control got this far then there is a trigger with the required name
480 // and sufficient version. I now need to check all of the non threshold parameters
481 // to make sure they match, i.e. make sure the ReducedSample was made with the same
482 // eta cuts or whatever.
483 // I don't care if the thresholds don't match because that's what's stored in the
484 // ReducedMenuSample.
485 std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
486 bool allParametersMatch=true;
487 for( const auto& parameterName : parameterNames )
488 {
489 if( trigger.parameter(parameterName)!=triggerInMenu.parameter(parameterName) ) allParametersMatch=false;
490 }
491
492 if( allParametersMatch ) return true;
493 } // end of loop over triggers
494
495 // If control got this far then no trigger was found that matched
496 return false;
497 }
498
499 const std::map<std::string,size_t> l1menu::ReducedMenuSample::getTriggerParameterIdentifiers( const l1menu::ITrigger& trigger, bool allowOlderVersion ) const
500 {
501 std::map<std::string,size_t> returnValue;
502
503 // Need to find out how many parameters there are for each event. Basically the sum
504 // of the number of thresholds for all triggers.
505 size_t parameterNumber=0;
506 bool triggerWasFound=true;
507 for( size_t triggerNumber=0; triggerNumber<pImple_->triggerMenu.numberOfTriggers(); ++triggerNumber )
508 {
509 const l1menu::ITrigger& triggerInMenu=pImple_->triggerMenu.getTrigger(triggerNumber);
510
511 triggerWasFound=true; // Set to true, then back to false if any of the tests fail
512 // See if this trigger in the menu is the same as the one passed as a parameter
513 if( triggerInMenu.name()!=trigger.name() ) triggerWasFound=false;
514 if( allowOlderVersion )
515 {
516 if( triggerInMenu.version()>trigger.version() ) triggerWasFound=false;
517 }
518 else
519 {
520 if( triggerInMenu.version()!=trigger.version() ) triggerWasFound=false;
521 }
522
523 // If control got this far then there is a trigger with the required name
524 // and sufficient version. I now need to check all of the non threshold parameters
525 // to make sure they match, i.e. make sure the ReducedSample was made with the same
526 // eta cuts or whatever.
527 // I don't care if the thresholds don't match because that's what's stored in the
528 // ReducedMenuSample.
529 if( triggerWasFound ) // Trigger can still fail, but no point doing this check if it already has
530 {
531 std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
532 for( const auto& parameterName : parameterNames )
533 {
534 if( trigger.parameter(parameterName)!=triggerInMenu.parameter(parameterName) ) triggerWasFound=false;
535 }
536 }
537
538 std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(triggerInMenu);
539 if( triggerWasFound )
540 {
541 for( const auto& thresholdName : thresholdNames )
542 {
543 returnValue[thresholdName]=parameterNumber;
544 ++parameterNumber;
545 }
546 break;
547 }
548 else parameterNumber+=thresholdNames.size();
549 }
550
551 // There could conceivably be a trigger that was found but has no thresholds
552 // (I guess - it would be a pretty pointless trigger though). To indicate the
553 // difference between that and a trigger that wasn't found I'll respectively
554 // return the empty vector or throw an exception.
555 if( !triggerWasFound ) throw std::runtime_error( "l1menu::ReducedMenuSample::getTriggerParameterIdentifiers() called for a trigger that was not used to create the sample - "+trigger.name() );
556
557 return returnValue;
558 }
559
560 const l1menu::IEvent& l1menu::ReducedMenuSample::getEvent( size_t eventNumber ) const
561 {
562 for( const auto& pRun : pImple_->protobufRuns )
563 {
564 if( eventNumber<static_cast<size_t>(pRun->event_size()) )
565 {
566 pImple_->event.pProtobufEvent_=pRun->mutable_event(eventNumber);
567 return pImple_->event;
568 }
569 // Event must be in a later run, so reduce the number by how many events
570 // were in this run and look again.
571 eventNumber-=pRun->event_size();
572 }
573
574 // Should always find the event before getting to this point, so throw an
575 // exception if this happens.
576 throw std::runtime_error( "ReducedMenuSample::getEvent(eventNumber) was asked for an invalid eventNumber" );
577
578 }
579
580 std::unique_ptr<l1menu::ICachedTrigger> l1menu::ReducedMenuSample::createCachedTrigger( const l1menu::ITrigger& trigger ) const
581 {
582 return std::unique_ptr<l1menu::ICachedTrigger>( new CachedTriggerImplementation(*this,trigger) );
583 }
584
585 float l1menu::ReducedMenuSample::eventRate() const
586 {
587 return pImple_->eventRate;
588 }
589
590 void l1menu::ReducedMenuSample::setEventRate( float rate )
591 {
592 pImple_->eventRate=rate;
593 }
594
595 float l1menu::ReducedMenuSample::sumOfWeights() const
596 {
597 return pImple_->sumOfWeights;
598 }
599
600 std::unique_ptr<const l1menu::IMenuRate> l1menu::ReducedMenuSample::rate( const l1menu::TriggerMenu& menu ) const
601 {
602 // TODO make sure the TriggerMenu is valid for this sample
603
604 // I need a non-const menu, so make a copy
605 l1menu::TriggerMenu mutableMenu( menu );
606
607 // The number of events that pass each trigger
608 std::vector<size_t> numberOfEventsPassed( menu.numberOfTriggers() );
609 float numberOfEventsPassingAnyTrigger;
610
611 for( size_t triggerNumber=0; triggerNumber<menu.numberOfTriggers(); ++triggerNumber )
612 {
613 mutableMenu.getTrigger( triggerNumber ).initiateForReducedSample( *this );
614 }
615
616 for( size_t eventNumber=0; eventNumber<numberOfEvents(); ++eventNumber )
617 {
618 const l1menu::IEvent& event=getEvent(eventNumber);
619 bool anyTriggerPassed=false;
620
621 for( size_t triggerNumber=0; triggerNumber<menu.numberOfTriggers(); ++triggerNumber )
622 {
623 if( event.passesTrigger( mutableMenu.getTrigger(triggerNumber) ) )
624 {
625 // If the event passes the trigger, increment the counter
626 ++numberOfEventsPassed[triggerNumber];
627 anyTriggerPassed=true;
628 }
629 }
630
631 if( anyTriggerPassed ) ++numberOfEventsPassingAnyTrigger;
632 }
633
634 ::MenuRateImplementation* pRates=new ::MenuRateImplementation;
635 pRates->setScaling( pImple_->eventRate );
636 // This is the value I want to return, but I still need access to the extended attributes of the subclass
637 std::unique_ptr<const l1menu::IMenuRate> pReturnValue( pRates );
638
639 pRates->setTotalFraction( static_cast<float>(numberOfEventsPassingAnyTrigger)/static_cast<float>(numberOfEvents()) );
640
641 for( size_t triggerNumber=0; triggerNumber<numberOfEventsPassed.size(); ++triggerNumber )
642 {
643 float fraction=static_cast<float>(numberOfEventsPassed[triggerNumber])/static_cast<float>(numberOfEvents());
644 pRates->addTriggerRate( mutableMenu.getTrigger(triggerNumber), fraction );
645 }
646
647 return pReturnValue;
648 }