ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/UserCode/grimes/L1Menu/src/MenuFitter.cpp
Revision: 1.1
Committed: Wed Jul 24 11:48:55 2013 UTC (11 years, 9 months ago) by grimes
Branch: MAIN
CVS Tags: HEAD
Log Message:
Big commit of lots of changes before migrating to Git.

File Contents

# Content
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 }