ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/grimes/L1Menu/src/ReducedMenuSample.cpp
Revision: 1.2
Committed: Fri May 31 01:29:10 2013 UTC (11 years, 11 months ago) by grimes
Branch: MAIN
Changes since 1.1: +291 -7 lines
Log Message:
Various improvements, including protobuf input and output for ReducedMenuSamples.

File Contents

# User Rev Content
1 grimes 1.1 #include "l1menu/ReducedMenuSample.h"
2    
3     #include <vector>
4 grimes 1.2 #include <stdexcept>
5     #include <fstream>
6 grimes 1.1 #include "l1menu/IReducedEvent.h"
7     #include "l1menu/MenuSample.h"
8     #include "l1menu/TriggerMenu.h"
9 grimes 1.2 #include "l1menu/ITrigger.h"
10 grimes 1.1 #include "l1menu/tools.h"
11 grimes 1.2 #include "protobuf/l1menu.pb.h"
12 grimes 1.1
13     namespace // unnamed namespace
14     {
15     class ReducedEventImplementation : public l1menu::IReducedEvent
16     {
17     public:
18     virtual ~ReducedEventImplementation() {}
19 grimes 1.2 virtual float parameterValue( size_t parameterNumber ) const { return pThresholdValues->at(parameterNumber); }
20     virtual float weight() const { return *pWeight; }
21     void setWeight( float newWeight ) { *pWeight=newWeight; }
22 grimes 1.1 std::vector<float>* pThresholdValues;
23 grimes 1.2 float* pWeight;
24 grimes 1.1 };
25     }
26    
27     namespace l1menu
28     {
29     /** @brief Private members for the ReducedMenuSample class
30     *
31     * @author Mark Grimes (mark.grimes@bristol.ac.uk)
32     * @date 28/May/2013
33     */
34     class ReducedMenuSamplePrivateMembers
35     {
36     public:
37 grimes 1.2 ReducedMenuSamplePrivateMembers( size_t newNumberOfEvents, size_t numberOfParameters, const l1menu::TriggerMenu newTriggerMenu )
38     : thresholdsForAllEvents( newNumberOfEvents, std::vector<float>(numberOfParameters) ),
39     weights( newNumberOfEvents ), numberOfEvents( newNumberOfEvents ), triggerMenu( newTriggerMenu )
40 grimes 1.1 {
41 grimes 1.2 // Run through every event and default every weight to 1.
42     for( auto& eventWeight : weights )
43     {
44     eventWeight=1;
45     }
46 grimes 1.1 }
47     std::vector< std::vector<float> > thresholdsForAllEvents;
48 grimes 1.2 std::vector<float> weights;
49 grimes 1.1 ::ReducedEventImplementation event;
50     size_t numberOfEvents;
51 grimes 1.2 const l1menu::TriggerMenu triggerMenu;
52 grimes 1.1 };
53     }
54    
55     l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::MenuSample& originalSample, const l1menu::TriggerMenu& triggerMenu )
56     {
57     size_t numberOfEvents=originalSample.numberOfEvents();
58     // Need to find out how many parameters there are for each event. Basically the sum
59     // of the number of thresholds for all triggers.
60     size_t numberOfParameters=0;
61     for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
62     {
63     const l1menu::ITrigger& trigger=triggerMenu.getTrigger(triggerNumber);
64     numberOfParameters+=l1menu::getThresholdNames(trigger).size();
65     }
66    
67     // Now I know how many events there are and how many parameters, I can create the pimple
68     // with the correct parameters.
69 grimes 1.2 pImple_.reset( new l1menu::ReducedMenuSamplePrivateMembers( numberOfEvents, numberOfParameters, triggerMenu ) );
70    
71     //
72     // Now I've set the storage to the correct size, run through each event
73     // and fill with the correct values.
74     //
75     for( size_t eventNumber=0; eventNumber<numberOfEvents; ++eventNumber )
76     {
77     const l1menu::IEvent& event=originalSample.getEvent( eventNumber );
78     std::vector<float>& parameters=pImple_->thresholdsForAllEvents[eventNumber];
79    
80     size_t parameterNumber=0;
81     // Loop over all of the triggers
82     for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
83     {
84     std::unique_ptr<l1menu::ITrigger> pTrigger=triggerMenu.getTriggerCopy(triggerNumber);
85     std::vector<std::string> thresholdNames=getThresholdNames(*pTrigger);
86    
87     try
88     {
89     setTriggerThresholdsAsTightAsPossible( event, *pTrigger, 0.001 );
90     // Set all of the parameters to match the thresholds in the trigger
91     for( const auto& thresholdName : thresholdNames )
92     {
93     parameters[parameterNumber]=pTrigger->parameter(thresholdName);
94     ++parameterNumber;
95     }
96     }
97     catch( std::exception& error )
98     {
99     // setTriggerThresholdsAsTightAsPossible() couldn't find thresholds so record
100     // -1 for everything.
101     for( size_t index=0; index<thresholdNames.size(); ++index )
102     {
103     parameters[parameterNumber]=-1;
104     ++parameterNumber;
105     }
106     } // end of try block that sets the trigger thresholds
107    
108     } // end of loop over triggers
109     } // end of loop over events
110     }
111    
112     l1menu::ReducedMenuSample::~ReducedMenuSample()
113     {
114     // No operation. Just need one defined otherwise the default one messes up
115     // the unique_ptr deletion because ReducedMenuSamplePrivateMembers isn't
116     // defined elsewhere.
117     }
118    
119     l1menu::ReducedMenuSample::ReducedMenuSample( const std::string& filename )
120     {
121     GOOGLE_PROTOBUF_VERIFY_VERSION;
122    
123     l1menuprotobuf::Sample inputSample;
124     { // new block to limit scope of variables
125     std::ifstream inputFile( filename );
126     inputSample.ParseFromIstream( &inputFile );
127     }
128    
129     l1menu::TriggerMenu triggerMenu;
130    
131     size_t totalNumberOfThresholds=0;
132    
133     for( int triggerNumber=0; triggerNumber<inputSample.trigger_size(); ++triggerNumber )
134     {
135     const l1menuprotobuf::Trigger& inputTrigger=inputSample.trigger(triggerNumber);
136    
137     triggerMenu.addTrigger( inputTrigger.name(), inputTrigger.version() );
138     // Get a reference to the trigger I just created
139     l1menu::ITrigger& trigger=triggerMenu.getTrigger(triggerNumber);
140    
141     // Run through all of the parameters and set them to what they were
142     // when the sample was made.
143     for( int parameterNumber=0; parameterNumber<inputTrigger.parameter_size(); ++parameterNumber )
144     {
145     const auto& inputParameter=inputTrigger.parameter(parameterNumber);
146     trigger.parameter(inputParameter.name())=inputParameter.value();
147     }
148    
149     // I should probably check the threshold names exist. At the moment I just see how
150     // many there are so that can initialise the buffer that holds the event data.
151     totalNumberOfThresholds+=inputTrigger.threshold_size();
152     }
153    
154     // Now I have the menu set up as much as I need it (thresholds aren't set
155     // but I don't care about those). I can initialise the ReducedMenuSamplePrivateMembers
156     // pImple_ with the menu and the correct sizes for the data buffer.
157     pImple_.reset( new l1menu::ReducedMenuSamplePrivateMembers( inputSample.event_size(), totalNumberOfThresholds, triggerMenu ) );
158    
159     // Now run over each event from the input file
160     for( int eventNumber=0; eventNumber<inputSample.event_size(); ++eventNumber )
161     {
162     auto& thresholdsForEvent=pImple_->thresholdsForAllEvents[eventNumber];
163     const auto& inputEvent=inputSample.event(eventNumber);
164    
165     for( int thresholdNumber=0; thresholdNumber<inputEvent.threshold_size(); ++thresholdNumber )
166     {
167     thresholdsForEvent[thresholdNumber]=inputEvent.threshold(thresholdNumber);
168     }
169    
170     // See if the weight was stored and set it if it has
171     if( inputEvent.has_weight() ) pImple_->weights[eventNumber]=inputEvent.weight();
172     }
173     }
174    
175     void l1menu::ReducedMenuSample::saveToFile( const std::string& filename ) const
176     {
177     GOOGLE_PROTOBUF_VERIFY_VERSION;
178    
179     l1menuprotobuf::Sample outputSample;
180    
181     for( size_t triggerNumber=0; triggerNumber<pImple_->triggerMenu.numberOfTriggers(); ++triggerNumber )
182     {
183     const l1menu::ITrigger& trigger=pImple_->triggerMenu.getTrigger(triggerNumber);
184    
185     l1menuprotobuf::Trigger* pOutputTrigger=outputSample.add_trigger();
186     pOutputTrigger->set_name( trigger.name() );
187     pOutputTrigger->set_version( trigger.version() );
188    
189     // For the parameters that aren't thresholds, record the name and value.
190     const auto parameterNames=l1menu::getNonThresholdParameterNames(trigger);
191     for( const auto& parameterName : parameterNames )
192     {
193     l1menuprotobuf::Trigger_TriggerParameter* pOutputParameter=pOutputTrigger->add_parameter();
194     pOutputParameter->set_name(parameterName);
195     pOutputParameter->set_value( trigger.parameter(parameterName) );
196     }
197    
198     // For the parameters that are thresholds, just record the name. The value is
199     // recorded in each event.
200     const auto thresholdNames=l1menu::getThresholdNames(trigger);
201     for( const auto& thresholdName : thresholdNames ) pOutputTrigger->add_threshold(thresholdName);
202    
203     } // end of loop over triggers
204    
205     //
206     // I've done everything for what is effectively the header. I now need
207     // to run through each event and record the thresholds for each one.
208     //
209     for( const auto& thresholdsForEvent : pImple_->thresholdsForAllEvents )
210     {
211     l1menuprotobuf::Event* pOutputEvent=outputSample.add_event();
212     for( const auto& threshold : thresholdsForEvent )
213     {
214     pOutputEvent->add_threshold( threshold );
215     }
216     }
217    
218     // I should have the correct number of events in the protobuf version of
219     // the sample now. I'll run through and set the event weights as well, but
220     // I'll only add it if it's different to 1. I think this saves space in
221     // the file.
222     for( int eventNumber=0; eventNumber<outputSample.event_size(); ++eventNumber )
223     {
224     if( pImple_->weights[eventNumber]!=1 )
225     {
226     l1menuprotobuf::Event* pOutputEvent=outputSample.mutable_event(eventNumber);
227     pOutputEvent->set_weight( pImple_->weights[eventNumber] );
228     }
229     }
230    
231    
232     //
233     // Everything should be set up in the class now, so I can open the
234     // output file and write to it.
235     //
236     std::ofstream outputFile( filename );
237     outputSample.SerializeToOstream( &outputFile );
238 grimes 1.1 }
239    
240     size_t l1menu::ReducedMenuSample::numberOfEvents() const
241     {
242     return pImple_->numberOfEvents;
243     }
244    
245     const l1menu::IReducedEvent& l1menu::ReducedMenuSample::getEvent( size_t eventNumber ) const
246     {
247     pImple_->event.pThresholdValues=&pImple_->thresholdsForAllEvents[eventNumber];
248 grimes 1.2 pImple_->event.pWeight=&pImple_->weights[eventNumber];
249 grimes 1.1 return pImple_->event;
250     }
251 grimes 1.2
252     const l1menu::TriggerMenu& l1menu::ReducedMenuSample::getTriggerMenu() const
253     {
254     return pImple_->triggerMenu;
255     }
256    
257     bool l1menu::ReducedMenuSample::containsTrigger( const l1menu::ITrigger& trigger, bool allowOlderVersion ) const
258     {
259     // Loop over all of the triggers in the menu, and see if there is one
260     // where the name and version match.
261     for( size_t triggerNumber=0; triggerNumber<pImple_->triggerMenu.numberOfTriggers(); ++triggerNumber )
262     {
263     const l1menu::ITrigger& triggerInMenu=pImple_->triggerMenu.getTrigger(triggerNumber);
264     if( triggerInMenu.name()!=trigger.name() ) continue;
265     if( allowOlderVersion )
266     {
267     if( triggerInMenu.version()>trigger.version() ) continue;
268     }
269     else
270     {
271     if( triggerInMenu.version()!=trigger.version() ) continue;
272     }
273    
274     // If control got this far then there is a trigger with the required name
275     // and sufficient version. I now need to check all of the non threshold parameters
276     // to make sure they match, i.e. make sure the ReducedSample was made with the same
277     // eta cuts or whatever.
278     // I don't care if the thresholds don't match because that's what's stored in the
279     // ReducedMenuSample.
280     std::vector<std::string> parameterNames=getNonThresholdParameterNames( trigger );
281     bool allParametersMatch=true;
282     for( const auto& parameterName : parameterNames )
283     {
284     if( trigger.parameter(parameterName)!=triggerInMenu.parameter(parameterName) ) allParametersMatch=false;
285     }
286    
287     if( allParametersMatch ) return true;
288     } // end of loop over triggers
289    
290     // If control got this far then no trigger was found that matched
291     return false;
292     }
293    
294     const std::map<std::string,size_t> l1menu::ReducedMenuSample::getTriggerParameterIdentifiers( const l1menu::ITrigger& trigger, bool allowOlderVersion ) const
295     {
296     std::map<std::string,size_t> returnValue;
297    
298     // Need to find out how many parameters there are for each event. Basically the sum
299     // of the number of thresholds for all triggers.
300     size_t parameterNumber=0;
301     bool triggerWasFound=true;
302     for( size_t triggerNumber=0; triggerNumber<pImple_->triggerMenu.numberOfTriggers(); ++triggerNumber )
303     {
304     const l1menu::ITrigger& triggerInMenu=pImple_->triggerMenu.getTrigger(triggerNumber);
305    
306     triggerWasFound=true; // Set to true, then back to false if any of the tests fail
307     // See if this trigger in the menu is the same as the one passed as a parameter
308     if( triggerInMenu.name()!=trigger.name() ) triggerWasFound=false;
309     if( allowOlderVersion )
310     {
311     if( triggerInMenu.version()>trigger.version() ) triggerWasFound=false;
312     }
313     else
314     {
315     if( triggerInMenu.version()!=trigger.version() ) triggerWasFound=false;
316     }
317    
318     // If control got this far then there is a trigger with the required name
319     // and sufficient version. I now need to check all of the non threshold parameters
320     // to make sure they match, i.e. make sure the ReducedSample was made with the same
321     // eta cuts or whatever.
322     // I don't care if the thresholds don't match because that's what's stored in the
323     // ReducedMenuSample.
324     if( triggerWasFound ) // Trigger can still fail, but no point doing this check if it already has
325     {
326     std::vector<std::string> parameterNames=getNonThresholdParameterNames( trigger );
327     for( const auto& parameterName : parameterNames )
328     {
329     if( trigger.parameter(parameterName)!=triggerInMenu.parameter(parameterName) ) triggerWasFound=false;
330     }
331     }
332    
333     std::vector<std::string> thresholdNames=l1menu::getThresholdNames(trigger);
334     if( triggerWasFound )
335     {
336     for( const auto& thresholdName : thresholdNames )
337     {
338     returnValue[thresholdName]=parameterNumber;
339     ++parameterNumber;
340     }
341     break;
342     }
343     else parameterNumber+=thresholdNames.size();
344     }
345    
346     // There could conceivably be a trigger that was found but has no thresholds
347     // (I guess - it would be a pretty pointless trigger though). To indicate the
348     // difference between that and a trigger that wasn't found I'll respectively
349     // return the empty vector or throw an exception.
350     if( !triggerWasFound ) throw std::runtime_error( "l1menu::ReducedMenuSample::getTriggerParameterIdentifiers() called for a trigger that was not used to create the sample" );
351    
352     return returnValue;
353     }