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.13 by grimes, Fri Jun 28 14:30:08 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 "protobuf/l1menu.pb.h"
20 <
21 < #include <iostream>
20 > #include <google/protobuf/io/zero_copy_stream_impl.h>
21 > #include <google/protobuf/io/gzip_stream.h>
22  
23   namespace // unnamed namespace
24   {
25 <        class ReducedEventImplementation : public l1menu::IReducedEvent
25 >        /** @brief Sentry that closes a Unix file descriptor when it goes out of scope.
26 >         * @author Mark Grimes (mark.grimes@bristol.ac.uk)
27 >         * @date 07/Jun/2013
28 >         */
29 >        class UnixFileSentry
30          {
31          public:
32 <                virtual ~ReducedEventImplementation() {}
33 <                virtual float parameterValue( size_t parameterNumber ) const {  return pThresholdValues->at(parameterNumber); }
34 <                virtual float weight() const { return *pWeight; }
35 <                void setWeight( float newWeight ) { *pWeight=newWeight; }
36 <                std::vector<float>* pThresholdValues;
37 <                float* pWeight;
32 >                UnixFileSentry( int fileDescriptor ) : fileDescriptor_(fileDescriptor) {}
33 >                ~UnixFileSentry() { close(fileDescriptor_); }
34 >        private:
35 >                int fileDescriptor_;
36 >        };
37 >
38 >        class MenuRateImplementation;
39 >
40 >        class TriggerRateImplementation : public l1menu::ITriggerRate
41 >        {
42 >        public:
43 >                TriggerRateImplementation( const l1menu::ITrigger& trigger, float fraction, const MenuRateImplementation& menuRate );
44 >                TriggerRateImplementation& operator=( TriggerRateImplementation&& otherTriggerRate ); // Move assignment
45 >                virtual ~TriggerRateImplementation();
46 >
47 >                // Methods required by the l1menu::ITriggerRate interface
48 >                virtual const l1menu::ITrigger& trigger() const;
49 >                virtual float fraction() const;
50 >                virtual float rate() const;
51 >        protected:
52 >                std::unique_ptr<l1menu::ITrigger> pTrigger_;
53 >                float fraction_;
54 >                const MenuRateImplementation& menuRate_;
55 >        };
56 >
57 >        class MenuRateImplementation : public l1menu::IMenuRate
58 >        {
59 >        public:
60 >                void setTotalFraction( float fraction ) { totalFraction_=fraction; }
61 >                void setScaling( float scaling ) { scaling_=scaling; }
62 >                float scaling() const { return scaling_; }
63 >                void addTriggerRate( const l1menu::ITrigger& trigger, float fraction ) { triggerRates_.push_back( std::move(TriggerRateImplementation(trigger,fraction,*this)) ); }
64 >                // Methods required by the l1menu::IMenuRate interface
65 >                virtual float totalFraction() const { return totalFraction_; }
66 >                virtual float totalRate() const { return totalFraction_*scaling_; }
67 >                virtual const std::vector<const l1menu::ITriggerRate*>& triggerRates() const
68 >                {
69 >                        // If the sizes are the same I'll assume nothing has changed and the references
70 >                        // are still valid. I don't expect this method to be called until the triggerRates_
71 >                        // vector is complete anyway.
72 >                        if( triggerRates_.size()!=baseClassReferences_.size() )
73 >                        {
74 >                                baseClassReferences_.clear();
75 >                                for( const auto& triggerRate : triggerRates_ )
76 >                                {
77 >                                        baseClassReferences_.push_back( &triggerRate );
78 >                                }
79 >                        }
80 >
81 >                        return baseClassReferences_;
82 >                }
83 >
84 >        protected:
85 >                float totalFraction_;
86 >                float scaling_;
87 >                std::vector<TriggerRateImplementation> triggerRates_;
88 >                mutable std::vector<const l1menu::ITriggerRate*> baseClassReferences_;
89          };
90 +
91 +        /** @brief An object that stores pointers to trigger parameters to avoid costly string comparisons.
92 +         *
93 +         * @author Mark Grimes (mark.grimes@bristol.ac.uk)
94 +         * @date 26/Jun/2013
95 +         */
96 +        class CachedTriggerImplementation : public l1menu::ICachedTrigger
97 +        {
98 +        public:
99 +                CachedTriggerImplementation( const l1menu::ReducedMenuSample& sample, const l1menu::ITrigger& trigger )
100 +                {
101 +                        const auto& parameterIdentifiers=sample.getTriggerParameterIdentifiers(trigger);
102 +
103 +                        for( const auto& identifier : parameterIdentifiers )
104 +                        {
105 +                                identifiers_.push_back( std::make_pair( identifier.second, &trigger.parameter(identifier.first) ) );
106 +                        }
107 +                }
108 +                virtual bool apply( const l1menu::IEvent& event )
109 +                {
110 +                        // Not happy using a static_cast, but this method is called in many, many loops.
111 +                        // I should probably find out how much faster this is than a dynamic_cast, maybe
112 +                        // it's not even worth it. Anyway, I'm banking that no one will ever pass an
113 +                        // event that wasn't created with the same sample that this proxy was created
114 +                        // with.
115 +                        const l1menu::ReducedEvent* pEvent=static_cast<const l1menu::ReducedEvent*>(&event);
116 +                        for( const auto& identifier : identifiers_ )
117 +                        {
118 +                                if( pEvent->parameterValue(identifier.first) < *identifier.second ) return false;
119 +                        }
120 +
121 +                        // If control got this far then all of the thresholds passed, and
122 +                        // I can pass the event.
123 +                        return true;
124 +                }
125 +        protected:
126 +                std::vector< std::pair<l1menu::ReducedEvent::ParameterID,const float*> > identifiers_;
127 +        }; // end of class ReducedSampleCachedTrigger
128 +
129 +        TriggerRateImplementation::TriggerRateImplementation( const l1menu::ITrigger& trigger, float fraction, const MenuRateImplementation& menuRate )
130 +                : fraction_(fraction), menuRate_(menuRate)
131 +        {
132 +                pTrigger_=std::move( l1menu::TriggerTable::instance().copyTrigger(trigger) );
133 +        }
134 +        TriggerRateImplementation& TriggerRateImplementation::operator=( TriggerRateImplementation&& otherTriggerRate )
135 +        {
136 +                pTrigger_=std::move( otherTriggerRate.pTrigger_ );
137 +                fraction_=otherTriggerRate.fraction_;
138 +                // I can't change the menuRate_ reference, but that should already be set to the right one anyway.
139 +                return *this;
140 +        }
141 +        TriggerRateImplementation::~TriggerRateImplementation() {}
142 +        const l1menu::ITrigger& TriggerRateImplementation::trigger() const { return *pTrigger_; }
143 +        float TriggerRateImplementation::fraction() const { return fraction_; }
144 +        float TriggerRateImplementation::rate() const { return fraction_*menuRate_.scaling(); }
145 +
146 +        float sumWeights( const l1menuprotobuf::Run& run )
147 +        {
148 +                float returnValue=0;
149 +                for( const auto& event : run.event() )
150 +                {
151 +                        if( event.has_weight() ) returnValue+=event.weight();
152 +                        else returnValue+=1;
153 +                }
154 +                return returnValue;
155 +        }
156   }
157  
158   namespace l1menu
# Line 35 | Line 164 | namespace l1menu
164           */
165          class ReducedMenuSamplePrivateMembers
166          {
167 +        private:
168 +                l1menu::TriggerMenu mutableTriggerMenu_;
169          public:
170 <                ReducedMenuSamplePrivateMembers( size_t newNumberOfEvents, size_t newNumberOfParameters, const l1menu::TriggerMenu newTriggerMenu )
171 <                        : thresholdsForAllEvents( newNumberOfEvents, std::vector<float>(numberOfParameters) ),
172 <                          weights( newNumberOfEvents, 1 ), numberOfEvents( newNumberOfEvents ), triggerMenu( newTriggerMenu ),
173 <                          numberOfParameters( newNumberOfParameters )
174 <                {
175 <                        std::cout << __LINE__ << std::endl;
176 <                }
177 <                ReducedMenuSamplePrivateMembers( size_t newNumberOfParameters, const l1menu::TriggerMenu newTriggerMenu )
178 <                        : numberOfEvents( 0 ), triggerMenu( newTriggerMenu ), numberOfParameters( newNumberOfParameters )
179 <                {
180 <                        std::cout << __LINE__ << std::endl;
181 <                }
182 <                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;
170 >                ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const l1menu::TriggerMenu& newTriggerMenu );
171 >                ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const std::string& filename );
172 >                //void copyMenuToProtobufSample();
173 >                l1menu::ReducedEvent event;
174 >                const l1menu::TriggerMenu& triggerMenu; // External const access to mutableTriggerMenu_
175 >                float eventRate;
176 >                float sumOfWeights;
177 >                l1menuprotobuf::SampleHeader protobufSampleHeader;
178 >                // Protobuf doesn't implement move semantics so I'll use pointers
179 >                std::vector<std::unique_ptr<l1menuprotobuf::Run> > protobufRuns;
180 >                const static int EVENTS_PER_RUN;
181 >                const static char PROTOBUF_MESSAGE_DELIMETER;
182 >                const static std::string FILE_FORMAT_MAGIC_NUMBER;
183          };
184 +
185 +        const int ReducedMenuSamplePrivateMembers::EVENTS_PER_RUN=20000;
186 +        const char ReducedMenuSamplePrivateMembers::PROTOBUF_MESSAGE_DELIMETER='\n';
187 +        const std::string ReducedMenuSamplePrivateMembers::FILE_FORMAT_MAGIC_NUMBER="l1menuReducedMenuSample";
188   }
189  
190 < l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::MenuSample& originalSample, const l1menu::TriggerMenu& triggerMenu )
190 > l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const l1menu::TriggerMenu& newTriggerMenu )
191 >        : mutableTriggerMenu_( newTriggerMenu ), event(thisObject), triggerMenu( mutableTriggerMenu_ ), eventRate(1), sumOfWeights(0)
192   {
193 <        size_t numberOfEvents=originalSample.numberOfEvents();
194 <        // Need to find out how many parameters there are for each event. Basically the sum
195 <        // of the number of thresholds for all triggers.
196 <        size_t numberOfParameters=0;
193 >        GOOGLE_PROTOBUF_VERIFY_VERSION;
194 >
195 >        // I need to copy the details of the trigger menu into the protobuf storage.
196 >        // This means I'm holding a duplicate, but I need it to write the sample to a
197 >        // protobuf file, so I might as well do it now.
198          for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
199          {
200                  const l1menu::ITrigger& trigger=triggerMenu.getTrigger(triggerNumber);
201 <                numberOfParameters+=l1menu::getThresholdNames(trigger).size();
201 >
202 >                l1menuprotobuf::Trigger* pProtobufTrigger=protobufSampleHeader.add_trigger();
203 >                pProtobufTrigger->set_name( trigger.name() );
204 >                pProtobufTrigger->set_version( trigger.version() );
205 >
206 >                // Record all of the parameters. It's not strictly necessary to record the values
207 >                // of the parameters that are recorded for each event, but I might as well so that
208 >                // the trigger menu is loaded exactly as it was saved.
209 >                const auto parameterNames=trigger.parameterNames();
210 >                for( const auto& parameterName : parameterNames )
211 >                {
212 >                        l1menuprotobuf::Trigger_TriggerParameter* pProtobufParameter=pProtobufTrigger->add_parameter();
213 >                        pProtobufParameter->set_name(parameterName);
214 >                        pProtobufParameter->set_value( trigger.parameter(parameterName) );
215 >                }
216 >
217 >                // Make a note of the names of the parameters that are recorded for each event. For this
218 >                // I'm just recording the parameters that refer to the thresholds.
219 >                const auto thresholdNames=l1menu::tools::getThresholdNames(trigger);
220 >                for( const auto& thresholdName : thresholdNames ) pProtobufTrigger->add_varying_parameter(thresholdName);
221 >
222 >        } // end of loop over triggers
223 >
224 >        // Always make sure there is at least one Run ready to be added to
225 >        std::unique_ptr<l1menuprotobuf::Run> pNewRun( new l1menuprotobuf::Run );
226 >        protobufRuns.push_back( std::move( pNewRun ) );
227 >
228 > }
229 >
230 > l1menu::ReducedMenuSamplePrivateMembers::ReducedMenuSamplePrivateMembers( const l1menu::ReducedMenuSample& thisObject, const std::string& filename )
231 >        : event(thisObject), triggerMenu(mutableTriggerMenu_), eventRate(1), sumOfWeights(0)
232 > {
233 >        GOOGLE_PROTOBUF_VERIFY_VERSION;
234 >
235 >        // Open the file with read ability
236 >        int fileDescriptor = open( filename.c_str(), O_RDONLY );
237 >        if( fileDescriptor==0 ) throw std::runtime_error( "ReducedMenuSample initialise from file - couldn't open file" );
238 >        ::UnixFileSentry fileSentry( fileDescriptor ); // Use this as an exception safe way of closing the input file
239 >        google::protobuf::io::FileInputStream fileInput( fileDescriptor );
240 >
241 >        // First read the magic number at the start of the file and make sure it
242 >        // matches what I expect. This is uncompressed so I'll wrap it in a block
243 >        // to make sure the CodedInputStream is destructed before creating a new
244 >        // one with gzip input.
245 >        {
246 >                google::protobuf::io::CodedInputStream codedInput( &fileInput );
247 >
248 >                // As a read buffer, I'll create a string the correct size (filled with an arbitrary
249 >                // character) and read straight into that.
250 >                std::string readMagicNumber;
251 >                if( !codedInput.ReadString( &readMagicNumber, FILE_FORMAT_MAGIC_NUMBER.size() ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - error reading magic number" );
252 >                if( readMagicNumber!=FILE_FORMAT_MAGIC_NUMBER ) throw std::runtime_error( "ReducedMenuSample - tried to initialise with a file that is not the correct format" );
253 >
254 >                google::protobuf::uint32 fileformatVersion;
255 >                if( !codedInput.ReadVarint32( &fileformatVersion ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - error reading file format version" );
256 >                // So far I only have (and ever expect to have) one version of the file
257 >                // format, imaginatively versioned "1". You never know though...
258 >                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;
259          }
260  
261 <        // Now I know how many events there are and how many parameters, I can create the pimple
262 <        // with the correct parameters.
263 <        pImple_.reset( new l1menu::ReducedMenuSamplePrivateMembers( numberOfEvents, numberOfParameters, triggerMenu ) );
261 >        google::protobuf::io::GzipInputStream gzipInput( &fileInput );
262 >        google::protobuf::io::CodedInputStream codedInput( &gzipInput );
263 >
264 >        // Disable warnings on this input stream (second parameter, -1). The
265 >        // first parameter is the default. I'll change this if necessary in
266 >        // the loop later.
267 >        size_t totalBytesLimit=67108864;
268 >        codedInput.SetTotalBytesLimit( totalBytesLimit, -1 );
269 >
270 >        google::protobuf::uint64 messageSize;
271 >
272 >        // Read the size of the header message
273 >        if( !codedInput.ReadVarint64( &messageSize ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - error reading message size for header" );
274 >        google::protobuf::io::CodedInputStream::Limit readLimit=codedInput.PushLimit(messageSize);
275 >        if( !protobufSampleHeader.ParseFromCodedStream( &codedInput ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - some unknown error while reading header" );
276 >        codedInput.PopLimit(readLimit);
277  
278 <        //
279 <        // 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 )
278 >        // Keep looping until there is nothing more to be read from the file.
279 >        while( codedInput.ReadVarint64( &messageSize ) )
280          {
281 <                const l1menu::IEvent& event=originalSample.getEvent( eventNumber );
83 <                std::vector<float>& parameters=pImple_->thresholdsForAllEvents[eventNumber];
281 >                readLimit=codedInput.PushLimit(messageSize);
282  
283 <                size_t parameterNumber=0;
284 <                // Loop over all of the triggers
285 <                for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
283 >                // Make sure the CodedInputStream doesn't refuse to read the message because it's
284 >                // read too much already. I'll also add an arbitrary 50 on to always make sure
285 >                // I can read the next messageSize if there is one.
286 >                if( gzipInput.ByteCount()+messageSize+50 > totalBytesLimit )
287                  {
288 <                        std::unique_ptr<l1menu::ITrigger> pTrigger=triggerMenu.getTriggerCopy(triggerNumber);
289 <                        std::vector<std::string> thresholdNames=getThresholdNames(*pTrigger);
288 >                        totalBytesLimit+=messageSize*5; // Might as well set it a little higher than necessary while I'm at it.
289 >                        codedInput.SetTotalBytesLimit( totalBytesLimit, -1 );
290 >                }
291 >                std::unique_ptr<l1menuprotobuf::Run> pNewRun( new l1menuprotobuf::Run );
292 >                if( !pNewRun->ParseFromCodedStream( &codedInput ) ) throw std::runtime_error( "ReducedMenuSample initialise from file - some unknown error while reading run" );
293 >                protobufRuns.push_back( std::move( pNewRun ) );
294  
295 <                        try
296 <                        {
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
295 >                codedInput.PopLimit(readLimit);
296 >        }
297  
298 <                } // end of loop over triggers
299 <        } // end of loop over events
298 >
299 >        // Always make sure there is at least one Run ready to be added to. Later
300 >        // code assumes there is already a run there.
301 >        if( protobufRuns.empty() )
302 >        {
303 >                std::unique_ptr<l1menuprotobuf::Run> pNewRun( new l1menuprotobuf::Run );
304 >                protobufRuns.push_back( std::move( pNewRun ) );
305 >        }
306 >
307 >        // Count up the sum of the weights of all events
308 >        for( const auto& pRun : protobufRuns )
309 >        {
310 >                sumOfWeights+=sumWeights( *pRun );
311 >        }
312 >
313 >        // I have all of the information in the protobuf members, but I also need the trigger information
314 >        // in the form of l1menu::TriggerMenu. Copy out the required information.
315 >        for( int triggerNumber=0; triggerNumber<protobufSampleHeader.trigger_size(); ++triggerNumber )
316 >        {
317 >                const l1menuprotobuf::Trigger& inputTrigger=protobufSampleHeader.trigger(triggerNumber);
318 >
319 >                mutableTriggerMenu_.addTrigger( inputTrigger.name(), inputTrigger.version() );
320 >                // Get a reference to the trigger I just created
321 >                l1menu::ITrigger& trigger=mutableTriggerMenu_.getTrigger(triggerNumber);
322 >
323 >                // Run through all of the parameters and set them to what they were
324 >                // when the sample was made.
325 >                for( int parameterNumber=0; parameterNumber<inputTrigger.parameter_size(); ++parameterNumber )
326 >                {
327 >                        const auto& inputParameter=inputTrigger.parameter(parameterNumber);
328 >                        trigger.parameter(inputParameter.name())=inputParameter.value();
329 >                }
330 >
331 >                // I should probably check the threshold names exist. I'll do it another time.
332 >        }
333 >
334 > }
335 >
336 > l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::MenuSample& originalSample, const l1menu::TriggerMenu& triggerMenu )
337 >        : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
338 > {
339 >        addSample( originalSample );
340   }
341  
342   l1menu::ReducedMenuSample::ReducedMenuSample( const l1menu::TriggerMenu& triggerMenu )
343 +        : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, triggerMenu ) )
344   {
345 <        // Need to find out how many parameters there are for each event. Basically the sum
346 <        // of the number of thresholds for all triggers.
347 <        size_t numberOfParameters=0;
348 <        for( size_t triggerNumber=0; triggerNumber<triggerMenu.numberOfTriggers(); ++triggerNumber )
349 <        {
350 <                const l1menu::ITrigger& trigger=triggerMenu.getTrigger(triggerNumber);
351 <                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;
345 >        // No operation besides the initialiser list
346 > }
347 >
348 > l1menu::ReducedMenuSample::ReducedMenuSample( const std::string& filename )
349 >        : pImple_( new l1menu::ReducedMenuSamplePrivateMembers( *this, filename ) )
350 > {
351 >        // No operation except the initialiser list
352   }
353  
354   l1menu::ReducedMenuSample::~ReducedMenuSample()
# Line 141 | Line 360 | l1menu::ReducedMenuSample::~ReducedMenuS
360  
361   void l1menu::ReducedMenuSample::addSample( const l1menu::MenuSample& originalSample )
362   {
363 <        size_t oldNumberEvents=pImple_->numberOfEvents;
145 <        pImple_->numberOfEvents=oldNumberEvents+originalSample.numberOfEvents();
363 >        l1menuprotobuf::Run* pCurrentRun=pImple_->protobufRuns.back().get();
364  
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        //
365          for( size_t eventNumber=0; eventNumber<originalSample.numberOfEvents(); ++eventNumber )
366          {
367 <                size_t bufferEventNumber=eventNumber+oldNumberEvents;
367 >                // Split the events up into groups in arbitrary numbers. This is to get around
368 >                // a protobuf aversion to long messages.
369 >                if( pCurrentRun->event_size() >= pImple_->EVENTS_PER_RUN )
370 >                {
371 >                        // Gone over the arbitrary limit, so create a new protobuf Run and start
372 >                        // using that instead.
373 >                        std::unique_ptr<l1menuprotobuf::Run> pNewRun( new l1menuprotobuf::Run );
374 >                        pImple_->protobufRuns.push_back( std::move( pNewRun ) );
375 >                        pCurrentRun=pImple_->protobufRuns.back().get();
376 >                }
377  
378 <                const l1menu::IEvent& event=originalSample.getEvent( eventNumber );
379 <                std::vector<float>& parameters=pImple_->thresholdsForAllEvents[bufferEventNumber];
378 >                const l1menu::L1TriggerDPGEvent& event=originalSample.getEvent( eventNumber );
379 >                l1menuprotobuf::Event* pProtobufEvent=pCurrentRun->add_event();
380  
162                size_t parameterNumber=0;
381                  // Loop over all of the triggers
382                  for( size_t triggerNumber=0; triggerNumber<pImple_->triggerMenu.numberOfTriggers(); ++triggerNumber )
383                  {
384                          std::unique_ptr<l1menu::ITrigger> pTrigger=pImple_->triggerMenu.getTriggerCopy(triggerNumber);
385 <                        std::vector<std::string> thresholdNames=getThresholdNames(*pTrigger);
385 >                        std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(*pTrigger);
386  
387                          try
388                          {
389 <                                setTriggerThresholdsAsTightAsPossible( event, *pTrigger, 0.001 );
389 >                                l1menu::tools::setTriggerThresholdsAsTightAsPossible( event, *pTrigger, 0.001 );
390                                  // Set all of the parameters to match the thresholds in the trigger
391                                  for( const auto& thresholdName : thresholdNames )
392                                  {
393 <                                        parameters[parameterNumber]=pTrigger->parameter(thresholdName);
176 <                                        ++parameterNumber;
393 >                                        pProtobufEvent->add_threshold( pTrigger->parameter(thresholdName) );
394                                  }
395                          }
396                          catch( std::exception& error )
397                          {
398                                  // setTriggerThresholdsAsTightAsPossible() couldn't find thresholds so record
399                                  // -1 for everything.
400 <                                for( size_t index=0; index<thresholdNames.size(); ++index )
401 <                                {
185 <                                        parameters[parameterNumber]=-1;
186 <                                        ++parameterNumber;
187 <                                }
400 >                                // Range based for loop gives me a warning because I don't use the thresholdName.
401 >                                for( size_t index=0; index<thresholdNames.size(); ++index ) pProtobufEvent->add_threshold(-1);
402                          } // end of try block that sets the trigger thresholds
403  
404                  } // end of loop over triggers
191        } // end of loop over events
192
193 }
194
195 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;
405  
406 <        size_t totalNumberOfThresholds=0;
407 <
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 <                // many there are so that I can initialise the buffer that holds the event data.
227 <                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 <        }
406 >                pImple_->sumOfWeights+=event.weight();
407 >        } // end of loop over events
408   }
409  
410   void l1menu::ReducedMenuSample::saveToFile( const std::string& filename ) const
411   {
412 <        GOOGLE_PROTOBUF_VERIFY_VERSION;
413 <
414 <        l1menuprotobuf::Sample outputSample;
415 <
416 <        for( size_t triggerNumber=0; triggerNumber<pImple_->triggerMenu.numberOfTriggers(); ++triggerNumber )
417 <        {
418 <                const l1menu::ITrigger& trigger=pImple_->triggerMenu.getTrigger(triggerNumber);
419 <
420 <                l1menuprotobuf::Trigger* pOutputTrigger=outputSample.add_trigger();
421 <                pOutputTrigger->set_name( trigger.name() );
422 <                pOutputTrigger->set_version( trigger.version() );
423 <
424 <                // Record all of the parameters. It's not strictly necessary to record the values
425 <                // of the parameters that are recorded for each event, but I might as well so that
426 <                // the trigger menu is loaded exactly as it was saved.
427 <                const auto parameterNames=trigger.parameterNames();
428 <                for( const auto& parameterName : parameterNames )
429 <                {
430 <                        l1menuprotobuf::Trigger_TriggerParameter* pOutputParameter=pOutputTrigger->add_parameter();
431 <                        pOutputParameter->set_name(parameterName);
273 <                        pOutputParameter->set_value( trigger.parameter(parameterName) );
274 <                }
412 >        // Open the file. Parameters are filename, write ability and create, rw-r--r-- permissions.
413 >        int fileDescriptor = open( filename.c_str(), O_WRONLY | O_CREAT, 0644 );
414 >        if( fileDescriptor==0 ) throw std::runtime_error( "ReducedMenuSample save to file - couldn't open file" );
415 >        ::UnixFileSentry fileSentry( fileDescriptor ); // Use this as an exception safe way of closing the output file
416 >
417 >        // Setup the protobuf file handlers
418 >        google::protobuf::io::FileOutputStream fileOutput( fileDescriptor );
419 >
420 >        // I want the magic number and file format identifier uncompressed, so
421 >        // I'll write those before switching to using gzipped output.
422 >        { // Block to make sure codedOutput is destructed before the gzip version is created
423 >                google::protobuf::io::CodedOutputStream codedOutput( &fileOutput );
424 >
425 >                // Write a magic number at the start of all files
426 >                codedOutput.WriteString( pImple_->FILE_FORMAT_MAGIC_NUMBER );
427 >                // Write an integer that specifies what version of the file format I'm using. I
428 >                // have no intention of changing the format but I might as well keep the option
429 >                // open.
430 >                codedOutput.WriteVarint32( 1 );
431 >        }
432  
433 <                // Make a note of the names of the parameters that are recorded for each event.
434 <                const auto thresholdNames=l1menu::getThresholdNames(trigger);
278 <                for( const auto& thresholdName : thresholdNames ) pOutputTrigger->add_threshold(thresholdName);
433 >        google::protobuf::io::GzipOutputStream gzipOutput( &fileOutput );
434 >        google::protobuf::io::CodedOutputStream codedOutput( &gzipOutput );
435  
436 <        } // end of loop over triggers
436 >        // Write the size of the header message into the file...
437 >        codedOutput.WriteVarint64( pImple_->protobufSampleHeader.ByteSize() );
438 >        // ...and then write the header
439 >        pImple_->protobufSampleHeader.SerializeToCodedStream( &codedOutput );
440  
441 <        //
442 <        // 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 )
441 >        // Now go through each of the runs and do the same for those
442 >        for( const auto& pRun : pImple_->protobufRuns )
443          {
444 <                l1menuprotobuf::Event* pOutputEvent=outputSample.add_event();
445 <                for( const auto& threshold : thresholdsForEvent )
290 <                {
291 <                        pOutputEvent->add_threshold( threshold );
292 <                }
444 >                codedOutput.WriteVarint64( pRun->ByteSize() );
445 >                pRun->SerializeToCodedStream( &codedOutput );
446          }
447  
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 );
448   }
449  
450   size_t l1menu::ReducedMenuSample::numberOfEvents() const
451   {
452 <        return pImple_->numberOfEvents;
453 < }
454 <
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;
452 >        size_t numberOfEvents=0;
453 >        for( const auto& pRun : pImple_->protobufRuns ) numberOfEvents+=pRun->event_size();
454 >        return numberOfEvents;
455   }
456  
457   const l1menu::TriggerMenu& l1menu::ReducedMenuSample::getTriggerMenu() const
# Line 354 | Line 482 | bool l1menu::ReducedMenuSample::contains
482                  // eta cuts or whatever.
483                  // I don't care if the thresholds don't match because that's what's stored in the
484                  // ReducedMenuSample.
485 <                std::vector<std::string> parameterNames=getNonThresholdParameterNames( trigger );
485 >                std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
486                  bool allParametersMatch=true;
487                  for( const auto& parameterName : parameterNames )
488                  {
# Line 400 | Line 528 | const std::map<std::string,size_t> l1men
528                  // ReducedMenuSample.
529                  if( triggerWasFound ) // Trigger can still fail, but no point doing this check if it already has
530                  {
531 <                        std::vector<std::string> parameterNames=getNonThresholdParameterNames( trigger );
531 >                        std::vector<std::string> parameterNames=l1menu::tools::getNonThresholdParameterNames( trigger );
532                          for( const auto& parameterName : parameterNames )
533                          {
534                                  if( trigger.parameter(parameterName)!=triggerInMenu.parameter(parameterName) ) triggerWasFound=false;
535                          }
536                  }
537  
538 <                std::vector<std::string> thresholdNames=l1menu::getThresholdNames(triggerInMenu);
538 >                std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(triggerInMenu);
539                  if( triggerWasFound )
540                  {
541                          for( const auto& thresholdName : thresholdNames )
# Line 424 | Line 552 | const std::map<std::string,size_t> l1men
552          // (I guess - it would be a pretty pointless trigger though). To indicate the
553          // difference between that and a trigger that wasn't found I'll respectively
554          // return the empty vector or throw an exception.
555 <        if( !triggerWasFound ) throw std::runtime_error( "l1menu::ReducedMenuSample::getTriggerParameterIdentifiers() called for a trigger that was not used to create the sample" );
555 >        if( !triggerWasFound ) throw std::runtime_error( "l1menu::ReducedMenuSample::getTriggerParameterIdentifiers() called for a trigger that was not used to create the sample - "+trigger.name() );
556  
557          return returnValue;
558   }
559 +
560 + const l1menu::IEvent& l1menu::ReducedMenuSample::getEvent( size_t eventNumber ) const
561 + {
562 +        for( const auto& pRun : pImple_->protobufRuns )
563 +        {
564 +                if( eventNumber<static_cast<size_t>(pRun->event_size()) )
565 +                {
566 +                        pImple_->event.pProtobufEvent_=pRun->mutable_event(eventNumber);
567 +                        return pImple_->event;
568 +                }
569 +                // Event must be in a later run, so reduce the number by how many events
570 +                // were in this run and look again.
571 +                eventNumber-=pRun->event_size();
572 +        }
573 +
574 +        // Should always find the event before getting to this point, so throw an
575 +        // exception if this happens.
576 +        throw std::runtime_error( "ReducedMenuSample::getEvent(eventNumber) was asked for an invalid eventNumber" );
577 +
578 + }
579 +
580 + std::unique_ptr<l1menu::ICachedTrigger> l1menu::ReducedMenuSample::createCachedTrigger( const l1menu::ITrigger& trigger ) const
581 + {
582 +        return std::unique_ptr<l1menu::ICachedTrigger>( new CachedTriggerImplementation(*this,trigger) );
583 + }
584 +
585 + float l1menu::ReducedMenuSample::eventRate() const
586 + {
587 +        return pImple_->eventRate;
588 + }
589 +
590 + void l1menu::ReducedMenuSample::setEventRate( float rate )
591 + {
592 +        pImple_->eventRate=rate;
593 + }
594 +
595 + float l1menu::ReducedMenuSample::sumOfWeights() const
596 + {
597 +        return pImple_->sumOfWeights;
598 + }
599 +
600 + std::unique_ptr<const l1menu::IMenuRate> l1menu::ReducedMenuSample::rate( const l1menu::TriggerMenu& menu ) const
601 + {
602 +        // TODO make sure the TriggerMenu is valid for this sample
603 +
604 +        // I need a non-const menu, so make a copy
605 +        l1menu::TriggerMenu mutableMenu( menu );
606 +
607 +        // The number of events that pass each trigger
608 +        std::vector<size_t> numberOfEventsPassed( menu.numberOfTriggers() );
609 +        float numberOfEventsPassingAnyTrigger;
610 +
611 +        for( size_t triggerNumber=0; triggerNumber<menu.numberOfTriggers(); ++triggerNumber )
612 +        {
613 +                mutableMenu.getTrigger( triggerNumber ).initiateForReducedSample( *this );
614 +        }
615 +
616 +        for( size_t eventNumber=0; eventNumber<numberOfEvents(); ++eventNumber )
617 +        {
618 +                const l1menu::IEvent& event=getEvent(eventNumber);
619 +                bool anyTriggerPassed=false;
620 +
621 +                for( size_t triggerNumber=0; triggerNumber<menu.numberOfTriggers(); ++triggerNumber )
622 +                {
623 +                        if( event.passesTrigger( mutableMenu.getTrigger(triggerNumber) ) )
624 +                        {
625 +                                // If the event passes the trigger, increment the counter
626 +                                ++numberOfEventsPassed[triggerNumber];
627 +                                anyTriggerPassed=true;
628 +                        }
629 +                }
630 +
631 +                if( anyTriggerPassed ) ++numberOfEventsPassingAnyTrigger;
632 +        }
633 +
634 +        ::MenuRateImplementation* pRates=new ::MenuRateImplementation;
635 +        pRates->setScaling( pImple_->eventRate );
636 +        // This is the value I want to return, but I still need access to the extended attributes of the subclass
637 +        std::unique_ptr<const l1menu::IMenuRate> pReturnValue( pRates );
638 +
639 +        pRates->setTotalFraction( static_cast<float>(numberOfEventsPassingAnyTrigger)/static_cast<float>(numberOfEvents()) );
640 +
641 +        for( size_t triggerNumber=0; triggerNumber<numberOfEventsPassed.size(); ++triggerNumber )
642 +        {
643 +                float fraction=static_cast<float>(numberOfEventsPassed[triggerNumber])/static_cast<float>(numberOfEvents());
644 +                pRates->addTriggerRate( mutableMenu.getTrigger(triggerNumber), fraction );
645 +        }
646 +
647 +        return pReturnValue;
648 + }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines