ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/grimes/L1Menu/src/ReducedMenuSample.cpp
Revision: 1.12
Committed: Mon Jun 24 17:09:05 2013 UTC (11 years, 10 months ago) by grimes
Branch: MAIN
Changes since 1.11: +35 -20 lines
Log Message:
Change to the ReducedSample fileformat so that the magic number and fileformat identifier are uncompressed

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