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