ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/grimes/L1Menu/src/ReducedMenuSample.cpp
(Generate patch)

Comparing UserCode/grimes/L1Menu/src/ReducedMenuSample.cpp (file contents):
Revision 1.5 by grimes, Tue Jun 4 16:46:42 2013 UTC vs.
Revision 1.16 by grimes, Thu Jul 4 00:37:53 2013 UTC

# Line 2 | Line 2
2  
3   #include <vector>
4   #include <stdexcept>
5 < #include <fstream>
6 < #include "l1menu/IReducedEvent.h"
5 > #include <fcntl.h>
6 > #include <algorithm>
7 > #include <iostream>
8 > #include <sstream>
9 > #include "l1menu/ReducedEvent.h"
10   #include "l1menu/MenuSample.h"
11   #include "l1menu/TriggerMenu.h"
12   #include "l1menu/ITrigger.h"
13 < #include "l1menu/tools.h"
13 > #include "l1menu/ICachedTrigger.h"
14 > #include "l1menu/L1TriggerDPGEvent.h"
15 > #include "l1menu/IEvent.h"
16 > #include "l1menu/IMenuRate.h"
17 > #include "l1menu/ITriggerRate.h"
18 > #include "l1menu/tools/tools.h"
19 > #include "l1menu/implementation/MenuRateImplementation.h"
20   #include "protobuf/l1menu.pb.h"
21 <
22 < #include <iostream>
21 > #include <google/protobuf/io/zero_copy_stream_impl.h>
22 > #include <google/protobuf/io/gzip_stream.h>
23  
24   namespace // unnamed namespace
25   {
26 <        class ReducedEventImplementation : public l1menu::IReducedEvent
26 >        /** @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 <                virtual ~ReducedEventImplementation() {}
34 <                virtual float parameterValue( size_t parameterNumber ) const {  return pThresholdValues->at(parameterNumber); }
35 <                virtual float weight() const { return *pWeight; }
36 <                void setWeight( float newWeight ) { *pWeight=newWeight; }
24 <                std::vector<float>* pThresholdValues;
25 <                float* pWeight;
33 >                UnixFileSentry( int fileDescriptor ) : fileDescriptor_(fileDescriptor) {}
34 >                ~UnixFileSentry() { close(fileDescriptor_); }
35 >        private:
36 >                int fileDescriptor_;
37          };
38 +
39 +        /** @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   }
88  
89   namespace l1menu
# Line 35 | Line 95 | namespace l1menu
95           */
96          class ReducedMenuSamplePrivateMembers
97          {
98 +        private:
99 +                l1menu::TriggerMenu mutableTriggerMenu_;
100          public:
101 <                ReducedMenuSamplePrivateMembers( size_t newNumberOfEvents, size_t newNumberOfParameters, const l1menu::TriggerMenu newTriggerMenu )
102 <                        : thresholdsForAllEvents( newNumberOfEvents, std::vector<float>(numberOfParameters) ),
103 <                          weights( newNumberOfEvents, 1 ), numberOfEvents( newNumberOfEvents ), triggerMenu( newTriggerMenu ),
104 <                          numberOfParameters( newNumberOfParameters )
105 <                {
106 <                        std::cout << __LINE__ << std::endl;
107 <                }
108 <                ReducedMenuSamplePrivateMembers( size_t newNumberOfParameters, const l1menu::TriggerMenu newTriggerMenu )
109 <                        : numberOfEvents( 0 ), triggerMenu( newTriggerMenu ), numberOfParameters( newNumberOfParameters )
110 <                {
111 <                        std::cout << __LINE__ << std::endl;
112 <                }
113 <                std::vector< std::vector<float> > thresholdsForAllEvents;
52 <                std::vector<float> weights;
53 <                ::ReducedEventImplementation event;
54 <                size_t numberOfEvents;
55 <                const l1menu::TriggerMenu triggerMenu;
56 <                const size_t numberOfParameters;
101 >                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 >                const l1menu::TriggerMenu& triggerMenu; // External const access to mutableTriggerMenu_
106 >                float eventRate;
107 >                float sumOfWeights;
108 >                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          };
115 +
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   }
120  
121 < l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::MenuSample& originalSample, const l1menu::TriggerMenu& triggerMenu )
121 > l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const l1menu::TriggerMenu& newTriggerMenu )
122 >        : mutableTriggerMenu_( newTriggerMenu ), event(thisObject), triggerMenu( mutableTriggerMenu_ ), eventRate(1), sumOfWeights(0)
123   {
124 <        size_t numberOfEvents=originalSample.numberOfEvents();
125 <        // Need to find out how many parameters there are for each event. Basically the sum
126 <        // of the number of thresholds for all triggers.
127 <        size_t numberOfParameters=0;
124 >        GOOGLE_PROTOBUF_VERIFY_VERSION;
125 >
126 >        // I need to copy the details of the trigger menu into the protobuf storage.
127 >        // 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          for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
130          {
131                  const l1menu::ITrigger& trigger=triggerMenu.getTrigger(triggerNumber);
132 <                numberOfParameters+=l1menu::getThresholdNames(trigger).size();
132 >
133 >                l1menuprotobuf::Trigger* pProtobufTrigger=protobufSampleHeader.add_trigger();
134 >                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 >                const auto thresholdNames=l1menu::tools::getThresholdNames(trigger);
151 >                for( const auto& thresholdName : thresholdNames ) pProtobufTrigger->add_varying_parameter(thresholdName);
152 >
153 >        } // end of loop over triggers
154 >
155 >        // 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 > }
160 >
161 > l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const std::string& filename )
162 >        : event(thisObject), triggerMenu(mutableTriggerMenu_), eventRate(1), sumOfWeights(0)
163 > {
164 >        GOOGLE_PROTOBUF_VERIFY_VERSION;
165 >
166 >        // 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 >
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 <        // Now I know how many events there are and how many parameters, I can create the pimple
193 <        // with the correct parameters.
194 <        pImple_.reset( new l1menu::ReducedMenuSamplePrivateMembers( numberOfEvents, numberOfParameters, triggerMenu ) );
192 >        google::protobuf::io::GzipInputStream gzipInput( &fileInput );
193 >        google::protobuf::io::CodedInputStream codedInput( &gzipInput );
194 >
195 >        // 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 >        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 <        //
210 <        // 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 )
209 >        // Keep looping until there is nothing more to be read from the file.
210 >        while( codedInput.ReadVarint64( &messageSize ) )
211          {
212 <                const l1menu::IEvent& event=originalSample.getEvent( eventNumber );
83 <                std::vector<float>& parameters=pImple_->thresholdsForAllEvents[eventNumber];
212 >                readLimit=codedInput.PushLimit(messageSize);
213  
214 <                size_t parameterNumber=0;
215 <                // Loop over all of the triggers
216 <                for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
214 >                // 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 <                        std::unique_ptr<l1menu::ITrigger> pTrigger=triggerMenu.getTriggerCopy(triggerNumber);
220 <                        std::vector<std::string> thresholdNames=getThresholdNames(*pTrigger);
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 >                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 <                        try
227 <                        {
228 <                                setTriggerThresholdsAsTightAsPossible( event, *pTrigger, 0.001 );
229 <                                // Set all of the parameters to match the thresholds in the trigger
230 <                                for( const auto& thresholdName : thresholdNames )
231 <                                {
232 <                                        parameters[parameterNumber]=pTrigger->parameter(thresholdName);
233 <                                        ++parameterNumber;
234 <                                }
235 <                        }
236 <                        catch( std::exception& error )
237 <                        {
238 <                                // setTriggerThresholdsAsTightAsPossible() couldn't find thresholds so record
239 <                                // -1 for everything.
240 <                                for( size_t index=0; index<thresholdNames.size(); ++index )
241 <                                {
242 <                                        parameters[parameterNumber]=-1;
243 <                                        ++parameterNumber;
244 <                                }
245 <                        } // end of try block that sets the trigger thresholds
226 >                codedInput.PopLimit(readLimit);
227 >        }
228 >
229 >
230 >        // 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 >        // Count up the sum of the weights of all events
239 >        for( const auto& pRun : protobufRuns )
240 >        {
241 >                sumOfWeights+=sumWeights( *pRun );
242 >        }
243 >
244 >        // I have all of the information in the protobuf members, but I also need the trigger information
245 >        // in the form of l1menu::TriggerMenu. Copy out the required information.
246 >        for( int triggerNumber=0; triggerNumber<protobufSampleHeader.trigger_size(); ++triggerNumber )
247 >        {
248 >                const l1menuprotobuf::Trigger& inputTrigger=protobufSampleHeader.trigger(triggerNumber);
249 >
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 >
254 >                // 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 >                {
258 >                        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  
265 <                } // end of loop over triggers
266 <        } // end of loop over events
265 > }
266 >
267 > l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::MenuSample& originalSample, const l1menu::TriggerMenu& triggerMenu )
268 >        : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
269 > {
270 >        addSample( originalSample );
271   }
272  
273   l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::TriggerMenu& triggerMenu )
274 +        : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
275   {
276 <        // Need to find out how many parameters there are for each event. Basically the sum
277 <        // of the number of thresholds for all triggers.
278 <        size_t numberOfParameters=0;
279 <        for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
280 <        {
281 <                const l1menu::ITrigger& trigger=triggerMenu.getTrigger(triggerNumber);
282 <                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;
276 >        // No operation besides the initialiser list
277 > }
278 >
279 > 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   l1menu::ReducedMenuSample::~ReducedMenuSample()
# Line 141 | Line 291 | l1menu::ReducedMenuSample::~ReducedMenuS
291  
292   void l1menu::ReducedMenuSample::addSample( const l1menu::MenuSample& originalSample )
293   {
294 <        size_t oldNumberEvents=pImple_->numberOfEvents;
145 <        pImple_->numberOfEvents=oldNumberEvents+originalSample.numberOfEvents();
294 >        l1menuprotobuf::Run* pCurrentRun=pImple_->protobufRuns.back().get();
295  
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        //
296          for( size_t eventNumber=0; eventNumber<originalSample.numberOfEvents(); ++eventNumber )
297          {
298 <                size_t bufferEventNumber=eventNumber+oldNumberEvents;
298 >                // Split the events up into groups in arbitrary numbers. This is to get around
299 >                // a protobuf aversion to long messages.
300 >                if( pCurrentRun->event_size() >= pImple_->EVENTS_PER_RUN )
301 >                {
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 <                const l1menu::IEvent& event=originalSample.getEvent( eventNumber );
310 <                std::vector<float>& parameters=pImple_->thresholdsForAllEvents[bufferEventNumber];
309 >                const l1menu::L1TriggerDPGEvent& event=originalSample.getFullEvent( eventNumber );
310 >                l1menuprotobuf::Event* pProtobufEvent=pCurrentRun->add_event();
311 >                if( event.weight()!=1 ) pProtobufEvent->set_weight( event.weight() );
312  
162                size_t parameterNumber=0;
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 <                        std::vector<std::string> thresholdNames=getThresholdNames(*pTrigger);
317 >                        std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(*pTrigger);
318  
319                          try
320                          {
321 <                                setTriggerThresholdsAsTightAsPossible( event, *pTrigger, 0.001 );
321 >                                l1menu::tools::setTriggerThresholdsAsTightAsPossible( event, *pTrigger, 0.001 );
322                                  // Set all of the parameters to match the thresholds in the trigger
323                                  for( const auto& thresholdName : thresholdNames )
324                                  {
325 <                                        parameters[parameterNumber]=pTrigger->parameter(thresholdName);
176 <                                        ++parameterNumber;
325 >                                        pProtobufEvent->add_threshold( pTrigger->parameter(thresholdName) );
326                                  }
327                          }
328                          catch( std::exception& error )
329                          {
330                                  // setTriggerThresholdsAsTightAsPossible() couldn't find thresholds so record
331                                  // -1 for everything.
332 <                                for( size_t index=0; index<thresholdNames.size(); ++index )
333 <                                {
185 <                                        parameters[parameterNumber]=-1;
186 <                                        ++parameterNumber;
187 <                                }
332 >                                // Range based for loop gives me a warning because I don't use the thresholdName.
333 >                                for( size_t index=0; index<thresholdNames.size(); ++index ) pProtobufEvent->add_threshold(-1);
334                          } // end of try block that sets the trigger thresholds
335  
336                  } // end of loop over triggers
191        } // end of loop over events
337  
338 +                pImple_->sumOfWeights+=event.weight();
339 +        } // end of loop over events
340   }
341  
342 < l1menu::ReducedMenuSample::ReducedMenuSample( const std::string& filename )
342 > void l1menu::ReducedMenuSample::saveToFile( const std::string& filename ) const
343   {
344 <        GOOGLE_PROTOBUF_VERIFY_VERSION;
345 <
346 <        l1menuprotobuf::Sample inputSample;
347 <        { // new block to limit scope of variables
348 <                std::ifstream inputFile( filename );
349 <                inputSample.ParseFromIstream( &inputFile );
344 >        // 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 >
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 <        l1menu::TriggerMenu triggerMenu;
365 >        google::protobuf::io::GzipOutputStream gzipOutput( &fileOutput );
366 >        google::protobuf::io::CodedOutputStream codedOutput( &gzipOutput );
367  
368 <        size_t totalNumberOfThresholds=0;
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 <        for( int triggerNumber=0; triggerNumber<inputSample.trigger_size(); ++triggerNumber )
373 >        // Now go through each of the runs and do the same for those
374 >        for( const auto& pRun : pImple_->protobufRuns )
375          {
376 <                const l1menuprotobuf::Trigger& inputTrigger=inputSample.trigger(triggerNumber);
377 <
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 <                // many there are so that I can initialise the buffer that holds the event data.
227 <                totalNumberOfThresholds+=inputTrigger.threshold_size();
376 >                codedOutput.WriteVarint64( pRun->ByteSize() );
377 >                pRun->SerializeToCodedStream( &codedOutput );
378          }
379  
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                // 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                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                // Make a note of the names of the parameters that are recorded for each event.
277                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 );
380   }
381  
382   size_t l1menu::ReducedMenuSample::numberOfEvents() const
383   {
384 <        return pImple_->numberOfEvents;
385 < }
386 <
322 < const l1menu::IReducedEvent& l1menu::ReducedMenuSample::getEvent( size_t eventNumber ) const
323 < {
324 <        pImple_->event.pThresholdValues=&pImple_->thresholdsForAllEvents[eventNumber];
325 <        pImple_->event.pWeight=&pImple_->weights[eventNumber];
326 <        return pImple_->event;
384 >        size_t numberOfEvents=0;
385 >        for( const auto& pRun : pImple_->protobufRuns ) numberOfEvents+=pRun->event_size();
386 >        return numberOfEvents;
387   }
388  
389   const l1menu::TriggerMenu& l1menu::ReducedMenuSample::getTriggerMenu() const
# Line 354 | Line 414 | bool l1menu::ReducedMenuSample::contains
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 <                std::vector<std::string> parameterNames=getNonThresholdParameterNames( trigger );
417 >                std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
418                  bool allParametersMatch=true;
419                  for( const auto& parameterName : parameterNames )
420                  {
# Line 400 | Line 460 | const std::map<std::string,size_t> l1men
460                  // ReducedMenuSample.
461                  if( triggerWasFound ) // Trigger can still fail, but no point doing this check if it already has
462                  {
463 <                        std::vector<std::string> parameterNames=getNonThresholdParameterNames( trigger );
463 >                        std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
464                          for( const auto& parameterName : parameterNames )
465                          {
466                                  if( trigger.parameter(parameterName)!=triggerInMenu.parameter(parameterName) ) triggerWasFound=false;
467                          }
468                  }
469  
470 <                std::vector<std::string> thresholdNames=l1menu::getThresholdNames(triggerInMenu);
470 >                std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(triggerInMenu);
471                  if( triggerWasFound )
472                  {
473                          for( const auto& thresholdName : thresholdNames )
# Line 424 | Line 484 | const std::map<std::string,size_t> l1men
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 <        if( !triggerWasFound ) throw std::runtime_error( "l1menu::ReducedMenuSample::getTriggerParameterIdentifiers() called for a trigger that was not used to create the sample" );
487 >        if( !triggerWasFound ) throw std::runtime_error( "l1menu::ReducedMenuSample::getTriggerParameterIdentifiers() called for a trigger that was not used to create the sample - "+trigger.name() );
488  
489          return returnValue;
490   }
491 +
492 + 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 + float l1menu::ReducedMenuSample::eventRate() const
517 + {
518 +        return pImple_->eventRate;
519 + }
520 +
521 + void l1menu::ReducedMenuSample::setEventRate( float rate )
522 + {
523 +        pImple_->eventRate=rate;
524 + }
525 +
526 + float l1menu::ReducedMenuSample::sumOfWeights() const
527 + {
528 +        return pImple_->sumOfWeights;
529 + }
530 +
531 + std::unique_ptr<const l1menu::IMenuRate> l1menu::ReducedMenuSample::rate( const l1menu::TriggerMenu& menu ) const
532 + {
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 +        float numberOfEventsPassingAnyTrigger;
538 +
539 +        // 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 +        for( size_t triggerNumber=0; triggerNumber<menu.numberOfTriggers(); ++triggerNumber )
544 +        {
545 +                cachedTriggers.push_back( createCachedTrigger( menu.getTrigger( triggerNumber ) ) );
546 +        }
547 +
548 +        for( size_t eventNumber=0; eventNumber<numberOfEvents(); ++eventNumber )
549 +        {
550 +                const l1menu::IEvent& event=getEvent(eventNumber);
551 +                bool anyTriggerPassed=false;
552 +
553 +                for( size_t triggerNumber=0; triggerNumber<cachedTriggers.size(); ++triggerNumber )
554 +                {
555 +                        if( cachedTriggers[triggerNumber]->apply(event) )
556 +                        {
557 +                                // If the event passes the trigger, increment the counter
558 +                                ++numberOfEventsPassed[triggerNumber];
559 +                                anyTriggerPassed=true;
560 +                        }
561 +                }
562 +
563 +                if( anyTriggerPassed ) ++numberOfEventsPassingAnyTrigger;
564 +        }
565 +
566 +        l1menu::implementation::MenuRateImplementation* pRates=new l1menu::implementation::MenuRateImplementation;
567 +        // 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 +        pRates->setScaling( pImple_->eventRate );
571 +
572 +        pRates->setTotalFraction( static_cast<float>(numberOfEventsPassingAnyTrigger)/static_cast<float>(numberOfEvents()) );
573 +
574 +        for( size_t triggerNumber=0; triggerNumber<numberOfEventsPassed.size(); ++triggerNumber )
575 +        {
576 +                float fraction=static_cast<float>(numberOfEventsPassed[triggerNumber])/static_cast<float>(numberOfEvents());
577 +                pRates->addTriggerRate( menu.getTrigger(triggerNumber), fraction );
578 +        }
579 +
580 +        return pReturnValue;
581 + }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines