コード例 #1
0
ファイル: yarrgui.cpp プロジェクト: Yarr/Yarr
void YarrGui::doScan(QString qn){
    std::ofstream *tmpOfCout = new std::ofstream("deleteMeCout.txt");
    std::streambuf *coutBuf = std::cout.rdbuf(tmpOfCout->rdbuf());
    std::ofstream *tmpOfCerr = new std::ofstream("deleteMeCerr.txt");
    std::streambuf *cerrBuf = std::cerr.rdbuf(tmpOfCerr->rdbuf());

    int N = ui->feTree->topLevelItemCount();
    int M = scanVec.size();
    for(int j = 0; j < N; j++){
        scanDone = false;
        processorDone = false;

        if(!(ui->feTree->topLevelItem(j)->child(4)->checkState(1))){continue;} //Is the Scan checkbox checked?

        Fei4 * fe = dynamic_cast<Fei4*>(bk->feList.at(j));

        ScanBase * s = nullptr;

        if(qn == "NS")   {s = new Fei4NoiseScan(bk);}
        if(qn == "DS")   {s = new Fei4DigitalScan(bk);}
        if(qn == "AS")   {s = new Fei4AnalogScan(bk);}
        if(qn == "TS")   {s = new Fei4ThresholdScan(bk);}
        if(qn == "ToTS") {s = new Fei4TotScan(bk);}
        if(qn == "GTT")  {s = new Fei4GlobalThresholdTune(bk);}
        if(qn == "GPT")  {s = new Fei4GlobalPreampTune(bk);}
        if(qn == "PTT")  {s = new Fei4PixelThresholdTune(bk);}
        if(qn == "PPT")  {s = new Fei4PixelPreampTune(bk);}
        if(qn == "CS")   {s = &cs;}

        if(s == nullptr){
            std::cerr << "Invalid scan QString parameter passed or scan object construction failed. Returning...\n";
            return;
        }

        QTreeWidgetItem * plotTreeItem = nullptr; //Plot tree item for current FE
        for(int k = 0; k < ui->plotTree->topLevelItemCount(); k++){ //Is the current FE already in the tree? ...
           if(ui->plotTree->topLevelItem(k)->text(0) == ui->feTree->topLevelItem(j)->text(0)){
               plotTreeItem = ui->plotTree->topLevelItem(k);
               break;
           }
        }

        if(plotTreeItem == nullptr){ //... if not: create a branch for it
            plotTreeItem = new QTreeWidgetItem(ui->plotTree);
            plotTreeItem->setText(0, ui->feTree->topLevelItem(j)->text(0));
        }

        QTreeWidgetItem * plotTreeItemDS = new QTreeWidgetItem(); //plot tree item for current scan
        plotTreeItemDS->setText(0, qn);
        plotTreeItem->addChild(plotTreeItemDS);

        fe->histogrammer = new Fei4Histogrammer();
        fe->histogrammer->connect(fe->clipDataFei4, fe->clipHisto);

        fe->ana = new Fei4Analysis(bk, fe->getRxChannel());
        fe->ana->connect(s, fe->clipHisto, fe->clipResult);

        if (qn=="CS"){
            CustomScan * tmp;
            tmp = dynamic_cast<CustomScan*>(s);
            if(tmp->bA.at(OCC_MAP) == true)   {fe->histogrammer->addHistogrammer(new OccupancyMap());}
            if(tmp->bA.at(TOT_MAP) == true)   {fe->histogrammer->addHistogrammer(new TotMap());}
            if(tmp->bA.at(TOT_2_MAP) == true) {fe->histogrammer->addHistogrammer(new Tot2Map());}

            if(tmp->bA.at(OCC_ANA) == true)   {fe->ana->addAlgorithm(new OccupancyAnalysis());}
            if(tmp->bA.at(NOISE_ANA) == true) {fe->ana->addAlgorithm(new NoiseAnalysis());}
            if(tmp->bA.at(TOT_ANA) == true)   {fe->ana->addAlgorithm(new TotAnalysis());}
            if(tmp->bA.at(S_CU_FIT) == true)  {fe->ana->addAlgorithm(new ScurveFitter());}
            if(tmp->bA.at(PIX_THR) == true)   {fe->ana->addAlgorithm(new OccPixelThresholdTune);}
        }else{
            fe->histogrammer->addHistogrammer(new OccupancyMap());

            if (qn == "ToTS" || qn == "GPT" || qn == "PPT"){
                fe->histogrammer->addHistogrammer(new TotMap());
                fe->histogrammer->addHistogrammer(new Tot2Map());
            }

            if(qn == "NS")   {fe->ana->addAlgorithm(new NoiseAnalysis());}
            if(qn == "DS")   {fe->ana->addAlgorithm(new OccupancyAnalysis());}
            if(qn == "AS")   {fe->ana->addAlgorithm(new OccupancyAnalysis());}
            if(qn == "TS")   {fe->ana->addAlgorithm(new ScurveFitter());}
            if(qn == "ToTS") {fe->ana->addAlgorithm(new TotAnalysis());}
            if(qn == "GTT")  {fe->ana->addAlgorithm(new OccGlobalThresholdTune());}
            if(qn == "GPT")  {fe->ana->addAlgorithm(new TotAnalysis());}
            if(qn == "PTT")  {fe->ana->addAlgorithm(new OccPixelThresholdTune());}
            if(qn == "PPT")  {fe->ana->addAlgorithm(new TotAnalysis());}
        }

        s->init();
        ui->scanProgressBar->setValue(ui->scanProgressBar->value() + (int)(100.0/(7.0*N*M))); //1
        s->preScan();
        ui->scanProgressBar->setValue(ui->scanProgressBar->value() + (int)(100.0/(7.0*N*M))); //2

        unsigned int numThreads = std::thread::hardware_concurrency();
        //std::cout << "-> Starting " << numThreads << " processor Threads:" << std::endl;
        std::vector<std::thread> procThreads;
        for (unsigned i=0; i<numThreads; i++){
            procThreads.push_back(std::thread(process, bk, &scanDone));
            //std::cout << "  -> Processor thread #" << i << " started!" << std::endl;
        }

        std::vector<std::thread> anaThreads;
        //std::cout << "-> Starting histogrammer and analysis threads:" << std::endl;
        if (fe->isActive()){
            anaThreads.push_back(std::thread(analysis, fe->histogrammer, fe->ana, &processorDone));
            //std::cout << "  -> Analysis thread of Fe " << fe->getRxChannel() << std::endl;
        }

        s->run();
        ui->scanProgressBar->setValue(ui->scanProgressBar->value() + (int)(100.0/(7.0*N*M))); //3
        s->postScan();
        ui->scanProgressBar->setValue(ui->scanProgressBar->value() + (int)(100.0/(7.0*N*M))); //4
        scanDone = true;

        for (unsigned i=0; i<numThreads; i++){
            procThreads[i].join();
        }
        ui->scanProgressBar->setValue(ui->scanProgressBar->value() + (int)(100.0/(7.0*N*M))); //5

        processorDone = true;

        for(unsigned i=0; i<anaThreads.size(); i++){
            anaThreads[i].join();
        }
        ui->scanProgressBar->setValue(ui->scanProgressBar->value() + (int)(100.0/(7.0*N*M))); //6

        if(qn != "CS") {
            delete s;
        }

//        fe->toFileBinary();
//        fe->ana->plot("Scan_GUI");

        //DEBUG begin [plotting]

        //clear raw data
        while(fe->clipDataFei4->size() != 0){
            fe->clipDataFei4->popData();
        }

        //clear raw data
        while(fe->clipHisto->size() != 0){
            fe->clipHisto->popData();
        }

        while(fe->clipResult->size() != 0){
            HistogramBase * showMe = fe->clipResult->popData();

            //Add tab to plot tab widget
            QCustomPlot * tabScanPlot = new QCustomPlot(ui->scanPlots_tabWidget); // X
            QWidget * addToTabWidget = dynamic_cast<QWidget*>(tabScanPlot);
            QString newTabName = qn + ' ' + ui->feTree->topLevelItem(j)->text(0);
            ui->scanPlots_tabWidget->addTab(addToTabWidget, newTabName);

            //Add plot to scan tree
            QTreeWidgetItem * plotTreeItemP = new QTreeWidgetItem(plotTreeItemDS); // X
            plotTreeItemP->setText(0, "Plot " + QString::number(plotTreeItemDS->childCount())
                                              + " (" + QString::fromStdString(showMe->getName()) + ")");
            plotTreeItemDS->addChild(plotTreeItemP);

            if(dynamic_cast<Histo2d*>(showMe) != nullptr){
                Histo2d * myHist2d = dynamic_cast<Histo2d*>(showMe);
                //Create plot

                QCPColorMap * colorMap = new QCPColorMap(tabScanPlot->xAxis, tabScanPlot->yAxis);
                QCPColorScale * colorScale = new QCPColorScale(tabScanPlot);

                tabScanPlot->addPlottable(colorMap);
                tabScanPlot->plotLayout()->insertRow(0);
                tabScanPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(tabScanPlot, QString::fromStdString(myHist2d->getName())));

                colorMap->setName(QString::fromStdString(myHist2d->getName()));
                colorMap->data()->setSize(80, 336);
                colorMap->data()->setRange(QCPRange(0, 80), QCPRange(0, 336));
                colorMap->keyAxis()->setLabel(QString::fromStdString(myHist2d->getXaxisTitle()));
                colorMap->valueAxis()->setLabel(QString::fromStdString(myHist2d->getYaxisTitle()));
                for(int xCoord = 0; xCoord<80; xCoord++){
                    for(int yCoord = 0; yCoord<336; yCoord++){
                        double colVal = myHist2d->getBin(yCoord + 336*xCoord);              //TODO make better
                        colorMap->data()->setCell(xCoord, yCoord, colVal);
                    }
                }

                tabScanPlot->plotLayout()->addElement(1, 1, colorScale);
                colorScale->setType(QCPAxis::atRight);
                colorMap->setColorScale(colorScale);
                colorScale->axis()->setLabel(QString::fromStdString(myHist2d->getZaxisTitle()));
                colorMap->setGradient(QCPColorGradient::gpPolar);
                colorMap->rescaleDataRange();

            }else if(dynamic_cast<Histo1d*>(showMe) != nullptr){
                Histo1d * myHist1d = dynamic_cast<Histo1d*>(showMe);

                QCPBars * myBars = new QCPBars(tabScanPlot->xAxis, tabScanPlot->yAxis);
                tabScanPlot->addPlottable(myBars);
                myBars->setName(QString::fromStdString(myHist1d->getName()));

                for(unsigned int i = 0; i < myHist1d->size(); i++) {
                    myBars->addData((double)(i+1), myHist1d->getBin(i));
                }

                myBars->rescaleAxes();
                tabScanPlot->plotLayout()->insertRow(0);
                tabScanPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(tabScanPlot, QString::fromStdString(myHist1d->getName())));
                myBars->keyAxis()->setLabel(QString::fromStdString(myHist1d->getXaxisTitle()));
                myBars->valueAxis()->setLabel(QString::fromStdString(myHist1d->getYaxisTitle()));

            }else{
                std::cerr << "Correct plot type not found or severe cast error\n";                  //DEBUG
                return;
            }

            tabScanPlot->rescaleAxes();
            tabScanPlot->replot();
        }
        ui->scanProgressBar->setValue(ui->scanProgressBar->value() + (int)(100.0/(7.0*N*M))); //7
        delete fe->histogrammer;
        fe->histogrammer = nullptr;
        delete fe->ana;
        fe->ana = nullptr;
    }

    std::cout.rdbuf(coutBuf);
    std::cerr.rdbuf(cerrBuf);
    tmpOfCout->close();
    tmpOfCerr->close();

    std::ifstream tmpInCout("deleteMeCout.txt");
    std::ifstream tmpInCerr("deleteMeCerr.txt");
    std::cout << tmpInCout.rdbuf();
    std::cerr << tmpInCerr.rdbuf();
    tmpInCout.close();
    tmpInCerr.close();

    return;
}
コード例 #2
0
ファイル: scanConsole.cpp プロジェクト: LoganAdams/Yarr
int main(int argc, char *argv[]) {
    std::cout << "#####################################" << std::endl;
    std::cout << "# Welcome to the YARR Scan Console! #" << std::endl;
    std::cout << "#####################################" << std::endl;

    std::cout << "-> Parsing command line parameters ..." << std::endl;
    
    // Init parameters
    unsigned specNum = 0;
    std::string scanType = "digitalscan";
    std::string configPath = "";
    std::string outputDir = "./";
    bool doPlots = false;

    int c;
    while ((c = getopt(argc, argv, "hs:n:c:po:")) != -1) {
        switch (c) {
            case 'h':
                printHelp();
                return 0;
                break;
            case 's':
                scanType = std::string(optarg);
                break;
            case 'n':
                specNum = atoi(optarg);
                break;
            case 'c':
                configPath = optarg;
                break;
            case 'p':
                doPlots = true;
                break;
            case 'o':
                outputDir = std::string(optarg);
                if (outputDir.back() != '/')
                    outputDir = outputDir + "/";
                break;
            case '?':
                if (optopt == 's' || optopt == 'n' || optopt == 'c') {
                    std::cerr << "-> Option " << (char)optopt 
                        << " requires a parameter! (Proceeding with default)" << std::endl;
                } else {
                    std::cerr << "-> Unknown parameter: " << (char)optopt << std::endl;
                }
                break;
            default:
                std::cerr << "-> Error while parsing command line parameters!" << std::endl;
                return -1;
        }
    }

    std::cout << " SPEC Nr: " << specNum << std::endl;
    std::cout << " Scan Type: " << scanType << std::endl;
    std::cout << " Global configuration: " << configPath << std::endl;
    std::cout << " Output Plots: " << doPlots << std::endl;
    std::cout << " Output Directory: " << outputDir << std::endl;

    std::cout << std::endl;
    std::cout << "#################" << std::endl;
    std::cout << "# Init Hardware #" << std::endl;
    std::cout << "#################" << std::endl;
    
    std::cout << "-> Init SPEC " << specNum << " : " << std::endl;
    SpecController spec(specNum);
    TxCore tx(&spec);
    RxCore rx(&spec);
    Bookkeeper bookie(&tx, &rx);
    bookie.setTargetThreshold(1500);
    
    std::cout << "-> Read global config (" << configPath << "):" << std::endl;
    std::fstream gConfig(configPath, std::ios::in);
    if (!gConfig) {
        std::cerr << "## ERROR ## Could not open file: " << configPath << std::endl;
        return -1;
    }

    while (!gConfig.eof() && gConfig) {
        unsigned id, tx, rx;
        std::string name, feCfgPath;
        char peekaboo = gConfig.peek();
        if (peekaboo == '\n') {
            gConfig.ignore();
            peekaboo = gConfig.peek();
        }
        if (peekaboo == '#') {
            char tmp[1024];
            gConfig.getline(tmp, 1024);
            std::cout << " Skipping: " << tmp << std::endl;
        } else {
            gConfig >> name >> id >> tx >> rx >> feCfgPath;
            if (gConfig.eof())
                break;
            std::cout << "-> Found FE " << name << std::endl;
            // Add FE to bookkeeper
            bookie.addFe(id, tx, rx);
            bookie.getLastFe()->setName(name);
            // TODO verify cfg typea
            // Load config
            bookie.getLastFe()->fromFileBinary(feCfgPath);
            // Set chipId again after loading in case we got std cfg
            bookie.getLastFe()->setChipId(id);
        }
    }
        
    std::cout << std::endl;
    std::cout << "#################" << std::endl;
    std::cout << "# Configure FEs #" << std::endl;
    std::cout << "#################" << std::endl;
    
    std::chrono::steady_clock::time_point cfg_start = std::chrono::steady_clock::now();
    for (unsigned i=0; i<bookie.feList.size(); i++) {
        Fei4 *fe = bookie.feList[i];
        std::cout << "-> Configuring " << fe->getName() << std::endl;
        // Select correct channel
        tx.setCmdEnable(0x1 << fe->getTxChannel());
        // Configure
        fe->configure();
        fe->configurePixels(); // TODO should call abstract configure only
        // Wait for fifo to be empty
        std::this_thread::sleep_for(std::chrono::microseconds(100));
        while(!tx.isCmdEmpty());
    }
    std::chrono::steady_clock::time_point cfg_end = std::chrono::steady_clock::now();
    std::cout << "-> All FEs configured in " 
        << std::chrono::duration_cast<std::chrono::milliseconds>(cfg_end-cfg_start).count() << " ms !" << std::endl;
    
    // Wait for rx to sync with FE stream
    // TODO Check RX sync
    std::this_thread::sleep_for(std::chrono::microseconds(1000));
    // Enable all active channels
    tx.setCmdEnable(bookie.getTxMask());
    std::cout << "-> Setting Tx Mask to: 0x" << std::hex << bookie.getTxMask() << std::dec << std::endl;
    rx.setRxEnable(bookie.getRxMask());
    std::cout << "-> Setting Rx Mask to: 0x" << std::hex << bookie.getRxMask() << std::dec << std::endl;
    
    std::cout << std::endl;
    std::cout << "##############" << std::endl;
    std::cout << "# Setup Scan #" << std::endl;
    std::cout << "##############" << std::endl;

    // TODO Make this nice
    ScanBase *s = NULL;
    std::cout << "-> Selecting Scan: " << scanType << std::endl;
    if (scanType == "digitalscan") {
        std::cout << "-> Found Digital Scan" << std::endl;
        s = new Fei4DigitalScan(&bookie);
    } else if (scanType == "analogscan") {
        std::cout << "-> Found Analog Scan" << std::endl;
        s = new Fei4AnalogScan(&bookie);
    } else if (scanType == "thresholdscan") {
        std::cout << "-> Found Threshold Scan" << std::endl;
        s = new Fei4ThresholdScan(&bookie);
    } else if (scanType == "totscan") {
        std::cout << "-> Found ToT Scan" << std::endl;
        s = new Fei4TotScan(&bookie);
    } else if (scanType == "tune_globalthreshold") {
        std::cout << "-> Found Global Threshold Tuning" << std::endl;
        s = new Fei4GlobalThresholdTune(&bookie);
    } else if (scanType == "tune_pixelthreshold") {
        std::cout << "-> Found Pixel Threshold Tuning" << std::endl;
        s = new Fei4PixelThresholdTune(&bookie);
    } else if (scanType == "tune_globalpreamp") {
        std::cout << "-> Found Global Preamp Tuning" << std::endl;
        s = new Fei4GlobalPreampTune(&bookie);
    } else if (scanType == "tune_pixelpreamp") {
        std::cout << "-> Found Pixel Preamp Tuning" << std::endl;
        s = new Fei4PixelPreampTune(&bookie);
    } else if (scanType == "noisescan") {
        std::cout << "-> Found Noisescan" << std::endl;
        s = new Fei4NoiseScan(&bookie);
    } else {
        std::cout << "-> No matching Scan found, possible:" << std::endl;
        listScans();
        std::cerr << "-> Aborting!" << std::endl;
        return -1;
    }
    
    // Init histogrammer and analysis
    for (unsigned i=0; i<bookie.feList.size(); i++) {
        Fei4 *fe = bookie.feList[i];
        if (fe->isActive()) {
            // Init histogrammer per FE
            fe->histogrammer = new Fei4Histogrammer();
            fe->histogrammer->connect(fe->clipDataFei4, fe->clipHisto);
            // Add generic histograms
            fe->histogrammer->addHistogrammer(new OccupancyMap());
            fe->histogrammer->addHistogrammer(new TotMap());
            fe->histogrammer->addHistogrammer(new Tot2Map());
            fe->histogrammer->addHistogrammer(new L1Dist());
            fe->histogrammer->addHistogrammer(new HitDist());
           
            // Init analysis per FE and depending on scan type
            fe->ana = new Fei4Analysis(&bookie, fe->getRxChannel());
            fe->ana->connect(s, fe->clipHisto, fe->clipResult);
            fe->ana->addAlgorithm(new L1Analysis());
            if (scanType == "digitalscan") {
                fe->ana->addAlgorithm(new OccupancyAnalysis());
            } else if (scanType == "analogscan") {
                fe->ana->addAlgorithm(new OccupancyAnalysis());
            } else if (scanType == "thresholdscan") {
                fe->ana->addAlgorithm(new ScurveFitter());
            } else if (scanType == "totscan") {
                fe->ana->addAlgorithm(new TotAnalysis());
            } else if (scanType == "tune_globalthreshold") {
                fe->ana->addAlgorithm(new OccGlobalThresholdTune());
            } else if (scanType == "tune_pixelthreshold") {
                fe->ana->addAlgorithm(new OccPixelThresholdTune());
            } else if (scanType == "tune_globalpreamp") {
                fe->ana->addAlgorithm(new TotAnalysis());
            } else if (scanType == "tune_pixelpreamp") {
                fe->ana->addAlgorithm(new TotAnalysis());
            } else if (scanType == "noisescan") {
                fe->ana->addAlgorithm(new NoiseAnalysis());
            } else {
                std::cout << "-> Analyses not defined for scan type" << std::endl;
                listScans();
                std::cerr << "-> Aborting!" << std::endl;
                return -1;
            }
        }
    }

    std::cout << "-> Running pre scan!" << std::endl;
    s->init();
    s->preScan();

    unsigned int numThreads = std::thread::hardware_concurrency();
    std::cout << "-> Starting " << numThreads << " processor Threads:" << std::endl; 
    std::vector<std::thread> procThreads;
    for (unsigned i=0; i<numThreads; i++) {
        procThreads.push_back(std::thread(process, &bookie));
        std::cout << "  -> Processor thread #" << i << " started!" << std::endl;
    }

    std::vector<std::thread> anaThreads;
    std::cout << "-> Starting histogrammer and analysis threads:" << std::endl;
    for (unsigned i=0; i<bookie.feList.size(); i++) {
        Fei4 *fe = bookie.feList[i];
        if (fe->isActive()) {
            anaThreads.push_back(std::thread(analysis, fe->histogrammer, fe->ana));
            std::cout << "  -> Analysis thread of Fe " << fe->getRxChannel() << std::endl;
        }
    }

    std::cout << std::endl;
    std::cout << "########" << std::endl;
    std::cout << "# Scan #" << std::endl;
    std::cout << "########" << std::endl;

    std::cout << "-> Starting scan!" << std::endl;
    std::chrono::steady_clock::time_point scan_start = std::chrono::steady_clock::now();
    s->run();
    s->postScan();
    std::cout << "-> Scan done!" << std::endl;
    scanDone = true;
    std::chrono::steady_clock::time_point scan_done = std::chrono::steady_clock::now();
    std::cout << "-> Waiting for processors to finish ..." << std::endl;
    for (unsigned i=0; i<numThreads; i++) {
        procThreads[i].join();
    }
    std::chrono::steady_clock::time_point processor_done = std::chrono::steady_clock::now();
    processorDone = true;
    std::cout << "-> Processor done, waiting for analysis ..." << std::endl;
    for (unsigned i=0; i<anaThreads.size(); i++) {
        anaThreads[i].join();
    }
    std::chrono::steady_clock::time_point all_done = std::chrono::steady_clock::now();
    std::cout << "-> All done!" << std::endl;

    tx.setCmdEnable(0x0);
    rx.setRxEnable(0x0);

    std::cout << std::endl;
    std::cout << "##########" << std::endl;
    std::cout << "# Timing #" << std::endl;
    std::cout << "##########" << std::endl;

    std::cout << "-> Configuration: " << std::chrono::duration_cast<std::chrono::milliseconds>(cfg_end-cfg_start).count() << " ms" << std::endl;
    std::cout << "-> Scan:          " << std::chrono::duration_cast<std::chrono::milliseconds>(scan_done-scan_start).count() << " ms" << std::endl;
    std::cout << "-> Processing:    " << std::chrono::duration_cast<std::chrono::milliseconds>(processor_done-scan_done).count() << " ms" << std::endl;
    std::cout << "-> Analysis:      " << std::chrono::duration_cast<std::chrono::milliseconds>(all_done-processor_done).count() << " ms" << std::endl;

    std::cout << std::endl;
    std::cout << "###########" << std::endl;
    std::cout << "# Cleanup #" << std::endl;
    std::cout << "###########" << std::endl;
    
    // Cleanup
    delete s;
    for (unsigned i=0; i<bookie.feList.size(); i++) {
        Fei4 *fe = bookie.feList[i];
        if (fe->isActive()) {
            // Save config
            std::cout << "-> Saving config of FE " << fe->getName() << std::endl;
            fe->toFileBinary();
            // Plot
            if (doPlots) {
                std::cout << "-> Plotting histograms of FE " << fe->getRxChannel() << std::endl;
                fe->ana->plot("ch" + std::to_string(fe->getRxChannel()) + "_" + scanType, outputDir);
            }
            // Free
            delete fe->histogrammer;
            fe->histogrammer = NULL;
            delete fe->ana;
            fe->ana = NULL;
        }
    }
    return 0;
}