1 |
#include "l1menu/MenuFitter.h"
|
2 |
|
3 |
#include <stdexcept>
|
4 |
#include <fstream>
|
5 |
#include <iostream>
|
6 |
#include <cmath>
|
7 |
#include "l1menu/ICachedTrigger.h"
|
8 |
#include "l1menu/ISample.h"
|
9 |
#include "l1menu/IEvent.h"
|
10 |
#include "l1menu/IMenuRate.h"
|
11 |
#include "l1menu/ITriggerRate.h"
|
12 |
#include "l1menu/TriggerMenu.h"
|
13 |
#include "l1menu/ITrigger.h"
|
14 |
#include "l1menu/TriggerRatePlot.h"
|
15 |
#include "l1menu/tools/tools.h"
|
16 |
#include "l1menu/tools/stringManipulation.h"
|
17 |
|
18 |
namespace // Use the unnamed namespace for things only used here
|
19 |
{
|
20 |
/** @brief Structure to collate a few things about triggers that can be scaled
|
21 |
* @author Mark Grimes (mark.grimes@bristol.ac.uk)
|
22 |
* @date 08/Jul/2013
|
23 |
*/
|
24 |
struct TriggerScalingDetails
|
25 |
{
|
26 |
size_t triggerNumber; ///< The number of the trigger in the trigger table
|
27 |
float bandwidthFraction; ///< The fraction of the total bandwidth requested for this trigger
|
28 |
l1menu::TriggerRatePlot ratePlot; ///< The rate plot for this trigger
|
29 |
std::string mainThreshold;
|
30 |
std::vector< std::pair<std::string,float> > thresholdScalings; ///< The constant to scale each threshold compared to the main threshold
|
31 |
};
|
32 |
} // end of the unnamed namespace
|
33 |
|
34 |
|
35 |
//
|
36 |
// Need to declare the pimple. So far it's just been forward declared.
|
37 |
//
|
38 |
namespace l1menu
|
39 |
{
|
40 |
/** @brief Private members for the MenuFitter wrapped up in a compiler firewall.
|
41 |
* @author Mark Grimes (mark.grimes@bristol.ac.uk)
|
42 |
* @date 08/Jul/2013
|
43 |
*/
|
44 |
class MenuFitterPrivateMembers
|
45 |
{
|
46 |
public:
|
47 |
MenuFitterPrivateMembers( const l1menu::ISample& newSample, const l1menu::MenuRatePlots* pRatePlots ) : sample(newSample), pMenuRatePlots(pRatePlots) {}
|
48 |
const l1menu::ISample& sample;
|
49 |
const l1menu::MenuRatePlots* pMenuRatePlots;
|
50 |
l1menu::TriggerMenu menu;
|
51 |
std::vector< ::TriggerScalingDetails > scalableTriggers;
|
52 |
std::vector<std::pair<size_t,float> > bandwidthFractions;
|
53 |
std::vector<l1menu::TriggerRatePlot> ratePlots;
|
54 |
void initiateOtherTriggerInfo( size_t triggerNumber, bool lockThresholds, float fractionOfTotalBandwidth );
|
55 |
};
|
56 |
|
57 |
}
|
58 |
|
59 |
l1menu::MenuFitter::MenuFitter( const l1menu::ISample& sample )
|
60 |
: pImple_( new MenuFitterPrivateMembers(sample,NULL) )
|
61 |
{
|
62 |
|
63 |
}
|
64 |
|
65 |
l1menu::MenuFitter::MenuFitter( const l1menu::ISample& sample, const l1menu::MenuRatePlots& menuRatePlots )
|
66 |
: pImple_( new MenuFitterPrivateMembers(sample,&menuRatePlots) )
|
67 |
{
|
68 |
|
69 |
}
|
70 |
|
71 |
l1menu::MenuFitter::~MenuFitter()
|
72 |
{
|
73 |
// No operation
|
74 |
}
|
75 |
|
76 |
const l1menu::TriggerMenu& l1menu::MenuFitter::menu() const
|
77 |
{
|
78 |
return pImple_->menu;
|
79 |
}
|
80 |
|
81 |
std::unique_ptr<const l1menu::IMenuRate> l1menu::MenuFitter::fit( float totalRate, float tolerance )
|
82 |
{
|
83 |
//
|
84 |
// First set all the thresholds to be the ratio of the total bandwidth that
|
85 |
// the user specified. With correlations this will probably mean the total
|
86 |
// will come out less than the requested rate, but it's a good first step.
|
87 |
// Loop over all of the triggers whose thresholds are not fixed.
|
88 |
//
|
89 |
for( const auto& triggerScalingDetails : pImple_->scalableTriggers )
|
90 |
{
|
91 |
l1menu::ITrigger& trigger=pImple_->menu.getTrigger( triggerScalingDetails.triggerNumber );
|
92 |
|
93 |
std::cout << "Trying to find a threshold for " << trigger.name() << " to try and get a rate of " << totalRate*triggerScalingDetails.bandwidthFraction << std::endl;
|
94 |
|
95 |
float& mainThreshold=trigger.parameter( triggerScalingDetails.mainThreshold );
|
96 |
// Figure out what threshold should give the target rate for this particular trigger.
|
97 |
// Note this is a reference so this command changes the trigger.
|
98 |
mainThreshold=triggerScalingDetails.ratePlot.findThreshold( totalRate*triggerScalingDetails.bandwidthFraction );
|
99 |
// Then scale all of the others off this
|
100 |
for( const auto& nameScalePair : triggerScalingDetails.thresholdScalings )
|
101 |
{
|
102 |
trigger.parameter( nameScalePair.first )=mainThreshold*nameScalePair.second;
|
103 |
}
|
104 |
|
105 |
std::cout << "Initially setting threshold for " << trigger.name() << " to " << mainThreshold << " to try and get a rate of " << totalRate*triggerScalingDetails.bandwidthFraction << std::endl;
|
106 |
}
|
107 |
|
108 |
// Then work out what the total rate is
|
109 |
std::unique_ptr<const l1menu::IMenuRate> pMenuRate=pImple_->sample.rate( pImple_->menu );
|
110 |
|
111 |
size_t iterationNumber=0;
|
112 |
while ( std::fabs(pMenuRate->totalRate()-totalRate)>tolerance )
|
113 |
{
|
114 |
if( iterationNumber>10 ) throw std::runtime_error( "Too many iterations" );
|
115 |
++iterationNumber;
|
116 |
|
117 |
float scaleAllRatesBy=totalRate/pMenuRate->totalRate();
|
118 |
std::cout << "\n" << "New loop. Last iteration had a rate of " << pMenuRate->totalRate() << ". Scaling all rates by " << scaleAllRatesBy << " to try and get " << totalRate << std::endl;
|
119 |
|
120 |
for( const auto& triggerScalingDetails : pImple_->scalableTriggers )
|
121 |
{
|
122 |
const size_t& triggerNumber=triggerScalingDetails.triggerNumber;
|
123 |
l1menu::ITrigger& trigger=pImple_->menu.getTrigger( triggerNumber );
|
124 |
const l1menu::ITriggerRate* pTriggerRate=pMenuRate->triggerRates()[triggerNumber];
|
125 |
|
126 |
float& mainThreshold=trigger.parameter( triggerScalingDetails.mainThreshold );
|
127 |
// Figure out what threshold should give the target rate for this particular trigger.
|
128 |
// Note this is a reference so this command changes the trigger.
|
129 |
mainThreshold=triggerScalingDetails.ratePlot.findThreshold( pTriggerRate->rate()*scaleAllRatesBy );
|
130 |
// Then scale all of the others off this
|
131 |
for( const auto& nameScalePair : triggerScalingDetails.thresholdScalings )
|
132 |
{
|
133 |
trigger.parameter( nameScalePair.first )=mainThreshold*nameScalePair.second;
|
134 |
}
|
135 |
std::cout << "Changing threshold for " << trigger.name() << " to " << mainThreshold << " to try and change the rate from " << pTriggerRate->rate() << " to " << pTriggerRate->rate()*scaleAllRatesBy << std::endl;
|
136 |
|
137 |
} // end of loop over triggers I'm allowed to change thresholds for
|
138 |
|
139 |
pMenuRate=pImple_->sample.rate( pImple_->menu );
|
140 |
}
|
141 |
|
142 |
return pMenuRate;
|
143 |
}
|
144 |
|
145 |
void l1menu::MenuFitter::addTrigger( const l1menu::ITrigger& trigger, float fractionOfTotalBandwidth, bool lockThresholds )
|
146 |
{
|
147 |
if( !lockThresholds && (fractionOfTotalBandwidth<0 || fractionOfTotalBandwidth>1) ) throw std::runtime_error("Invalid fraction of the total bandwidth requested");
|
148 |
|
149 |
size_t triggerNumber=pImple_->menu.numberOfTriggers(); // This will be the number of the next trigger added
|
150 |
pImple_->menu.addTrigger( trigger );
|
151 |
pImple_->initiateOtherTriggerInfo( triggerNumber, lockThresholds, fractionOfTotalBandwidth );
|
152 |
}
|
153 |
|
154 |
void l1menu::MenuFitter::loadMenuFromFile( const std::string& filename )
|
155 |
{
|
156 |
std::ifstream file( filename.c_str() );
|
157 |
if( !file.is_open() ) throw std::runtime_error( "Unable to open file "+filename );
|
158 |
|
159 |
const size_t bufferSize=200;
|
160 |
char buffer[bufferSize];
|
161 |
|
162 |
// The old file format provides the rates it wants for each trigger
|
163 |
// as an absolute value, whereas I want it as a fraction of the total.
|
164 |
// So I need to run through the whole file and add up each of these
|
165 |
// to get a total, then divide each value by the total to get the fraction.
|
166 |
// Hence I can't do that until I've loaded every trigger. I'll keep track
|
167 |
// of whether each threshold is locked and the absolute rate requested in
|
168 |
// this vector.
|
169 |
std::vector< std::pair<bool,float> > triggerAuxiliaryInfo;
|
170 |
float totalRequestedRate=0; // This is the sum of all the rates requested for each trigger
|
171 |
|
172 |
while( file.good() )
|
173 |
{
|
174 |
try
|
175 |
{
|
176 |
// Get one line at a time
|
177 |
file.getline( buffer, bufferSize );
|
178 |
|
179 |
// split the line by whitespace into columns
|
180 |
std::vector<std::string> tableColumns=l1menu::tools::splitByWhitespace( buffer );
|
181 |
|
182 |
if( tableColumns.size()==1 && tableColumns[0].empty() ) continue; // Allow blank lines without giving a warning
|
183 |
if( tableColumns.size()!=12 ) throw std::runtime_error( "The line does not have the correct number of columns" );
|
184 |
|
185 |
if( pImple_->menu.addTriggerFromOldFormat( tableColumns ) )
|
186 |
{
|
187 |
// If the trigger was created successfully, keep a note of these
|
188 |
// details so that I can later initiate the auxiliary information.
|
189 |
// I can't do that until I know the total requestedRate.
|
190 |
bool lockThresholds=l1menu::tools::convertStringToFloat( tableColumns[11] );
|
191 |
float requestedRate=l1menu::tools::convertStringToFloat( tableColumns[9] );
|
192 |
triggerAuxiliaryInfo.push_back( std::make_pair(lockThresholds,requestedRate) );
|
193 |
totalRequestedRate+=requestedRate;
|
194 |
}
|
195 |
|
196 |
} // end of try block
|
197 |
catch( std::runtime_error& exception )
|
198 |
{
|
199 |
std::cout << "Some error occured while processing the line \"" << buffer << "\":" << exception.what() << std::endl;
|
200 |
}
|
201 |
}
|
202 |
|
203 |
// Now I've processed all the lines I should know the total requested rate and I can
|
204 |
// initiate the auxiliary information.
|
205 |
size_t triggerNumber=pImple_->menu.numberOfTriggers()-triggerAuxiliaryInfo.size(); // There may have been triggers in there previously
|
206 |
for( const auto auxiliaryInfo : triggerAuxiliaryInfo )
|
207 |
{
|
208 |
pImple_->initiateOtherTriggerInfo( triggerNumber, auxiliaryInfo.first, auxiliaryInfo.second/totalRequestedRate );
|
209 |
++triggerNumber;
|
210 |
}
|
211 |
}
|
212 |
|
213 |
void l1menu::MenuFitterPrivateMembers::initiateOtherTriggerInfo( size_t triggerNumber, bool lockThresholds, float fractionOfTotalBandwidth )
|
214 |
{
|
215 |
const l1menu::ITrigger& newTrigger=menu.getTrigger( triggerNumber );
|
216 |
|
217 |
if( !lockThresholds )
|
218 |
{
|
219 |
|
220 |
//
|
221 |
// Create a rate plot for this trigger
|
222 |
//
|
223 |
const std::vector<std::string> thresholdNames=l1menu::tools::getThresholdNames(newTrigger);
|
224 |
const std::string& mainThreshold=thresholdNames.front();
|
225 |
unsigned int numberOfBins=100;
|
226 |
float lowerEdge=0;
|
227 |
float upperEdge=100;
|
228 |
try
|
229 |
{
|
230 |
l1menu::TriggerTable& triggerTable=l1menu::TriggerTable::instance();
|
231 |
numberOfBins=triggerTable.getSuggestedNumberOfBins( newTrigger.name(), mainThreshold );
|
232 |
lowerEdge=triggerTable.getSuggestedLowerEdge( newTrigger.name(), mainThreshold );
|
233 |
upperEdge=triggerTable.getSuggestedUpperEdge( newTrigger.name(), mainThreshold );
|
234 |
}
|
235 |
catch( std::exception& error) { /* Do nothing. If no binning suggestions have been set for this trigger use the defaults I set above. */ }
|
236 |
|
237 |
l1menu::TriggerRatePlot ratePlot(newTrigger,newTrigger.name()+"_v_allThresholdsScaled",numberOfBins,lowerEdge,upperEdge,mainThreshold,thresholdNames);
|
238 |
ratePlot.addSample( sample );
|
239 |
|
240 |
// Record the scaling between the main threshold and all of the others, so that
|
241 |
// when they get increased/decreased it's all done proportionally.
|
242 |
std::vector< std::pair<std::string,float> > thresholdScalings;
|
243 |
const float mainThresholdValue=newTrigger.parameter(mainThreshold);
|
244 |
for( const auto& thresholdName : thresholdNames )
|
245 |
{
|
246 |
if( thresholdName==mainThreshold ) continue;
|
247 |
thresholdScalings.push_back( std::make_pair( thresholdName, newTrigger.parameter(thresholdName)/mainThresholdValue ) );
|
248 |
}
|
249 |
|
250 |
//
|
251 |
// Bundle all of this information in the helper structure I wrote in
|
252 |
// the unnamed namespace.
|
253 |
//
|
254 |
scalableTriggers.push_back( ::TriggerScalingDetails{triggerNumber,fractionOfTotalBandwidth,std::move(ratePlot),mainThreshold,std::move(thresholdScalings)} );
|
255 |
}
|
256 |
|
257 |
}
|