Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-06-17 02:28:48

0001 #ifndef PLOT_2D__H
0002 #define PLOT_2D__H
0003 
0004 #include "Plot1D.h"
0005 #include "PlotCompareUtility.h"
0006 #include "PlotTypes.h"
0007 
0008 #include <TGaxis.h>
0009 #include <TH1F.h>
0010 #include <TH2F.h>
0011 #include <TLine.h>
0012 #include <TProfile.h>
0013 
0014 #include <cmath>
0015 #include <cstdio>
0016 #include <fstream>
0017 #include <iostream>
0018 #include <string>
0019 
0020 template <>
0021 inline bool PlotCompareUtility::compare<Plot2D>(HistoData *HD) {
0022   // get the reference and comparison histograms
0023   TH2F *href2d = (TH2F *)HD->getRefHisto();
0024   TH2F *hnew2d = (TH2F *)HD->getNewHisto();
0025 
0026   // do not run comparisons if either histogram is empty/broken
0027   if (hnew2d == nullptr || href2d == nullptr || hnew2d->GetEntries() <= 1 || href2d->GetEntries() <= 1) {
0028     // std::cerr << HD->getName() << " error: unable to retrieve histogram (or
0029     // no entries)\n";
0030     HD->setIsEmpty(true);
0031     return false;
0032   }
0033 
0034   // prepare an overall result
0035   bool projectionsPassed = true;
0036 
0037   // loop over axes (projections on one or both may be requested)
0038   for (int axis = axisX; axis <= axisY; ++axis) {
0039     // for X: verify projections requested and proper Y binning of href2d and
0040     // hnew2d
0041     if (axis == axisX && !HD->getDoProjectionsX())
0042       continue;
0043     if (axis == axisX && href2d->GetNbinsY() != hnew2d->GetNbinsY()) {
0044       std::cerr << HD->getName() << " error: incorrect number of bins for X projection tests\n";
0045       projectionsPassed = false;
0046       continue;
0047     }
0048 
0049     // for Y: verify projections requested and proper X binning of href2d and
0050     // hnew2d
0051     if (axis == axisY && !HD->getDoProjectionsY())
0052       continue;
0053     if (axis == axisY && href2d->GetNbinsX() != hnew2d->GetNbinsX()) {
0054       std::cerr << HD->getName() << " error: incorrect number of bins for Y projection tests\n";
0055       projectionsPassed = false;
0056       continue;
0057     }
0058 
0059     // setup the rebinning variables
0060     int nBins = (axis == axisX) ? href2d->GetNbinsY() : href2d->GetNbinsX();
0061     int nProjections = (axis == axisX) ? HD->getMaxProjectionsX() : HD->getMaxProjectionsY();
0062     int nGroups = (int)ceil(float(nBins) / nProjections);
0063     bool rebinned = false;
0064 
0065     // for X projections: if required rebin a clone of href2d and hnew2d
0066     if (axis == axisX && HD->getDoAllow2DRebinningX() && nGroups > 1) {
0067       href2d = (TH2F *)(((TH2F *)(href2d->Clone()))->RebinY(nGroups));
0068       hnew2d = (TH2F *)(((TH2F *)(hnew2d->Clone()))->RebinY(nGroups));
0069       nBins = href2d->GetNbinsY();
0070       rebinned = true;
0071     }
0072 
0073     // for Y projections: if required rebin a clone of href2d and hnew2d
0074     if (axis == axisY && HD->getDoAllow2DRebinningY() && nGroups > 1) {
0075       href2d = (TH2F *)(((TH2F *)(href2d->Clone()))->RebinX(nGroups));
0076       hnew2d = (TH2F *)(((TH2F *)(hnew2d->Clone()))->RebinX(nGroups));
0077       nBins = href2d->GetNbinsX();
0078       rebinned = true;
0079     }
0080 
0081     // loop over bins in histograms (go backwords to keep in order)
0082     // for (int bin = nBins; bin >= 1; --bin) {
0083     for (int bin = 1; bin <= nBins; ++bin) {
0084       std::cout << "bin " << bin << " of " << nBins << std::endl;
0085       // create some unique identifiers for the histogram names
0086       TString projName = HD->getName() + (axis == axisX ? "_px" : "_py");
0087       projName += bin;
0088       TString newProjName = "new_";
0089       newProjName += projName;
0090       TString refProjName = "ref_";
0091       refProjName += projName;
0092 
0093       // get the 1d projections for this bin out of the histogram
0094       TH1D *hnew = (axis == axisX) ? hnew2d->ProjectionX(newProjName.Data(), bin, bin)
0095                                    : hnew2d->ProjectionY(newProjName.Data(), bin, bin);
0096       TH1D *href = (axis == axisX) ? href2d->ProjectionX(refProjName.Data(), bin, bin)
0097                                    : href2d->ProjectionY(refProjName.Data(), bin, bin);
0098 
0099       // set histogram axis labels
0100       hnew->GetXaxis()->SetTitle((axis == axisX ? hnew2d->GetXaxis()->GetTitle() : hnew2d->GetYaxis()->GetTitle()));
0101       href->GetXaxis()->SetTitle((axis == axisX ? href2d->GetXaxis()->GetTitle() : href2d->GetYaxis()->GetTitle()));
0102 
0103       // allow Root to delete these histograms after display
0104       hnew->SetBit(kCanDelete);
0105       href->SetBit(kCanDelete);
0106 
0107       // create a new HistoData based on this projection
0108       HistoData *proj = (axis == axisX) ? addProjectionXData(HD, projName.Data(), Plot1D, bin, hnew, href)
0109                                         : addProjectionYData(HD, projName.Data(), Plot1D, bin, hnew, href);
0110 
0111       // ignore empty bins
0112       // if (hnew->Integral() == 0 || href->Integral() == 0) continue;
0113       if (hnew->GetEntries() <= 1 || href->GetEntries() <= 1 || hnew->Integral() == 0 || href->Integral() == 0)
0114         continue;
0115 
0116       // run this new HistoData through compare<Plot1D>
0117       projectionsPassed &= compare<Plot1D>(proj);
0118 
0119       // get the high and low scores from this comparison
0120       float lowScore = proj->getLowScore();
0121       float highScore = proj->getHighScore();
0122       if (lowScore < HD->getLowScore())
0123         HD->setLowScore(lowScore);
0124       if (highScore > HD->getHighScore())
0125         HD->setHighScore(highScore);
0126     }
0127 
0128     // if 2d histograms were rebinned, delete the clone and re-get the original
0129     if (rebinned) {
0130       delete href2d;
0131       href2d = (TH2F *)HD->getRefHisto();
0132       delete hnew2d;
0133       hnew2d = (TH2F *)HD->getNewHisto();
0134     }
0135   }
0136 
0137   // check overall result
0138   HD->setResult(projectionsPassed);
0139   HD->setIsEmpty(false);
0140 
0141   // returns true on test passed and false on test failed
0142   return projectionsPassed;
0143 }
0144 
0145 template <>
0146 inline void PlotCompareUtility::makePlots<Plot2D>(HistoData *HD) {
0147   // do not make any new plot if empty
0148   if (HD->getIsEmpty()) {
0149     HD->setResultImage("NoData_Results.gif");
0150     HD->setResultTarget("NoData_Results.gif");
0151     return;
0152   }
0153 
0154   // loop over the projections to make 1D plots
0155   std::vector<HistoData>::iterator hd;
0156   for (hd = projectionsX[HD].begin(); hd != projectionsX[HD].end(); hd++)
0157     makePlots<Plot1D>(&(*hd));
0158   for (hd = projectionsY[HD].begin(); hd != projectionsY[HD].end(); hd++)
0159     makePlots<Plot1D>(&(*hd));
0160 
0161   // make projection summaries
0162   for (int axis = axisX; axis <= axisY; ++axis) {
0163     // get the list of projections associated with this HistoData
0164     std::vector<HistoData> *proj = (axis == axisX) ? &projectionsX[HD] : &projectionsY[HD];
0165     if (proj == nullptr || proj->empty())
0166       continue;
0167 
0168     // get the 2d histograms
0169     TH2F *hnew2d = (TH2F *)HD->getNewHisto();
0170     TH2F *href2d = (TH2F *)HD->getRefHisto();
0171 
0172     // generate a reasonable width for the projections summary
0173     int numHistos = proj->size();
0174     int bodyWidth = int(float(numHistos * projectionsBarsThickness) * 1.5);
0175     projectionsWidth = projectionsLeftMargin + projectionsRightMargin + bodyWidth;
0176 
0177     // the canvas is rescaled during gif conversion, so add padding to Canvas
0178     // dimensions
0179     int projectionsCanvasWidth = projectionsWidth + 4;
0180     int projectionsCanvasHeight = projectionsHeight + 28;
0181 
0182     // create and setup projections canvas
0183     TCanvas projectionsCanvas(
0184         "projectionsCanvas", "projectionsCanvas", projectionsCanvasWidth, projectionsCanvasHeight);
0185     projectionsCanvas.SetFrameFillColor(10);
0186     projectionsCanvas.SetLogy(1);
0187     projectionsCanvas.SetTopMargin(float(projectionsTopMargin) / projectionsHeight);
0188     projectionsCanvas.SetLeftMargin(float(projectionsLeftMargin) / projectionsWidth);
0189     projectionsCanvas.SetRightMargin(float(projectionsRightMargin) / projectionsWidth);
0190     projectionsCanvas.SetBottomMargin(float(projectionsBottomMargin) / projectionsHeight);
0191     projectionsCanvas.Draw();
0192 
0193     // create and setup the summary histogram
0194     TH1F projectionsSummary(
0195         "projectionsSummary", "Compatibility with Reference Histograms", numHistos, 1, numHistos + 1);
0196     projectionsSummary.GetYaxis()->SetRangeUser(getThreshold() / 10, 2);
0197     projectionsSummary.SetStats(false);
0198 
0199     // display histogram (take axis from original histogram)
0200     projectionsSummary.Draw("AH");
0201 
0202     // draw X axis
0203     float xMin = hnew2d->GetXaxis()->GetXmin();
0204     float xMax = hnew2d->GetXaxis()->GetXmax();
0205     int ticksNDiv = numHistos * 20 + bodyWidth / 50;  // formerly *20
0206     TGaxis *xAxis = new TGaxis(1, 0, numHistos + 1, 0, xMin, xMax, ticksNDiv, "");
0207     if (axis == axisX)
0208       xAxis->SetTitle(hnew2d->GetYaxis()->GetTitle());
0209     if (axis == axisY)
0210       xAxis->SetTitle(hnew2d->GetXaxis()->GetTitle());
0211     xAxis->Draw();
0212 
0213     // draw Y axis
0214     float yMin = getThreshold() / 10;
0215     float yMax = 2;
0216     TGaxis *yAxis = new TGaxis(1, yMin, 1, yMax, yMin, yMax, 510, "G");
0217     yAxis->SetTitle("Compatibility");
0218     yAxis->Draw();
0219 
0220     // loop over projections and draw result
0221     std::vector<HistoData>::iterator pd;
0222     for (pd = proj->begin(); pd != proj->end(); pd++)
0223       pd->drawResult(&projectionsSummary, true, false);
0224 
0225     // draw the pass/fail cutoff line
0226     TLine passLine(1, getThreshold(), numHistos + 1, getThreshold());
0227     passLine.SetLineColor(kRed);
0228     passLine.SetLineWidth(2);
0229     passLine.SetLineStyle(2);
0230     passLine.Draw("SAME");
0231 
0232     // create the summary image
0233     std::string gifName = HD->getName() + (axis == axisX ? "_Results_px.gif" : "_Results_py.gif");
0234     projectionsCanvas.Print(gifName.c_str());
0235 
0236     // make overall projection plot of the original 2d histogram
0237     std::string projName = HD->getName() + (axis == axisX ? "_py" : "_px");
0238     std::string newBinsProj = projName + "_new";
0239     std::string refBinsProj = projName + "_ref";
0240     TH1D *href = (axis == axisX) ? href2d->ProjectionY(refBinsProj.c_str()) : href2d->ProjectionX(refBinsProj.c_str());
0241     TH1D *hnew = (axis == axisX) ? hnew2d->ProjectionY(newBinsProj.c_str()) : hnew2d->ProjectionX(newBinsProj.c_str());
0242 
0243     // allow Root to delete these histograms after display
0244     href->SetBit(kCanDelete);
0245     hnew->SetBit(kCanDelete);
0246 
0247     // create a new HistoData based on this projection and plot it
0248     HistoData allBins(projName, Plot1D, 0, hnew, href);
0249     allBins.setIsEmpty(false);
0250     allBins.setShadedFillColor(HD->getShadedFillColor());
0251     allBins.setShadedFillStyle(HD->getShadedFillStyle());
0252     allBins.setShadedLineColor(HD->getShadedLineColor());
0253     makePlots<Plot1D>(&allBins);
0254 
0255     // set the default image (axisY takes priority by default)
0256     if (HD->getResultImage().empty() || axis == axisY)
0257       HD->setResultImage(projName + "_Results.gif");
0258 
0259     // set the default target (in case additional HTML code is/was not produced)
0260     std::string currentTarget = HD->getResultTarget();
0261     std::string xImgTarget = HD->getName() + "_px_Results.gif";
0262     if (currentTarget.empty() || (axis == axisY && currentTarget == xImgTarget))
0263       HD->setResultTarget(projName + "_Results.gif");
0264   }
0265 
0266   /*
0267   // make overall profile plot of the original 2d histogram
0268   for (int axis = axisX; axis <= axisY; ++axis) {
0269 
0270     // make profile plots out of original 2D histograms
0271     TProfile *pref = (axis == axisX) ? ((TH2F *)HD->getRefHisto())->ProfileY() :
0272   ((TH2F *)HD->getRefHisto())->ProfileX(); TProfile *pnew = (axis == axisX) ?
0273   ((TH2F *)HD->getNewHisto())->ProfileY() : ((TH2F
0274   *)HD->getNewHisto())->ProfileX();
0275 
0276     // renormalize results for display
0277         renormalize(pref,pnew);
0278 
0279     // do not allow Root to deallocate this memory after drawing (tries to free
0280   twice?) pref->SetBit(kCanDelete); pnew->SetBit(kCanDelete);
0281 
0282     // set drawing options on the reference histogram
0283     pref->SetStats(0);
0284     pref->SetLineColor(kBlack);
0285     pref->SetMarkerColor(kBlack);
0286 
0287     // set drawing options on the new histogram
0288     pnew->SetStats(0);
0289     pnew->SetLineColor(HD->getSolidLineColor());
0290 
0291     // place the test results as the title
0292     TString title = HD->getName();
0293 
0294     // the canvas is rescaled during gif conversion, so add padding to Canvas
0295   dimensions int plotsCanvasWidth = plotsWidth + 4; int plotsCanvasHeight =
0296   plotsHeight + 28;
0297 
0298     // setup canvas for displaying the compared histograms
0299     TCanvas hCanvas("hCanvas",title.Data(),plotsCanvasWidth,plotsCanvasHeight);
0300     hCanvas.SetTopMargin(float(plotsTopMargin) / plotsHeight);
0301     hCanvas.SetLeftMargin(float(plotsLeftMargin) / plotsWidth);
0302     hCanvas.SetRightMargin(float(plotsRightMargin) / plotsWidth);
0303     hCanvas.SetBottomMargin(float(plotsBottomMargin) / plotsHeight);
0304     hCanvas.SetFrameFillColor(10);
0305     hCanvas.SetGrid();
0306     //hCanvas.SetLogy(1);
0307     hCanvas.Draw();
0308 
0309     // draw the profiles
0310     pref->Draw();
0311     pnew->Draw("SAME");
0312     if (HD->getDoDrawErrorBars()) pnew->Draw("E1SAME");
0313 
0314     // draw a legend
0315     TLegend legend(0.15,0.01,0.3, 0.08);
0316     legend.AddEntry(pnew,"New","lF");
0317     legend.AddEntry(pref,"Reference","lF");
0318     legend.SetFillColor(kNone);
0319     legend.Draw("SAME");
0320 
0321     // create the plots overlay image
0322     std::string gifName = HD->getName() + (axis == axisX ? "_pfx.gif" :
0323   "_pfy.gif"); hCanvas.Print(gifName.c_str());
0324 
0325     // set the default image (axisY takes priority by default)
0326     if (HD->getResultImage() == "" || axis == axisY)
0327   HD->setResultImage(gifName);
0328 
0329     // set the default target (in case additional HTML code is/was not produced)
0330     std::string currentTarget = HD->getResultTarget();
0331     std::string xImgTarget = HD->getName() + "_pfx.gif";
0332     if (currentTarget == "" || (axis == axisY && currentTarget == xImgTarget))
0333   HD->setResultTarget(gifName);
0334 
0335   }
0336   */
0337 }
0338 
0339 template <>
0340 inline void PlotCompareUtility::makeHTML<Plot2D>(HistoData *HD) {
0341   /* at present, makeHTML<Plot1D> does nothing, so don't waste the CPU cycles
0342   // loop over projections and produce HTML
0343   std::vector<HistoData>::iterator hd;
0344   for (hd = projectionsX[HD].begin(); hd != projectionsX[HD].end(); hd++)
0345   makePlots<Plot1D>(&(*hd)); for (hd = projectionsY[HD].begin(); hd !=
0346   projectionsY[HD].end(); hd++) makePlots<Plot1D>(&(*hd));
0347   */
0348 
0349   // get the HistoData name for later reuse
0350   std::string Name = HD->getName();
0351 
0352   // loop over the axes to see if projections were produced
0353   bool pfDone[2] = {false, false};
0354   for (int axis = axisX; axis <= axisY; axis++) {
0355     // get the list of projections associated with this HistoData
0356     std::vector<HistoData> *proj = (axis == axisX) ? &projectionsX[HD] : &projectionsY[HD];
0357     if (proj == nullptr || proj->empty())
0358       continue;
0359     else
0360       pfDone[axis] = true;
0361 
0362     // setup some names, etc. for insertion into the HTML
0363     std::string gifNameProjections = Name + (axis == axisX ? "_Results_px.gif" : "_Results_py.gif");
0364     std::string gifNameAllProj = Name + (axis == axisX ? "_py_Results.gif" : "_px_Results.gif");
0365     std::string gifNameProfile = Name + (axis == axisX ? "_pfx.gif" : "_pfy.gif");
0366     std::string gifBinPrefix = Name + (axis == axisX ? "_px" : "_py");
0367 
0368     // setup some locations to put thumbnails, etc.
0369     int offset = 10;
0370     int thumbWidth = plotsWidth / 4;
0371     int thumbHeight = plotsHeight / 4;
0372     int bodyWidth = projectionsWidth - projectionsLeftMargin - projectionsRightMargin;
0373     int leftThumbPos = offset + projectionsLeftMargin + bodyWidth / 4 - thumbWidth / 2;
0374     int rightThumbPos = leftThumbPos + bodyWidth / 2;
0375     int thumbsLoc = projectionsTopMargin + thumbHeight / 2;
0376 
0377     // create the profile's HTML document
0378     std::string htmlNameProfile = Name + (axis == axisX ? "_Results_px.html" : "_Results_py.html");
0379     std::ofstream fout(htmlNameProfile.c_str());
0380 
0381     // print top portion of document
0382     fout << "<!DOCTYPE gif PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>" << std::endl
0383          << "<html>" << std::endl
0384          << "  <head>" << std::endl
0385          << "    <title>Compatibility of Projections for " << HD->getRefHisto()->GetTitle() << "</title>" << std::endl
0386          << "    <script type='text/javascript'>" << std::endl
0387          << std::endl
0388          << "      function tn(target,image,class) {" << std::endl
0389          << "        clear()" << std::endl
0390          << "        "
0391             "document.getElementById('thumb_div').setAttribute('class',class)"
0392          << std::endl
0393          << "        "
0394             "document.getElementById('thumb_div').setAttribute('className',"
0395             "class)"
0396          << std::endl
0397          << "        document.getElementById('thumb_link').href = target" << std::endl
0398          << "        document.getElementById('thumb_img').src = image" << std::endl
0399          << "        document.getElementById('thumb_img').width = '" << thumbWidth << "'" << std::endl
0400          << "        document.getElementById('thumb_img').height = '" << thumbHeight << "'" << std::endl
0401          << "        document.getElementById('thumb_img').border = '1'" << std::endl
0402          << "      }" << std::endl
0403          << std::endl
0404          << "      function clear() {" << std::endl
0405          << "        document.getElementById('thumb_link').href = '#'" << std::endl
0406          << "        document.getElementById('thumb_img').src = ''" << std::endl
0407          << "        document.getElementById('thumb_img').width = '0'" << std::endl
0408          << "        document.getElementById('thumb_img').height = '0'" << std::endl
0409          << "        document.getElementById('thumb_img').border = '0'" << std::endl
0410          << "      }" << std::endl
0411          << std::endl
0412          << "    </script>" << std::endl
0413          << "  </head>" << std::endl
0414          << "  <body onClick=\"window.location.href='index.html'\">"
0415          << std::endl
0416          //         << "<a href='index.html'>"
0417          << "    <style type='text/css'>" << std::endl
0418          << "      #thumb_div {}" << std::endl
0419          << "      div.thumb_left {position: absolute; left: " << leftThumbPos << "px; top: " << thumbsLoc << "px;}"
0420          << std::endl
0421          << "      div.thumb_right {position: absolute; left: " << rightThumbPos << "px; top: " << thumbsLoc << "px;}"
0422          << std::endl
0423          << "      #main_d {position: absolute; left: " << offset << "px;}" << std::endl
0424          << "      a:link {color: #000000}" << std::endl
0425          << "      a:visited {color: #000000}" << std::endl
0426          << "      a:hover {color: #000000}" << std::endl
0427          << "      a:active {color: #000000}" << std::endl
0428          << "    </style>" << std::endl
0429          << "    <div id='main_d'>"
0430          << std::endl
0431          // << " <p>" <<   HD->getRefHisto()->GetTitle() << "</p>"  //include
0432          // the Title of the Plot as a title of the page
0433          << "      <img src='" << gifNameProjections << "' usemap='#results' alt=''"
0434          << " height=" << projectionsHeight << " width=" << projectionsWidth << " border=0>" << std::endl
0435          << "      <map id='#results' name='results' onMouseOut=\"clear()\">" << std::endl;
0436 
0437     // loop over projections
0438     std::vector<HistoData>::iterator pd;
0439     for (pd = proj->begin(); pd != proj->end(); pd++) {
0440       // determine map coordinates for this bin (1 pixel offset due to borders?)
0441       int bin = pd->getBin();
0442       int x1 = projectionsLeftMargin + int(float(bin * 1.5 - 1.25) * projectionsBarsThickness);
0443       int x2 = x1 + projectionsBarsThickness;
0444       int y1 = projectionsTopMargin + 1;
0445       int y2 = projectionsHeight - projectionsBottomMargin;
0446       std::string image = pd->getResultImage();
0447       std::string target = pd->getResultTarget();
0448 
0449       // add coordinates area to image map
0450       std::string tnClass = (bin - 1 >= float(proj->size()) / 2 ? "thumb_left" : "thumb_right");
0451       fout << "        <area shape='rect' alt='' coords='" << x1 << "," << y1 << "," << x2 << "," << y2 << "'"
0452            << " href='" << target << "' onMouseOver=\"tn('" << target << "','" << image << "','" << tnClass << "')\" "
0453            << "onMouseDown=\"window.location.href='" << target << "'\">" << std::endl;
0454     }
0455 
0456     fout << "        <area shape='default' nohref='nohref' "
0457             "onMouseDown='window.location.reload()' alt=''>"
0458          << std::endl
0459          << "      </map>" << std::endl
0460          << "      <br><img src=\"" << gifNameAllProj << "\">" << std::endl
0461          << "    </div>" << std::endl
0462          << "    <div id='thumb_div'><a href='#' id='thumb_link'><img src='' "
0463             "id='thumb_img' width=0 height=0 border=0></a></div>"
0464          << std::endl
0465          //         << " </a>"
0466          << "  </body>" << std::endl
0467          << "</html>" << std::endl;
0468 
0469     // close the file
0470     HD->setResultTarget(htmlNameProfile);
0471     fout.close();
0472   }
0473 
0474   // if both profile dimensions were filled, we need an additional HTML document
0475   if (pfDone[axisX] && pfDone[axisY]) {
0476     // create HTML support code for this HistoData
0477     std::string html = Name + "_Results_Profiles.html";
0478     std::ofstream fout(html.c_str());
0479 
0480     // make a simple frames portal to show both profile
0481     fout << "<html>" << std::endl
0482          << "  <frameset rows=\"50%,50%\">" << std::endl
0483          << "    <frame src=\"" << Name << "_Results_py.html\">" << std::endl
0484          << "    <frame src=\"" << Name << "_Results_px.html\">" << std::endl
0485          << "    <noframes><body>" << std::endl
0486          << "      unable to display frames -- click to select desired page" << std::endl
0487          << "      <br><a href =\"" << Name << "_Results_py.html\">Y Projections</a>" << std::endl
0488          << "      <br><a href =\"" << Name << "_Results_px.html\">X Projections</a>" << std::endl
0489          << "    </body></noframes>" << std::endl
0490          << "  </frameset>" << std::endl
0491          << "</html>" << std::endl;
0492 
0493     // close the file
0494     HD->setResultTarget(html);
0495     fout.close();
0496   }
0497 }
0498 
0499 #endif  // PLOT_2D__H