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.6 by grimes, Wed Jun 5 11:02:36 2013 UTC vs.
Revision 1.11 by grimes, Mon Jun 24 14:47:38 2013 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines