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

# User Rev Content
1 grimes 1.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     }