TrimRangeAlgorithm.cpp

00001 #include "TrimRangeAlgorithm.h"
00002 #include "AnalysisAlgorithmMap.h"
00003 #include "AnalysisService.h"
00004 #include "Sct/SctParameters.h"
00005 #include "Sct/UnsupportedOperationError.h"
00006 #include "SctData/DefectPrototype.h"
00007 #include "SctData/TrimRangeTestResult.h"
00008 #include "SctData/FitScanResult.h"
00009 #include "SctData/FitObject.h"
00010 #include "SctData/StandardDefects.h"
00011 #include "SctData/ModuleConfiguration.h"
00012 #include "SctData/ChipConfiguration.h"
00013 #include "SctData/ResponseCurve.h"
00014 #include <boost/shared_ptr.hpp>
00015 #include <algorithm>
00016 #include <memory>
00017 #include <vector>
00018 
00019 using namespace Sct;
00020 using namespace SctData;
00021 using namespace boost;
00022 
00023 using std::cout;
00024 using std::endl;
00025 
00026 namespace SctAnalysis {
00027 
00028 bool debug = false;
00029     
00030 bool TrimRangeAlgorithm::inMap = AnalysisAlgorithmMap::instance().setAlgorithm("TrimRangeTest", auto_ptr<AnalysisAlgorithm>(new TrimRangeAlgorithm()));
00031 
00032 shared_ptr<AnalysisAlgorithm> TrimRangeAlgorithm::clone(shared_ptr<const TestData> testData, const string& moduleName) const throw() {
00033     return shared_ptr<AnalysisAlgorithm>(new TrimRangeAlgorithm(testData, moduleName, *this));
00034 }
00035 
00036 bool TrimRangeAlgorithm::s_targetVariation = false;
00037 bool TrimRangeAlgorithm::s_rangeVariation = true;
00038 
00039 void TrimRangeAlgorithm::loadData() {
00040     loadAllFits();
00041 }
00042 
00043 bool TrimRangeAlgorithm::canAnalyze() const{
00044     return hasAllFits();
00045 }
00046 
00047 shared_ptr<TestResult> TrimRangeAlgorithm::createTestResult() const {
00048         return shared_ptr<TestResult>(new TrimRangeTestResult());    
00049 }
00050 
00051 void TrimRangeAlgorithm::analyze() {           
00052     shared_ptr<TrimRangeTestResult> result = dynamic_pointer_cast<TrimRangeTestResult> ( getTestResult() );
00053     result->setScanVariable(getFit(0)->getHeader().getVariable());
00054     const ModuleConfiguration& config = getFit(0)->getConfiguration();
00055 
00056     /*
00057        STAGE 1 : make sense of the incoming data.
00058        make a vector of all ranges, all points in those ranges and the 
00059        appropriate scan number.
00060     */
00061     std::vector<TrimRange> all_ranges = createRanges(*result);
00062 
00063     /*
00064        STAGE 2 : make graphs of trim against vt50 for each chip
00065        and each channel and each range
00066     */
00067 
00068     shared_ptr<SctData::TrimRangeTestResult::ChipTrimData> dataArray[all_ranges.size()][nChipModule];
00069 
00070     // loop over trim ranges
00071     for (unsigned irange=0; irange<all_ranges.size(); ++irange) {
00072 
00073         TrimRange& range = all_ranges[irange];
00074 
00075         // loop over chips
00076         for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00077             dataArray[irange][ichip] =
00078                 shared_ptr<TrimRangeTestResult::ChipTrimData>(new TrimRangeTestResult::ChipTrimData());
00079 
00080             // loop over trim-scan points
00081             for (unsigned ipoint=0; ipoint<range.points.size(); ++ipoint) {
00082 
00083                 TrimPoint& this_point = range.points[ipoint];
00084                 shared_ptr<const FitScanResult> fitted=getFit(this_point.scan);
00085                 const DefectList& fitDefects=fitted->getDefects();
00086 
00087                 // loop over channels in that chip.
00088                 for (unsigned iChanInChip=0; iChanInChip<nChannelChip; ++iChanInChip) {
00089                     unsigned ichannel=iChanInChip + ichip*nChannelChip;
00090 
00091                     // get the relevant TrimData object
00092                     Stat<TrimRangeTestResult::TrimData>& trdt = dataArray[irange][ichip]
00093                             ->channelData.modifyAt(iChanInChip);
00094 
00095                     // channel is valid if there are no defects on it.
00096                     if(fitDefects.defectSeverityEncompassingElement(ModuleElement::Channel(ichannel)) >= SERIOUS) {
00097                         trdt.valid=false;
00098                         if (debug) {
00099                             cout << "Severe defect on Channel " << ichannel << endl;
00100                             //fitDefects.getDefectsEncompassingElement( ModuleElement::Channel(ichannel))->print(cout);
00101                         }
00102                     }
00103             if (config.getChipConfiguration(ichip).isMasked(iChanInChip)) {
00104             trdt.valid=false;
00105             if (debug) cout << "Masked channel: " << ichannel << endl;
00106             }
00107                     if (trdt.valid) {
00108                         float gr_mean = fitted->getChannelFit(ichannel).getParameter(1);
00109                         float gr_value = this_point.value;
00110                         trdt.value.graph.push_back(std::make_pair(gr_mean, gr_value));
00111                     } // if channel is valid
00112                 } // loop over channels
00113             } // loop over scan-points
00114         } // loop over chips
00115     } // loop over ranges
00116 
00117 
00118     /*
00119       STAGE 3:
00120       create a vector of target s starting from firstTarget in uniform steps
00121       to a total of ntargets
00122     */
00123     std::vector<float> target;
00124     for (unsigned itarg=0; itarg<nTargets(); ++itarg) {
00125         target.push_back (firstTarget() + itarg * targetStep());
00126     }
00127 
00128     shared_ptr<const TrimRangeTestResult::ChipTrim> trimArray[all_ranges.size()][nChipModule][nTargets()];
00129 
00130     /*
00131       STAGE 4a: fit the graphs 
00132       STAGE 4b: find the number of trimmable channels for each in a series of targets
00133     */
00134     for (unsigned irange=0; irange<all_ranges.size(); ++irange) {
00135         for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00136             // 4a:
00137             if (dataArray[irange][ichip].get()==0) {
00138                 throw InvariantViolatedError("TrimRangeAlgorithm: dataArray pointer should not be zero!", __FILE__, __LINE__);
00139             }
00140             doTrimDataFit( *dataArray[irange][ichip] );
00141             for (unsigned itarget=0; itarget<nTargets(); ++itarget) {
00142                 //4b:
00143         if (debug && target[itarget]> 85 && target[itarget]<95) 
00144             cout << "Chip: " << ichip << " Range: " << all_ranges[irange].range << endl;
00145                 trimArray[irange][ichip][itarget] = getChipTrim(*dataArray[irange][ichip],
00146                                                     target[itarget],
00147                                                     all_ranges[irange].range);
00148             }
00149         }
00150     }
00151 
00152     /*
00153       STAGE 5 select the best trim target which trims the maximum number of channels
00154     */
00155 
00156     unsigned best_target_for_chip[nChipModule];       // index of vector targets
00157     unsigned best_range_for_chip[nChipModule];        // index of vector all_ranges
00158 
00159     // 5a no chip-to-chip variation:
00160     if ( !allowTrimTargetVariation() && !allowTrimRangeVariation() ) {
00161     if (debug) cout << "stage 5a" << endl;
00162         unsigned max_trimmed=0;
00163         unsigned target_lo=0, target_hi=0;
00164 
00165         for (unsigned irange=0; irange<all_ranges.size(); ++irange) {
00166             for( unsigned itarget=0; itarget<nTargets(); ++itarget) {
00167                 unsigned n_trimmed=0;
00168                 /* find number of channels which are trimmable in the module */
00169                 for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00170                     n_trimmed+=trimArray[irange][ichip][itarget]->channelTrim.n();
00171 
00172                 }
00173                 if (n_trimmed>max_trimmed) {
00174                     target_lo=itarget;
00175                     best_range_for_chip[0] = irange;
00176                     max_trimmed=n_trimmed;
00177                 }
00178                 if (n_trimmed==max_trimmed && irange==best_range_for_chip[0]) {
00179                     target_hi=itarget;
00180                 }
00181             }
00182         }
00183         for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00184             best_range_for_chip[ichip]=best_range_for_chip[0];
00185             best_target_for_chip[ichip]=(target_hi+target_lo)/2;
00186         }
00187     } else if ( allowTrimTargetVariation() && !allowTrimRangeVariation() ) { // 5b chip-to-chip target variation:
00188     if (debug) cout << "stage 5b" << endl;
00189     unsigned target_lo[nChipModule];
00190     unsigned target_hi[nChipModule];
00191     unsigned max_trimmed[nChipModule];
00192     for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00193         target_lo[ichip]=target_hi[ichip]=max_trimmed[ichip]=0;
00194     }   
00195 
00196     for (unsigned irange=0; irange<all_ranges.size(); ++irange) {
00197         for( unsigned itarget=0; itarget<nTargets(); ++itarget) {
00198         unsigned n_trimmed=0;
00199         /* find number of channels which are trimmable in the module */
00200         for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00201             n_trimmed = trimArray[irange][ichip][itarget]->channelTrim.n();
00202             
00203             if (n_trimmed>max_trimmed[ichip] ) {
00204             target_lo[ichip]=itarget;
00205             best_range_for_chip[ichip] = irange;
00206             }
00207             if (n_trimmed==max_trimmed[ichip] && irange==best_range_for_chip[ichip]) {
00208             target_hi[ichip]=itarget;
00209             }
00210         }
00211         }
00212     }
00213     for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00214         best_target_for_chip[ichip]=(target_hi[ichip]+target_lo[ichip])/2;
00215     }
00216     } else if ( !allowTrimTargetVariation() && allowTrimRangeVariation() ) {           // 5c chip-to-chip range variation:
00217     if (debug) cout << "stage 5c"<<endl;
00218     unsigned target_lo=0;
00219     unsigned target_hi=0;
00220     unsigned total_channels_trimmed_max=0;
00221     unsigned total_range_lo=0;
00222 
00223     for( unsigned itarget=0; itarget<nTargets(); ++itarget) {
00224         if (debug)
00225         cout << "TARGET " << itarget << "  " << target[itarget] << "mV" << endl;
00226         unsigned total_channels_trimmed=0;
00227         unsigned range_total=0;
00228         
00229         for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00230         unsigned best_range=0;
00231         unsigned max = 0;
00232 
00233         for (unsigned irange=0; irange<all_ranges.size(); ++irange) {
00234             unsigned n_trimmed = trimArray[irange][ichip][itarget]->channelTrim.n();
00235 
00236             // if this is the lowest range with the maximum number of channels:
00237             if (n_trimmed > max ||
00238             ( all_ranges[irange].range < all_ranges[best_range].range
00239               && n_trimmed==max)) {
00240             best_range = irange;
00241             max = n_trimmed;
00242             }
00243         }
00244         // find the total number of channels trimmed
00245         total_channels_trimmed+=max;
00246         range_total+=all_ranges[best_range].range ;
00247         }
00248         if (debug)
00249         cout << "  TOTAL = " <<  total_channels_trimmed << " for " << range_total << endl;
00250         
00251         if (total_channels_trimmed > total_channels_trimmed_max ||
00252         ( total_channels_trimmed == total_channels_trimmed_max &&
00253           range_total < total_range_lo ) ) {
00254         target_lo=itarget;
00255         total_range_lo=range_total;
00256         total_channels_trimmed_max=total_channels_trimmed;
00257         if (debug)
00258             cout << " ...... modified lower target" << endl;
00259         }
00260 
00261         if (total_channels_trimmed == total_channels_trimmed_max &&
00262         total_range_lo==range_total ) {
00263         target_hi=itarget;
00264         if (debug)
00265             cout << " ...... modified upper target" << endl;
00266         }
00267 
00268     }
00269     for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00270         best_target_for_chip[ichip]=(target_hi+target_lo)/2;
00271         // know the target, get the ranges again:
00272         if (debug)
00273         cout << endl << " --> CHIP " << ichip << endl;
00274         unsigned max=0;
00275         best_range_for_chip[ichip]=0;
00276 
00277         for (unsigned irange=0; irange<all_ranges.size(); ++irange) {
00278         unsigned n_trimmed = trimArray[irange][ichip][best_target_for_chip[ichip]]->channelTrim.n();
00279         if (debug)
00280             cout << "R" << irange << " -> " << n_trimmed << endl;
00281         if (n_trimmed> max ||
00282             ( all_ranges[irange].range < all_ranges[best_range_for_chip[ichip]].range
00283               && n_trimmed==max)) {
00284             best_range_for_chip[ichip] = irange;
00285             max=n_trimmed;
00286             if (debug)
00287             cout << "best range changed to #" << irange << " = " << all_ranges[irange].range << endl;
00288         }
00289         }
00290     }
00291     } else {
00292     throw UnsupportedOperationError("Trim Range Algorithm not implemented for desired options!", __FILE__, __LINE__);
00293     }
00294     
00295     /* STAGE 6 fill output TestResult data */
00296     if (debug)
00297         cout << "stage 6"<<endl;
00298 
00299     {
00300         // get a chip configuration for an example chip:
00301         const ChipConfiguration& config=getFit(0)->getConfiguration().getChipConfiguration(0);
00302         auto_ptr<ResponseCurve> rc = ResponseCurveMap::getMap().get(config);
00303         float cal_charge_mV = getFit(0)->getConfiguration().getChipConfiguration(0).getCalCharge()*2.5 ;
00304         float cal_charge_fC = rc->getFunction()->Eval(cal_charge_mV);
00305 
00306         result->charge = cal_charge_fC;
00307         result->type = allowTrimRangeVariation() ? (unsigned)-1 : best_range_for_chip[0] ;
00308         result->algorithm = allowTrimTargetVariation() ? 1 : 0 ;
00309     }
00310 
00311     for (unsigned ichip=0; ichip<nChipModule; ++ichip) {
00312         if (debug)
00313             cout << "  CHIP " << ichip << endl;
00314         unsigned best_range  = best_range_for_chip[ichip];
00315         unsigned best_target = best_target_for_chip[ichip];
00316 
00317         if (debug)
00318             cout << "    optimum range = " <<  best_range
00319             << ", target #" << best_target
00320             << " = " << target[best_target]
00321             << ", ntrim=" << trimArray[best_range][ichip][best_target_for_chip[ichip]]->channelTrim.n() << endl;
00322 
00323         result->chipTrim[ichip] =
00324             trimArray [best_range] [ichip] [best_target];
00325         result->chipTrimData[ichip]=
00326             dataArray[best_range] [ichip];
00327 
00328         const Stats<double> offsets = dataArray[best_range][ichip]->getOffsets();
00329         const Stats<double> steps   = dataArray[best_range][ichip]->getSteps();
00330 
00331         double stepmin=0, stepmax=16;
00332         switch (all_ranges[best_range].range) {
00333         case 0 :
00334             stepmin = 1.5 ;
00335             stepmax = 5.0 ;
00336             break;
00337         case 1 :
00338             stepmin = 5.0 ;
00339             stepmax = 8.5 ;
00340             break;
00341         case 2 :
00342             stepmin = 8.5 ;
00343             stepmax = 12.0;
00344             break;
00345         case 3 :
00346             stepmin = 12.0;
00347             stepmax = 15.5;
00348             break;
00349         }
00350         // Chip Defects:
00351         if (steps.mean() < stepmin || steps.mean() > stepmax ) {
00352             result->getDefects().addDefect(Defect(StandardDefects::TR_RANGE, ModuleElement::Chip(ichip)));
00353         }
00354         // Channel Defects:
00355         for (unsigned iChanInChip=0; iChanInChip<nChannelChip ; ++iChanInChip) {
00356             // Step Defects:
00357             if (steps.getAt(iChanInChip).valid &&
00358                     ( fabs( steps.getAt(iChanInChip).value - steps.mean() )
00359                       > StandardDefects::TR_STEP.getParameter() * sqrt(steps.var() ) ) ) {
00360                 result->getDefects().addDefect(Defect(StandardDefects::TR_STEP,
00361                                                ModuleElement::Channel(ichip*nChannelChip+iChanInChip)));
00362             }
00363             // Offset Defects:
00364             if (offsets.getAt(iChanInChip).valid &&
00365                     ( fabs( offsets.getAt(iChanInChip).value - offsets.mean() )
00366                       > StandardDefects::TR_OFFSET.getParameter() * sqrt(offsets.var() ) ) ) {
00367                 result->getDefects().addDefect(Defect(StandardDefects::TR_OFFSET,
00368                                                ModuleElement::Channel(ichip*nChannelChip+iChanInChip)));
00369             }
00370             // Untrimmable Defects:
00371             if (result->chipTrim[ichip]->channelTrim.getAt(iChanInChip).valid==false) {
00372                 result->getDefects().addDefect(Defect(StandardDefects::TR_NOTRIM, 
00373                                        ModuleElement::Channel(ichip*nChannelChip + iChanInChip)));
00374             } // if valid
00375         }// Channel loop
00376     }// chip loop
00377 
00378     //Check for defects - fail if more than 15 masked channels or more than 7 consecutive masked channels
00379     //Problem if 1 masked channel
00380     const DefectList::DefectCollection& defects = result->getDefects().getAllDefects();
00381     bool passed = true;
00382     unsigned int totalChannels = 0;
00383     for (DefectList::DefectCollection::const_iterator i=defects.begin(); i!=defects.end(); ++i) {
00384     if (i->getPrototype().getSeverity() >= UNUSEABLE) {
00385         totalChannels += i->getModuleElement().getNChannels();
00386         if (i->getModuleElement().getNChannels()>7) {
00387         passed = false;
00388         break;
00389         }
00390     }
00391     }
00392     
00393     if (totalChannels > 15) passed = false;
00394     if (totalChannels > 0) result->setProblem(true);
00395     
00396     result->setPassed(passed);
00397 }
00398 
00399 
00400 /* fit the trim data with a line */
00401 void TrimRangeAlgorithm::doTrimDataFit(TrimRangeTestResult::ChipTrimData& chipData) throw() {
00402     for (unsigned iChanInChip=0; iChanInChip<nChannelChip; ++iChanInChip) {
00403         Stat<TrimRangeTestResult::TrimData>& trimdata = chipData.channelData.modifyAt(iChanInChip);
00404 
00405         trimdata.valid=false;
00406         trimdata.value.p0 = 0;
00407         trimdata.value.p1 = 0;
00408 
00409         if (trimdata.value.graph.empty())
00410             continue;
00411 
00413         float xbar=0, ybar=0, x2bar=0, xybar=0;
00414         const unsigned n=trimdata.value.graph.size();
00415 
00416         for (unsigned ipoint = 0; ipoint<n; ++ipoint) {
00417             const float& x=trimdata.value.graph[ipoint].first;
00418             const float& y=trimdata.value.graph[ipoint].second;
00419             xbar  += x;
00420             ybar  += y;
00421             x2bar += x*x;
00422             xybar += x*y;
00423         }
00424 
00425         const float det = n*x2bar - xbar*xbar;
00426         if (det>0) {
00427             trimdata.value.p0 = (x2bar*ybar - xbar*xybar) / det;
00428             trimdata.value.p1 = (n*xybar - xbar*ybar) / det;
00429             trimdata.valid=trimdata.value.p1>0;
00430         }
00431     
00432     }
00433 }
00434 
00435 /* interpreet the incoming data as a series of scans over different ranges */
00436 std::vector<TrimRangeAlgorithm::TrimRange> TrimRangeAlgorithm::createRanges(const TestResult& result) throw() {
00437     // Check all chips in each scan have the same range.
00438     // if they don't, then analyse all ranges together.
00439     std::vector<TrimRangeAlgorithm::TrimRange> all_ranges;
00440 
00441     bool inputRangeVariesChipToChip=false;
00442 
00443     for (unsigned iscan=0; iscan<result.getNScans(); ++iscan) {
00444         shared_ptr<const FitScanResult> fitted = getFit(iscan);
00445         for (unsigned ichip=1; ichip<nChipModule; ichip++) {
00446             if (fitted->getConfiguration().getChipConfiguration(ichip).getTrimRange()
00447                     != fitted->getConfiguration().getChipConfiguration(0).getTrimRange()) {
00448                 inputRangeVariesChipToChip=true;
00449             }
00450         }
00451     }
00452 
00453     for (unsigned iscan=0; iscan<result.getNScans(); ++iscan) {
00454         shared_ptr<const FitScanResult> fitted = getFit(iscan);
00455 
00456         // find the trim range for module 0
00457         int range_value=fitted->getConfiguration().getChipConfiguration(0).getTrimRange();
00458         // find the trim for channel 0
00459         int trim_value=fitted->getConfiguration().getChipConfiguration(0).getTrim(0);
00460 
00461         std::vector<TrimRange>::iterator it = all_ranges.begin();
00462         while (it!=all_ranges.end()) {
00463             if ( (*it).range == range_value ) {
00464                 break;
00465             }
00466             ++it;
00467         }
00468         if (it==all_ranges.end() ) { // new trim range?
00469             // if there is chip to chip range variation, treat it all as one range
00470             if ( !inputRangeVariesChipToChip || all_ranges.empty() ) {
00471                 all_ranges.push_back(TrimRange(range_value));
00472             }
00473             it=all_ranges.end()-1;
00474         }
00475 
00476         // store in that range the trim value and the scan identifier.
00477         (*it).points.push_back(TrimPoint(trim_value, iscan) );
00478     }
00479     // sort by range value.
00480     sort(all_ranges.begin(), all_ranges.end());
00481     return all_ranges;
00482 }
00483 
00484 
00485 shared_ptr<const TrimRangeTestResult::ChipTrim> TrimRangeAlgorithm::getChipTrim(const TrimRangeTestResult::ChipTrimData& data, const float target, const short unsigned range) throw() {
00486 
00487     shared_ptr<SctData::TrimRangeTestResult::ChipTrim> chipTrim (new SctData::TrimRangeTestResult::ChipTrim() );
00488     chipTrim->target=target;
00489     chipTrim->range =range;
00490 
00491     for (unsigned iChanInChip=0; iChanInChip<nChannelChip; ++iChanInChip) {
00492     const Stat<TrimRangeTestResult::TrimData>& d= data.channelData.getAt(iChanInChip);
00493     Stat<TrimRangeTestResult::Trim>& t= chipTrim->channelTrim.modifyAt(iChanInChip);
00494     
00495     if (!d.valid) {
00496         t.valid=false;
00497         continue;
00498     }
00499     
00500     // record the trim value.
00501     int i_trim=d.value.getTrim(target);
00502     
00503     // check if trimmable - if not, then do your best
00504     // If trim is -1 or 16, we allow it (because going to the range above wont improve matters!)
00505     // But we set the trim to the maximum or minimum as appropriate
00506     if ( i_trim>=0 && i_trim<=15) {
00507         t.valid=true;
00508         t.value.trim = i_trim;
00509     } else if (i_trim==-1) {
00510         t.valid=true;
00511         t.value.trim = 0;
00512         if (debug) {
00513         cout << "Wanted trim of -1, setting to 0 and passing" << endl;
00514         }
00515     } else if (i_trim==16) {
00516             t.valid=true;
00517         t.value.trim = 15;
00518         if (debug) {
00519         cout << "Wanted trim of 16, setting to 15 and passing" << endl;
00520         }
00521     } else {
00522             t.valid=false;
00523         if (debug && target>85. && target<95) {
00524         cout << "Couldn't trim channel: " << iChanInChip << " i_trim: " << i_trim << " for target: " << target << endl;
00525         cout << "Vt50 min: " << d.value.getVthr(0) << "Vt50 max: " << d.value.getVthr(15) << endl;
00526         }
00527             if (i_trim<0)
00528                 t.value.trim = 0;
00529             if (i_trim>15)
00530                 t.value.trim = 15;
00531         }
00532     
00533     //Don't use i_trim - we want the actual threshold that will be set in case i_trim<0 or >15
00534         t.value.vthr = d.value.getVthr(t.value.trim);
00535     }
00536     return chipTrim;
00537 }
00538 
00539 
00540 } // end of namespace SctAnalysis

Generated on Mon Feb 6 14:01:36 2006 for SCT DAQ/DCS Software - C++ by  doxygen 1.4.6