ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/grimes/L1Menu/src/ReducedMenuSample.cpp
Revision: 1.15
Committed: Tue Jul 2 23:30:37 2013 UTC (11 years, 10 months ago) by grimes
Branch: MAIN
Changes since 1.14: +14 -82 lines
Log Message:
Various changes to make interface conformance better, and dropping old unrequired methods

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.15 #include "l1menu/implementation/MenuRateImplementation.h"
20 grimes 1.2 #include "protobuf/l1menu.pb.h"
21 grimes 1.8 #include <google/protobuf/io/zero_copy_stream_impl.h>
22     #include <google/protobuf/io/gzip_stream.h>
23 grimes 1.1
24     namespace // unnamed namespace
25     {
26 grimes 1.8 /** @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 grimes 1.11
39 grimes 1.13 /** @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 grimes 1.1 }
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 grimes 1.7 private:
99     l1menu::TriggerMenu mutableTriggerMenu_;
100 grimes 1.1 public:
101 grimes 1.13 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 grimes 1.11 const l1menu::TriggerMenu& triggerMenu; // External const access to mutableTriggerMenu_
106     float eventRate;
107 grimes 1.13 float sumOfWeights;
108 grimes 1.8 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 grimes 1.1 };
115 grimes 1.8
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 grimes 1.1 }
120    
121 grimes 1.13 l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const l1menu::TriggerMenu& newTriggerMenu )
122     : mutableTriggerMenu_( newTriggerMenu ), event(thisObject), triggerMenu( mutableTriggerMenu_ ), eventRate(1), sumOfWeights(0)
123 grimes 1.1 {
124 grimes 1.7 GOOGLE_PROTOBUF_VERIFY_VERSION;
125    
126     // I need to copy the details of the trigger menu into the protobuf storage.
127 grimes 1.8 // 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 grimes 1.1 for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
130     {
131     const l1menu::ITrigger& trigger=triggerMenu.getTrigger(triggerNumber);
132 grimes 1.7
133 grimes 1.8 l1menuprotobuf::Trigger* pProtobufTrigger=protobufSampleHeader.add_trigger();
134 grimes 1.7 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 grimes 1.11 const auto thresholdNames=l1menu::tools::getThresholdNames(trigger);
151 grimes 1.8 for( const auto& thresholdName : thresholdNames ) pProtobufTrigger->add_varying_parameter(thresholdName);
152 grimes 1.7
153     } // end of loop over triggers
154    
155 grimes 1.8 // 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 grimes 1.7 }
160    
161 grimes 1.13 l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const std::string& filename )
162     : event(thisObject), triggerMenu(mutableTriggerMenu_), eventRate(1), sumOfWeights(0)
163 grimes 1.7 {
164     GOOGLE_PROTOBUF_VERIFY_VERSION;
165    
166 grimes 1.8 // 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 grimes 1.12
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 grimes 1.8 google::protobuf::io::GzipInputStream gzipInput( &fileInput );
193     google::protobuf::io::CodedInputStream codedInput( &gzipInput );
194    
195 grimes 1.9 // 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 grimes 1.8 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 grimes 1.9 // 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 grimes 1.8 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 grimes 1.1 }
228    
229 grimes 1.7
230 grimes 1.8 // 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 grimes 1.13 // Count up the sum of the weights of all events
239     for( const auto& pRun : protobufRuns )
240     {
241     sumOfWeights+=sumWeights( *pRun );
242     }
243    
244 grimes 1.8 // I have all of the information in the protobuf members, but I also need the trigger information
245 grimes 1.7 // in the form of l1menu::TriggerMenu. Copy out the required information.
246 grimes 1.8 for( int triggerNumber=0; triggerNumber<protobufSampleHeader.trigger_size(); ++triggerNumber )
247 grimes 1.2 {
248 grimes 1.8 const l1menuprotobuf::Trigger& inputTrigger=protobufSampleHeader.trigger(triggerNumber);
249 grimes 1.7
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 grimes 1.2
254 grimes 1.7 // 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 grimes 1.2 {
258 grimes 1.7 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 grimes 1.2
265 grimes 1.7 }
266 grimes 1.2
267 grimes 1.7 l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::MenuSample& originalSample, const l1menu::TriggerMenu& triggerMenu )
268 grimes 1.13 : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
269 grimes 1.7 {
270     addSample( originalSample );
271 grimes 1.2 }
272    
273 grimes 1.5 l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::TriggerMenu& triggerMenu )
274 grimes 1.13 : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
275 grimes 1.5 {
276 grimes 1.7 // No operation besides the initialiser list
277 grimes 1.5 }
278    
279 grimes 1.13 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 grimes 1.2 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 grimes 1.5 void l1menu::ReducedMenuSample::addSample( const l1menu::MenuSample& originalSample )
293     {
294 grimes 1.8 l1menuprotobuf::Run* pCurrentRun=pImple_->protobufRuns.back().get();
295    
296 grimes 1.5 for( size_t eventNumber=0; eventNumber<originalSample.numberOfEvents(); ++eventNumber )
297     {
298 grimes 1.8 // Split the events up into groups in arbitrary numbers. This is to get around
299     // a protobuf aversion to long messages.
300 grimes 1.9 if( pCurrentRun->event_size() >= pImple_->EVENTS_PER_RUN )
301 grimes 1.8 {
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 grimes 1.15 const l1menu::L1TriggerDPGEvent& event=originalSample.getFullEvent( eventNumber );
310 grimes 1.8 l1menuprotobuf::Event* pProtobufEvent=pCurrentRun->add_event();
311 grimes 1.15 if( event.weight()!=1 ) pProtobufEvent->set_weight( event.weight() );
312 grimes 1.5
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 grimes 1.11 std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(*pTrigger);
318 grimes 1.5
319     try
320     {
321 grimes 1.11 l1menu::tools::setTriggerThresholdsAsTightAsPossible( event, *pTrigger, 0.001 );
322 grimes 1.5 // Set all of the parameters to match the thresholds in the trigger
323     for( const auto& thresholdName : thresholdNames )
324     {
325 grimes 1.7 pProtobufEvent->add_threshold( pTrigger->parameter(thresholdName) );
326 grimes 1.5 }
327     }
328     catch( std::exception& error )
329     {
330     // setTriggerThresholdsAsTightAsPossible() couldn't find thresholds so record
331 grimes 1.15 // a default for everything.
332 grimes 1.7 // Range based for loop gives me a warning because I don't use the thresholdName.
333 grimes 1.15 for( size_t index=0; index<thresholdNames.size(); ++index ) pProtobufEvent->add_threshold(100000);
334 grimes 1.5 } // end of try block that sets the trigger thresholds
335    
336     } // end of loop over triggers
337 grimes 1.7
338 grimes 1.13 pImple_->sumOfWeights+=event.weight();
339 grimes 1.5 } // end of loop over events
340     }
341    
342 grimes 1.2 void l1menu::ReducedMenuSample::saveToFile( const std::string& filename ) const
343     {
344 grimes 1.8 // 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 grimes 1.12
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 grimes 1.8 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 grimes 1.1 }
381    
382     size_t l1menu::ReducedMenuSample::numberOfEvents() const
383     {
384 grimes 1.8 size_t numberOfEvents=0;
385     for( const auto& pRun : pImple_->protobufRuns ) numberOfEvents+=pRun->event_size();
386     return numberOfEvents;
387 grimes 1.1 }
388    
389 grimes 1.2 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 grimes 1.11 std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
418 grimes 1.2 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 grimes 1.11 std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
464 grimes 1.2 for( const auto& parameterName : parameterNames )
465     {
466     if( trigger.parameter(parameterName)!=triggerInMenu.parameter(parameterName) ) triggerWasFound=false;
467     }
468     }
469    
470 grimes 1.11 std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(triggerInMenu);
471 grimes 1.2 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 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() );
488 grimes 1.2
489     return returnValue;
490     }
491 grimes 1.10
492 grimes 1.13 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 grimes 1.11 float l1menu::ReducedMenuSample::eventRate() const
517     {
518     return pImple_->eventRate;
519     }
520    
521 grimes 1.13 void l1menu::ReducedMenuSample::setEventRate( float rate )
522 grimes 1.11 {
523     pImple_->eventRate=rate;
524     }
525    
526 grimes 1.13 float l1menu::ReducedMenuSample::sumOfWeights() const
527     {
528     return pImple_->sumOfWeights;
529     }
530    
531 grimes 1.11 std::unique_ptr<const l1menu::IMenuRate> l1menu::ReducedMenuSample::rate( const l1menu::TriggerMenu& menu ) const
532 grimes 1.10 {
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 grimes 1.11 float numberOfEventsPassingAnyTrigger;
538 grimes 1.10
539 grimes 1.15 // 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 grimes 1.10 for( size_t triggerNumber=0; triggerNumber<menu.numberOfTriggers(); ++triggerNumber )
544     {
545 grimes 1.15 cachedTriggers.push_back( createCachedTrigger( menu.getTrigger( triggerNumber ) ) );
546 grimes 1.10 }
547    
548     for( size_t eventNumber=0; eventNumber<numberOfEvents(); ++eventNumber )
549     {
550 grimes 1.13 const l1menu::IEvent& event=getEvent(eventNumber);
551 grimes 1.11 bool anyTriggerPassed=false;
552 grimes 1.10
553 grimes 1.15 for( size_t triggerNumber=0; triggerNumber<cachedTriggers.size(); ++triggerNumber )
554 grimes 1.10 {
555 grimes 1.15 if( cachedTriggers[triggerNumber]->apply(event) )
556 grimes 1.10 {
557     // If the event passes the trigger, increment the counter
558     ++numberOfEventsPassed[triggerNumber];
559 grimes 1.11 anyTriggerPassed=true;
560 grimes 1.10 }
561     }
562    
563 grimes 1.11 if( anyTriggerPassed ) ++numberOfEventsPassingAnyTrigger;
564 grimes 1.10 }
565    
566 grimes 1.15 l1menu::implementation::MenuRateImplementation* pRates=new l1menu::implementation::MenuRateImplementation;
567 grimes 1.11 // 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 grimes 1.14 pRates->setScaling( pImple_->eventRate );
571    
572 grimes 1.11 pRates->setTotalFraction( static_cast<float>(numberOfEventsPassingAnyTrigger)/static_cast<float>(numberOfEvents()) );
573 grimes 1.10
574     for( size_t triggerNumber=0; triggerNumber<numberOfEventsPassed.size(); ++triggerNumber )
575     {
576 grimes 1.11 float fraction=static_cast<float>(numberOfEventsPassed[triggerNumber])/static_cast<float>(numberOfEvents());
577 grimes 1.15 pRates->addTriggerRate( menu.getTrigger(triggerNumber), fraction );
578 grimes 1.10 }
579    
580 grimes 1.11 return pReturnValue;
581 grimes 1.10 }