ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/grimes/L1Menu/src/ReducedMenuSample.cpp
Revision: 1.17
Committed: Thu Jul 4 13:02:22 2013 UTC (11 years, 10 months ago) by grimes
Branch: MAIN
CVS Tags: HEAD
Changes since 1.16: +0 -0 lines
State: FILE REMOVED
Log Message:
Renamed ReducedMenuSample to ReducedSample, and MenuSample to FullSample

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