ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/grimes/L1Menu/src/ReducedMenuSample.cpp
Revision: 1.5
Committed: Tue Jun 4 16:46:42 2013 UTC (11 years, 11 months ago) by grimes
Branch: MAIN
Changes since 1.4: +83 -8 lines
Log Message:
Changes to allow multiple samples to be added to a ReducedMenuSample, currently segfaults but I need to transfer the files between machines.

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