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

# User Rev Content
1 grimes 1.1 #include "l1menu/ReducedMenuSample.h"
2    
3     #include <vector>
4 grimes 1.2 #include <stdexcept>
5 grimes 1.8 #include <fcntl.h>
6 grimes 1.7 #include <algorithm>
7 grimes 1.8 #include <iostream>
8 grimes 1.13 #include <sstream>
9     #include "l1menu/ReducedEvent.h"
10 grimes 1.1 #include "l1menu/MenuSample.h"
11     #include "l1menu/TriggerMenu.h"
12 grimes 1.2 #include "l1menu/ITrigger.h"
13 grimes 1.13 #include "l1menu/ICachedTrigger.h"
14     #include "l1menu/L1TriggerDPGEvent.h"
15     #include "l1menu/IEvent.h"
16 grimes 1.11 #include "l1menu/IMenuRate.h"
17     #include "l1menu/ITriggerRate.h"
18     #include "l1menu/tools/tools.h"
19 grimes 1.2 #include "protobuf/l1menu.pb.h"
20 grimes 1.8 #include <google/protobuf/io/zero_copy_stream_impl.h>
21     #include <google/protobuf/io/gzip_stream.h>
22 grimes 1.1
23     namespace // unnamed namespace
24     {
25 grimes 1.8 /** @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 grimes 1.11
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 grimes 1.13 /** @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 grimes 1.11 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 grimes 1.13 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 grimes 1.1 }
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 grimes 1.7 private:
168     l1menu::TriggerMenu mutableTriggerMenu_;
169 grimes 1.1 public:
170 grimes 1.13 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 grimes 1.11 const l1menu::TriggerMenu& triggerMenu; // External const access to mutableTriggerMenu_
175     float eventRate;
176 grimes 1.13 float sumOfWeights;
177 grimes 1.8 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 grimes 1.1 };
184 grimes 1.8
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 grimes 1.1 }
189    
190 grimes 1.13 l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const l1menu::TriggerMenu& newTriggerMenu )
191     : mutableTriggerMenu_( newTriggerMenu ), event(thisObject), triggerMenu( mutableTriggerMenu_ ), eventRate(1), sumOfWeights(0)
192 grimes 1.1 {
193 grimes 1.7 GOOGLE_PROTOBUF_VERIFY_VERSION;
194    
195     // I need to copy the details of the trigger menu into the protobuf storage.
196 grimes 1.8 // 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 grimes 1.1 for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
199     {
200     const l1menu::ITrigger& trigger=triggerMenu.getTrigger(triggerNumber);
201 grimes 1.7
202 grimes 1.8 l1menuprotobuf::Trigger* pProtobufTrigger=protobufSampleHeader.add_trigger();
203 grimes 1.7 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 grimes 1.11 const auto thresholdNames=l1menu::tools::getThresholdNames(trigger);
220 grimes 1.8 for( const auto& thresholdName : thresholdNames ) pProtobufTrigger->add_varying_parameter(thresholdName);
221 grimes 1.7
222     } // end of loop over triggers
223    
224 grimes 1.8 // 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 grimes 1.7 }
229    
230 grimes 1.13 l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const std::string& filename )
231     : event(thisObject), triggerMenu(mutableTriggerMenu_), eventRate(1), sumOfWeights(0)
232 grimes 1.7 {
233     GOOGLE_PROTOBUF_VERIFY_VERSION;
234    
235 grimes 1.8 // 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 grimes 1.12
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 grimes 1.8 google::protobuf::io::GzipInputStream gzipInput( &fileInput );
262     google::protobuf::io::CodedInputStream codedInput( &gzipInput );
263    
264 grimes 1.9 // 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 grimes 1.8 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 grimes 1.9 // 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 grimes 1.8 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 grimes 1.1 }
297    
298 grimes 1.7
299 grimes 1.8 // 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 grimes 1.13 // Count up the sum of the weights of all events
308     for( const auto& pRun : protobufRuns )
309     {
310     sumOfWeights+=sumWeights( *pRun );
311     }
312    
313 grimes 1.8 // I have all of the information in the protobuf members, but I also need the trigger information
314 grimes 1.7 // in the form of l1menu::TriggerMenu. Copy out the required information.
315 grimes 1.8 for( int triggerNumber=0; triggerNumber<protobufSampleHeader.trigger_size(); ++triggerNumber )
316 grimes 1.2 {
317 grimes 1.8 const l1menuprotobuf::Trigger& inputTrigger=protobufSampleHeader.trigger(triggerNumber);
318 grimes 1.7
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 grimes 1.2
323 grimes 1.7 // 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 grimes 1.2 {
327 grimes 1.7 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 grimes 1.2
334 grimes 1.7 }
335 grimes 1.2
336 grimes 1.7 l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::MenuSample& originalSample, const l1menu::TriggerMenu& triggerMenu )
337 grimes 1.13 : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
338 grimes 1.7 {
339     addSample( originalSample );
340 grimes 1.2 }
341    
342 grimes 1.5 l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::TriggerMenu& triggerMenu )
343 grimes 1.13 : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
344 grimes 1.5 {
345 grimes 1.7 // No operation besides the initialiser list
346 grimes 1.5 }
347    
348 grimes 1.13 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 grimes 1.2 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 grimes 1.5 void l1menu::ReducedMenuSample::addSample( const l1menu::MenuSample& originalSample )
362     {
363 grimes 1.8 l1menuprotobuf::Run* pCurrentRun=pImple_->protobufRuns.back().get();
364    
365 grimes 1.5 for( size_t eventNumber=0; eventNumber<originalSample.numberOfEvents(); ++eventNumber )
366     {
367 grimes 1.8 // Split the events up into groups in arbitrary numbers. This is to get around
368     // a protobuf aversion to long messages.
369 grimes 1.9 if( pCurrentRun->event_size() >= pImple_->EVENTS_PER_RUN )
370 grimes 1.8 {
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 grimes 1.13 const l1menu::L1TriggerDPGEvent& event=originalSample.getEvent( eventNumber );
379 grimes 1.8 l1menuprotobuf::Event* pProtobufEvent=pCurrentRun->add_event();
380 grimes 1.5
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 grimes 1.11 std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(*pTrigger);
386 grimes 1.5
387     try
388     {
389 grimes 1.11 l1menu::tools::setTriggerThresholdsAsTightAsPossible( event, *pTrigger, 0.001 );
390 grimes 1.5 // Set all of the parameters to match the thresholds in the trigger
391     for( const auto& thresholdName : thresholdNames )
392     {
393 grimes 1.7 pProtobufEvent->add_threshold( pTrigger->parameter(thresholdName) );
394 grimes 1.5 }
395     }
396     catch( std::exception& error )
397     {
398     // setTriggerThresholdsAsTightAsPossible() couldn't find thresholds so record
399     // -1 for everything.
400 grimes 1.7 // 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 grimes 1.5 } // end of try block that sets the trigger thresholds
403    
404     } // end of loop over triggers
405 grimes 1.7
406 grimes 1.13 pImple_->sumOfWeights+=event.weight();
407 grimes 1.5 } // end of loop over events
408     }
409    
410 grimes 1.2 void l1menu::ReducedMenuSample::saveToFile( const std::string& filename ) const
411     {
412 grimes 1.8 // 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 grimes 1.12
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 grimes 1.8 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 grimes 1.1 }
449    
450     size_t l1menu::ReducedMenuSample::numberOfEvents() const
451     {
452 grimes 1.8 size_t numberOfEvents=0;
453     for( const auto& pRun : pImple_->protobufRuns ) numberOfEvents+=pRun->event_size();
454     return numberOfEvents;
455 grimes 1.1 }
456    
457 grimes 1.2 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 grimes 1.11 std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
486 grimes 1.2 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 grimes 1.11 std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
532 grimes 1.2 for( const auto& parameterName : parameterNames )
533     {
534     if( trigger.parameter(parameterName)!=triggerInMenu.parameter(parameterName) ) triggerWasFound=false;
535     }
536     }
537    
538 grimes 1.11 std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(triggerInMenu);
539 grimes 1.2 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 grimes 1.11 if( !triggerWasFound ) throw std::runtime_error( "l1menu::ReducedMenuSample::getTriggerParameterIdentifiers() called for a trigger that was not used to create the sample - "+trigger.name() );
556 grimes 1.2
557     return returnValue;
558     }
559 grimes 1.10
560 grimes 1.13 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 grimes 1.11 float l1menu::ReducedMenuSample::eventRate() const
586     {
587     return pImple_->eventRate;
588     }
589    
590 grimes 1.13 void l1menu::ReducedMenuSample::setEventRate( float rate )
591 grimes 1.11 {
592     pImple_->eventRate=rate;
593     }
594    
595 grimes 1.13 float l1menu::ReducedMenuSample::sumOfWeights() const
596     {
597     return pImple_->sumOfWeights;
598     }
599    
600 grimes 1.11 std::unique_ptr<const l1menu::IMenuRate> l1menu::ReducedMenuSample::rate( const l1menu::TriggerMenu& menu ) const
601 grimes 1.10 {
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 grimes 1.11 float numberOfEventsPassingAnyTrigger;
610 grimes 1.10
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 grimes 1.13 const l1menu::IEvent& event=getEvent(eventNumber);
619 grimes 1.11 bool anyTriggerPassed=false;
620 grimes 1.10
621     for( size_t triggerNumber=0; triggerNumber<menu.numberOfTriggers(); ++triggerNumber )
622     {
623 grimes 1.13 if( event.passesTrigger( mutableMenu.getTrigger(triggerNumber) ) )
624 grimes 1.10 {
625     // If the event passes the trigger, increment the counter
626     ++numberOfEventsPassed[triggerNumber];
627 grimes 1.11 anyTriggerPassed=true;
628 grimes 1.10 }
629     }
630    
631 grimes 1.11 if( anyTriggerPassed ) ++numberOfEventsPassingAnyTrigger;
632 grimes 1.10 }
633    
634 grimes 1.11 ::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 grimes 1.10
641     for( size_t triggerNumber=0; triggerNumber<numberOfEventsPassed.size(); ++triggerNumber )
642     {
643 grimes 1.11 float fraction=static_cast<float>(numberOfEventsPassed[triggerNumber])/static_cast<float>(numberOfEvents());
644     pRates->addTriggerRate( mutableMenu.getTrigger(triggerNumber), fraction );
645 grimes 1.10 }
646    
647 grimes 1.11 return pReturnValue;
648 grimes 1.10 }