CMS 3D CMS Logo

CMSSW_4_4_3_patch1/src/RecoLocalTracker/SiPixelRecHits/plugins/PixelCPETemplateReco.cc

Go to the documentation of this file.
00001 // Include our own header first
00002 #include "PixelCPETemplateReco.h"
00003 
00004 // Geometry services
00005 #include "Geometry/TrackerGeometryBuilder/interface/PixelGeomDetUnit.h"
00006 #include "Geometry/TrackerGeometryBuilder/interface/RectangularPixelTopology.h"
00007 
00008 //#define DEBUG
00009 
00010 // MessageLogger
00011 #include "FWCore/MessageLogger/interface/MessageLogger.h"
00012 
00013 // Magnetic field
00014 #include "MagneticField/Engine/interface/MagneticField.h"
00015 
00016 // The template header files
00017 #include "SiPixelTemplateReco.h"
00018 
00019 #include "SiPixelTemplateSplit.h"
00020 
00021 #include "FWCore/MessageLogger/interface/MessageLogger.h"
00022 
00023 #include <vector>
00024 #include "boost/multi_array.hpp"
00025 
00026 #include <iostream>
00027 
00028 using namespace SiPixelTemplateReco;
00029 using namespace std;
00030 
00031 const float PI = 3.141593;
00032 const float HALFPI = PI * 0.5;
00033 const float degsPerRad = 57.29578;
00034 
00035 // &&& need a class const
00036 const float micronsToCm = 1.0e-4;
00037 
00038 const int cluster_matrix_size_x = 13;
00039 const int cluster_matrix_size_y = 21;
00040 
00041 //-----------------------------------------------------------------------------
00042 //  Constructor.  All detUnit-dependent quantities will be initialized later,
00043 //  in setTheDet().  Here we only load the templates into the template store templ_ .
00044 //-----------------------------------------------------------------------------
00045 PixelCPETemplateReco::PixelCPETemplateReco(edm::ParameterSet const & conf, 
00046                                            const MagneticField * mag, const SiPixelLorentzAngle * lorentzAngle, 
00047                                            const SiPixelTemplateDBObject * templateDBobject) 
00048   : PixelCPEBase(conf, mag, lorentzAngle, 0, templateDBobject)
00049 {
00050   //cout << "From PixelCPETemplateReco::PixelCPETemplateReco(...)" << endl;
00051 
00052   // &&& initialize the templates, etc.
00053   
00054   //-- Use Magnetic field at (0,0,0) to select a template ID [Morris, 6/25/08] (temporary until we implement DB access)
00055   
00056   DoCosmics_ = conf.getParameter<bool>("DoCosmics");
00057   LoadTemplatesFromDB_ = conf.getParameter<bool>("LoadTemplatesFromDB");
00058   //cout << "(int)LoadTemplatesFromDB_ = " << (int)LoadTemplatesFromDB_ << endl;
00059   //cout << "field_magnitude = " << field_magnitude << endl;
00060   
00061   // ggiurgiu@fnal.gov, 12/17/2008: use configuration parameter to decide between DB or text file template access
00062   if ( LoadTemplatesFromDB_ )
00063     {
00064       // Initialize template store to the selected ID [Morris, 6/25/08]  
00065       if ( !templ_.pushfile( *templateDBobject_) )
00066         throw cms::Exception("PixelCPETemplateReco") 
00067           << "\nERROR: Templates not filled correctly. Check the sqlite file. Using SiPixelTemplateDBObject version " 
00068           << (*templateDBobject_).version() << "\n\n";
00069     }
00070   else 
00071     {
00072       if ( !templ_.pushfile( templID_ ) )
00073         throw cms::Exception("PixelCPETemplateReco") 
00074           << "\nERROR: Templates not loaded correctly from text file. Reconstruction will fail.\n\n";
00075     }
00076   
00077   speed_ = conf.getParameter<int>( "speed");
00078   LogDebug("PixelCPETemplateReco::PixelCPETemplateReco:") <<
00079     "Template speed = " << speed_ << "\n";
00080   
00081   UseClusterSplitter_ = conf.getParameter<bool>("UseClusterSplitter");
00082   
00083 }
00084 
00085 //-----------------------------------------------------------------------------
00086 //  Clean up.
00087 //-----------------------------------------------------------------------------
00088 PixelCPETemplateReco::~PixelCPETemplateReco()
00089 {
00090   // &&& delete template store?
00091 }
00092 
00093 
00094 MeasurementPoint 
00095 PixelCPETemplateReco::measurementPosition(const SiPixelCluster& cluster, 
00096                                           const GeomDetUnit & det) const
00097 {
00098   LocalPoint lp = localPosition(cluster,det);
00099  
00100   // ggiurgiu@jhu.edu 12/09/2010 : trk angles needed to correct for bows/kinks
00101   if ( with_track_angle )
00102     return theTopol->measurementPosition(lp, Topology::LocalTrackAngles( loc_traj_param_.dxdz(), loc_traj_param_.dydz() ) );
00103   else
00104     {
00105       edm::LogError("PixelCPETemplateReco") 
00106         << "@SUB = PixelCPETemplateReco::measurementPosition" 
00107         << "Should never be here. PixelCPETemplateReco should always be called with track angles. This is a bad error !!! ";
00108       
00109       return theTopol->measurementPosition( lp ); 
00110     }
00111 }
00112 
00113 
00114 //------------------------------------------------------------------
00115 //  Public methods mandated by the base class.
00116 //------------------------------------------------------------------
00117 
00118 //------------------------------------------------------------------
00119 //  The main call to the template code.
00120 //------------------------------------------------------------------
00121 LocalPoint
00122 PixelCPETemplateReco::localPosition(const SiPixelCluster& cluster, const GeomDetUnit & det) const 
00123 {
00124   setTheDet( det, cluster );
00125   templID_ = templateDBobject_->getTemplateID(theDet->geographicalId());
00126   
00127   //int ierr;   //!< return status
00128   int ID = templID_; 
00129 
00130 
00131   bool fpix;  
00132   if ( thePart == GeomDetEnumerators::PixelBarrel )   
00133     fpix = false;    // no, it's not forward -- it's barrel
00134   else                                              
00135     fpix = true;     // yes, it's forward
00136   
00137   // Make from cluster (a SiPixelCluster) a boost multi_array_2d called 
00138   // clust_array_2d.
00139   boost::multi_array<float, 2> clust_array_2d(boost::extents[cluster_matrix_size_x][cluster_matrix_size_y]);
00140   
00141   // Preparing to retrieve ADC counts from the SiPixelCluster.  In the cluster,
00142   // we have the following:
00143   //   int minPixelRow(); // Minimum pixel index in the x direction (low edge).
00144   //   int maxPixelRow(); // Maximum pixel index in the x direction (top edge).
00145   //   int minPixelCol(); // Minimum pixel index in the y direction (left edge).
00146   //   int maxPixelCol(); // Maximum pixel index in the y direction (right edge).
00147   // So the pixels from minPixelRow() will go into clust_array_2d[0][*],
00148   // and the pixels from minPixelCol() will go into clust_array_2d[*][0].
00149   int row_offset = cluster.minPixelRow();
00150   int col_offset = cluster.minPixelCol();
00151   
00152   // Store the coordinates of the center of the (0,0) pixel of the array that 
00153   // gets passed to PixelTempReco2D
00154   // Will add these values to the output of  PixelTempReco2D
00155   float tmp_x = float(cluster.minPixelRow()) + 0.5;
00156   float tmp_y = float(cluster.minPixelCol()) + 0.5;
00157   
00158   // Store these offsets (to be added later) in a LocalPoint after tranforming 
00159   // them from measurement units (pixel units) to local coordinates (cm)
00160   
00161   // ggiurgiu@jhu.edu 12/09/2010 : update call with trk angles needed for bow/kink corrections
00162   LocalPoint lp;
00163   
00164   if ( with_track_angle )
00165     lp = theTopol->localPosition( MeasurementPoint(tmp_x, tmp_y), loc_trk_pred_ );
00166   else
00167     {
00168       edm::LogError("PixelCPETemplateReco") 
00169         << "@SUB = PixelCPETemplateReco::localPosition" 
00170         << "Should never be here. PixelCPETemplateReco should always be called with track angles. This is a bad error !!! ";
00171       
00172       lp = theTopol->localPosition( MeasurementPoint(tmp_x, tmp_y) );
00173     }
00174 
00175   const std::vector<SiPixelCluster::Pixel> & pixVec = cluster.pixels();
00176   std::vector<SiPixelCluster::Pixel>::const_iterator 
00177     pixIter = pixVec.begin(), pixEnd = pixVec.end();
00178   
00179   // Visualize large clusters ---------------------------------------------------------
00180   // From Petar: maybe this should be moved into a method in the base class?
00181   /*
00182     char cluster_matrix[100][100];
00183     for (int i=0; i<100; i++)
00184     for (int j=0; j<100; j++)
00185     cluster_matrix[i][j] = '.';
00186     
00187     if ( cluster.sizeX()>cluster_matrix_size_x || cluster.sizeY()>cluster_matrix_size_y )
00188     {           
00189     cout << "cluster.size()  = " << cluster.size()  << endl;
00190     cout << "cluster.sizeX() = " << cluster.sizeX() << endl;
00191     cout << "cluster.sizeY() = " << cluster.sizeY() << endl;
00192     
00193     for ( std::vector<SiPixelCluster::Pixel>::const_iterator pix = pixVec.begin(); pix != pixVec.end(); ++pix )
00194     {
00195     int i = (int)(pix->x) - row_offset;
00196     int j = (int)(pix->y) - col_offset;
00197     cluster_matrix[i][j] = '*';
00198     }
00199     
00200     for (int i=0; i<(int)cluster.sizeX()+2; i++)
00201     {
00202     for (int j=0; j<(int)cluster.sizeY()+2; j++)
00203     cout << cluster_matrix[i][j];
00204     cout << endl;
00205     }
00206     } // if ( cluster.sizeX()>cluster_matrix_size_x || cluster.sizeY()>cluster_matrix_size_y )
00207   */
00208   // End Visualize clusters ---------------------------------------------------------
00209   
00210 
00211   // Copy clust's pixels (calibrated in electrons) into clust_array_2d;
00212   for ( ; pixIter != pixEnd; ++pixIter ) 
00213     {
00214       // *pixIter dereferences to Pixel struct, with public vars x, y, adc (all float)
00215       // 02/13/2008 ggiurgiu@fnal.gov: type of x, y and adc has been changed to unsigned char, unsigned short, unsigned short
00216       // in DataFormats/SiPixelCluster/interface/SiPixelCluster.h so the type cast to int is redundant. Leave it there, it 
00217       // won't hurt. 
00218       int irow = int(pixIter->x) - row_offset;   // &&& do we need +0.5 ???
00219       int icol = int(pixIter->y) - col_offset;   // &&& do we need +0.5 ???
00220       
00221       // ggiurgiu@jhu.edu : what do we do here if the row/column is larger than cluster_matrix_size_x/cluster_matrix_size_y = 7/21 ?
00222       // Ignore them for the moment...
00223       if ( irow<cluster_matrix_size_x && icol<cluster_matrix_size_y )
00224         // 02/13/2008 ggiurgiu@fnal.gov typecast pixIter->adc to float
00225         clust_array_2d[irow][icol] = (float)pixIter->adc;
00226     }
00227   
00228   // Make and fill the bool arrays flagging double pixels
00229   std::vector<bool> ydouble(cluster_matrix_size_y), xdouble(cluster_matrix_size_x);
00230   // x directions (shorter), rows
00231   for (int irow = 0; irow < cluster_matrix_size_x; ++irow)
00232     {
00233       xdouble[irow] = theTopol->isItBigPixelInX( irow+row_offset );
00234     }
00235       
00236   // y directions (longer), columns
00237   for (int icol = 0; icol < cluster_matrix_size_y; ++icol) 
00238     {
00239       ydouble[icol] = theTopol->isItBigPixelInY( icol+col_offset );
00240     }
00241 
00242   // Output:
00243   float nonsense = -99999.9; // nonsense init value
00244   templXrec_ = templYrec_ = templSigmaX_ = templSigmaY_ = nonsense;
00245         // If the template recontruction fails, we want to return 1.0 for now
00246         templProbY_ = templProbX_ = templProbQ_ = 1.0;
00247         templQbin_ = 0;
00248         // We have a boolean denoting whether the reco failed or not
00249         hasFilledProb_ = false;
00250         
00251   float templYrec1_ = nonsense;
00252   float templXrec1_ = nonsense;
00253   float templYrec2_ = nonsense;
00254   float templXrec2_ = nonsense;
00255 
00256   // ******************************************************************
00257   // Do it! Use cotalpha_ and cotbeta_ calculated in PixelCPEBase
00258 
00259   GlobalVector bfield = magfield_->inTesla( theDet->surface().position() ); 
00260   
00261   Frame detFrame( theDet->surface().position(), theDet->surface().rotation() );
00262   LocalVector Bfield = detFrame.toLocal( bfield );
00263   float locBz = Bfield.z();
00264     
00265   ierr =
00266     PixelTempReco2D( ID, cotalpha_, cotbeta_,
00267                      locBz, 
00268                      clust_array_2d, ydouble, xdouble,
00269                      templ_,
00270                      templYrec_, templSigmaY_, templProbY_,
00271                      templXrec_, templSigmaX_, templProbX_, 
00272                      templQbin_, 
00273                      speed_,
00274                      templProbQ_
00275                      );
00276 
00277   // ******************************************************************
00278 
00279   // Check exit status
00280   if ( ierr != 0 ) 
00281     {
00282       LogDebug("PixelCPETemplateReco::localPosition") <<
00283         "reconstruction failed with error " << ierr << "\n";
00284 
00285       // ggiurgiu@jhu.edu: what do we do in this case ? For now, just return the cluster center of gravity in microns
00286       // In the x case, apply a rough Lorentz drift average correction
00287       // To do: call PixelCPEGeneric whenever PixelTempReco2D fails
00288       double lorentz_drift = -999.9;
00289       if ( thePart == GeomDetEnumerators::PixelBarrel )
00290         lorentz_drift = 60.0; // in microns
00291       else if ( thePart == GeomDetEnumerators::PixelEndcap )
00292         lorentz_drift = 10.0; // in microns
00293       else 
00294         throw cms::Exception("PixelCPETemplateReco::localPosition :") 
00295           << "A non-pixel detector type in here?" << "\n";    
00296       
00297       // ggiurgiu@jhu.edu, 21/09/2010 : trk angles needed to correct for bows/kinks
00298       if ( with_track_angle )
00299         {
00300           templXrec_ = theTopol->localX( cluster.x(), loc_trk_pred_ ) - lorentz_drift * micronsToCm; // rough Lorentz drift correction
00301           templYrec_ = theTopol->localY( cluster.y(), loc_trk_pred_ ); 
00302         }
00303       else
00304         {
00305           edm::LogError("PixelCPETemplateReco") 
00306             << "@SUB = PixelCPETemplateReco::localPosition" 
00307             << "Should never be here. PixelCPETemplateReco should always be called with track angles. This is a bad error !!! ";
00308           
00309           templXrec_ = theTopol->localX( cluster.x() ) - lorentz_drift * micronsToCm; // rough Lorentz drift correction
00310           templYrec_ = theTopol->localY( cluster.y() );
00311         }
00312 
00313     }
00314   else if ( UseClusterSplitter_ && templQbin_ == 0 )
00315     {
00316       ierr = 
00317         PixelTempSplit( ID, fpix, cotalpha_, cotbeta_, 
00318                         clust_array_2d, ydouble, xdouble, 
00319                         templ_, 
00320                         templYrec1_, templYrec2_, templSigmaY_, templProbY_,
00321                         templXrec1_, templXrec2_, templSigmaX_, templProbX_, 
00322                         templQbin_ );
00323 
00324       if ( ierr != 0 )
00325         {
00326           LogDebug("PixelCPETemplateReco::localPosition") <<
00327             "reconstruction failed with error " << ierr << "\n";
00328           
00329           // ggiurgiu@jhu.edu: what do we do in this case ? For now, just return the cluster center of gravity in microns
00330           // In the x case, apply a rough Lorentz drift average correction
00331           // To do: call PixelCPEGeneric whenever PixelTempReco2D fails
00332           double lorentz_drift = -999.9;
00333           if ( thePart == GeomDetEnumerators::PixelBarrel )
00334             lorentz_drift = 60.0; // in microns
00335           else if ( thePart == GeomDetEnumerators::PixelEndcap )
00336             lorentz_drift = 10.0; // in microns
00337           else 
00338             throw cms::Exception("PixelCPETemplateReco::localPosition :") 
00339               << "A non-pixel detector type in here?" << "\n";
00340 
00341           // ggiurgiu@jhu.edu, 12/09/2010 : trk angles needed to correct for bows/kinks
00342           if ( with_track_angle )
00343             {
00344               templXrec_ = theTopol->localX( cluster.x(),loc_trk_pred_ ) - lorentz_drift * micronsToCm; // rough Lorentz drift correction
00345               templYrec_ = theTopol->localY( cluster.y(),loc_trk_pred_ );
00346             }
00347           else
00348             {
00349               edm::LogError("PixelCPETemplateReco") 
00350                 << "@SUB = PixelCPETemplateReco::localPosition" 
00351                 << "Should never be here. PixelCPETemplateReco should always be called with track angles. This is a bad error !!! ";
00352               
00353               templXrec_ = theTopol->localX( cluster.x() ) - lorentz_drift * micronsToCm; // very rough Lorentz drift correction
00354               templYrec_ = theTopol->localY( cluster.y() );
00355 
00356             }
00357         }
00358       else
00359         {
00360           // go from micrometer to centimeter      
00361           templXrec1_ *= micronsToCm;
00362           templYrec1_ *= micronsToCm;     
00363           templXrec2_ *= micronsToCm;
00364           templYrec2_ *= micronsToCm;
00365       
00366           // go back to the module coordinate system   
00367           templXrec1_ += lp.x();
00368           templYrec1_ += lp.y();
00369           templXrec2_ += lp.x();
00370           templYrec2_ += lp.y();
00371       
00372           // calculate distance from each hit to the track and choose the 
00373           // hit closest to the track
00374           float distance11 = sqrt( (templXrec1_ - trk_lp_x)*(templXrec1_ - trk_lp_x) + 
00375                                    (templYrec1_ - trk_lp_y)*(templYrec1_ - trk_lp_y) );
00376           
00377           float distance12 = sqrt( (templXrec1_ - trk_lp_x)*(templXrec1_ - trk_lp_x) + 
00378                                    (templYrec2_ - trk_lp_y)*(templYrec2_ - trk_lp_y) );
00379           
00380           float distance21 = sqrt( (templXrec2_ - trk_lp_x)*(templXrec2_ - trk_lp_x) + 
00381                                    (templYrec1_ - trk_lp_y)*(templYrec1_ - trk_lp_y) );
00382           
00383           float distance22 = sqrt( (templXrec2_ - trk_lp_x)*(templXrec2_ - trk_lp_x) + 
00384                                    (templYrec2_ - trk_lp_y)*(templYrec2_ - trk_lp_y) );
00385           
00386           int index_dist = -999;
00387           float min_templXrec_ = -999.9;
00388           float min_templYrec_ = -999.9;
00389           float distance_min = 9999999999.9;
00390           if ( distance11 < distance_min )
00391             {
00392               distance_min = distance11;
00393               min_templXrec_ = templXrec1_;
00394               min_templYrec_ = templYrec1_;
00395               index_dist = 1;
00396             }
00397           if ( distance12 < distance_min )
00398             {
00399               distance_min = distance12;
00400               min_templXrec_ = templXrec1_;
00401               min_templYrec_ = templYrec2_;
00402               index_dist = 2;
00403             }
00404           if ( distance21 < distance_min )
00405             {
00406               distance_min = distance21;
00407               min_templXrec_ = templXrec2_;
00408               min_templYrec_ = templYrec1_;
00409               index_dist = 3;
00410             }
00411           if ( distance22 < distance_min )
00412             {
00413               distance_min = distance22;
00414               min_templXrec_ = templXrec2_;
00415               min_templYrec_ = templYrec2_;
00416               index_dist = 4;
00417             }
00418                   
00419           templXrec_ = min_templXrec_;
00420           templYrec_ = min_templYrec_;
00421         }
00422     } // else if ( UseClusterSplitter_ && templQbin_ == 0 )
00423   else 
00424     {
00425       // go from micrometer to centimeter      
00426       templXrec_ *= micronsToCm;
00427       templYrec_ *= micronsToCm;
00428       
00429       // go back to the module coordinate system 
00430       templXrec_ += lp.x();
00431       templYrec_ += lp.y();
00432     }
00433     
00434   // Save probabilities and qBin in the quantities given to us by the base class
00435   // (for which there are also inline getters).  &&& templProbX_ etc. should be retired...
00436   probabilityX_  = templProbX_;
00437   probabilityY_  = templProbY_;
00438   probabilityQ_  = templProbQ_;
00439   qBin_          = templQbin_;
00440         if(ierr==0) hasFilledProb_ = true;
00441         
00442   LocalPoint template_lp = LocalPoint( nonsense, nonsense );
00443   template_lp = LocalPoint( templXrec_, templYrec_ );      
00444   
00445   return template_lp;
00446 
00447 }
00448   
00449 //------------------------------------------------------------------
00450 //  localError() relies on localPosition() being called FIRST!!!
00451 //------------------------------------------------------------------
00452 LocalError  
00453 PixelCPETemplateReco::localError( const SiPixelCluster& cluster, 
00454                                   const GeomDetUnit& det ) const 
00455 {
00456   setTheDet( det, cluster );
00457   
00458   //--- Default is the maximum error used for edge clusters.
00459   float xerr = thePitchX / sqrt(12.0);
00460   float yerr = thePitchY / sqrt(12.0);
00461   
00462   int maxPixelCol = cluster.maxPixelCol();
00463   int maxPixelRow = cluster.maxPixelRow();
00464   int minPixelCol = cluster.minPixelCol();
00465   int minPixelRow = cluster.minPixelRow();
00466   
00467   //--- Are we near either of the edges?
00468   bool edgex = ( theTopol->isItEdgePixelInX( minPixelRow ) || theTopol->isItEdgePixelInX( maxPixelRow ) );
00469   bool edgey = ( theTopol->isItEdgePixelInY( minPixelCol ) || theTopol->isItEdgePixelInY( maxPixelCol ) );
00470   
00471   if ( ierr !=0 ) 
00472     {
00473       // If reconstruction fails the hit position is calculated from cluster center of gravity 
00474       // corrected in x by average Lorentz drift. Assign huge errors.
00475       //xerr = 10.0 * (float)cluster.sizeX() * xerr;
00476       //yerr = 10.0 * (float)cluster.sizeX() * yerr;
00477 
00478       // Assign better errors based on the residuals for failed template cases
00479       if ( thePart == GeomDetEnumerators::PixelBarrel )
00480         {
00481           xerr = 55.0 * micronsToCm;
00482           yerr = 36.0 * micronsToCm;
00483         }
00484       else if ( thePart == GeomDetEnumerators::PixelEndcap )
00485         {
00486           xerr = 42.0 * micronsToCm;
00487           yerr = 39.0 * micronsToCm;
00488         }
00489       else 
00490         throw cms::Exception("PixelCPETemplateReco::localError :") << "A non-pixel detector type in here?" ;
00491 
00492 
00493       return LocalError(xerr*xerr, 0, yerr*yerr);
00494     }
00495   else if ( edgex || edgey )
00496     {
00497       // for edge pixels assign errors according to observed residual RMS 
00498       if      ( edgex && !edgey )
00499         {
00500           xerr = 23.0 * micronsToCm;
00501           yerr = 39.0 * micronsToCm;
00502         }
00503       else if ( !edgex && edgey )
00504         {
00505           xerr = 24.0 * micronsToCm;
00506           yerr = 96.0 * micronsToCm;
00507         }
00508       else if ( edgex && edgey )
00509         {
00510           xerr = 31.0 * micronsToCm;
00511           yerr = 90.0 * micronsToCm;
00512         }
00513       else
00514         {
00515           throw cms::Exception(" PixelCPETemplateReco::localError: Something wrong with pixel edge flag !!!");
00516         }
00517     }
00518   else 
00519     {
00520       // &&& need a class const
00521       const float micronsToCm = 1.0e-4;
00522 
00523       xerr = templSigmaX_ * micronsToCm;
00524       yerr = templSigmaY_ * micronsToCm;
00525       
00526       // &&& should also check ierr (saved as class variable) and return
00527       // &&& nonsense (another class static) if the template fit failed.
00528     }       
00529   
00530   if (theVerboseLevel > 9) 
00531     {
00532       LogDebug("PixelCPETemplateReco") <<
00533         " Sizex = " << cluster.sizeX() << " Sizey = " << cluster.sizeY() <<     " Edgex = " << edgex           << " Edgey = " << edgey << 
00534         " ErrX  = " << xerr            << " ErrY  = " << yerr;
00535     }
00536   
00537   return LocalError(xerr*xerr, 0, yerr*yerr);
00538 }
00539