1 // -*- C++ -*- 2 // 3 // This file is part of YODA -- Yet more Objects for Data Analysis 4 // Copyright (C) 2008-2021 The YODA collaboration (see AUTHORS for details) 5 // 6 #ifndef YODA_POINT2D_H 7 #define YODA_POINT2D_H 8 9 #include "YODA/Point.h" 10 #include "YODA/Exceptions.h" 11 #include "YODA/Utils/MathUtils.h" 12 #include <utility> 13 14 15 namespace YODA { 16 17 18 /// A 2D data point to be contained in a Scatter2D 19 class Point2D : public Point { 20 public: 21 22 /// @name Constructors 23 /// @{ 24 25 // Default constructor Point2D()26 Point2D() { } 27 28 29 /// Constructor from values with optional symmetric errors 30 Point2D(double x, double y, double ex=0.0, double ey=0.0, std::string source="") _x(x)31 : _x(x), _y(y) 32 { 33 _ex = std::make_pair(ex, ex); 34 _ey[source] = std::make_pair(ey, ey); 35 } 36 37 38 /// Constructor from values with explicit asymmetric errors 39 Point2D(double x, double y, 40 double exminus, 41 double explus, 42 double eyminus, 43 double eyplus, std::string source="") _x(x)44 : _x(x), _y(y) 45 { 46 _ex = std::make_pair(exminus, explus); 47 _ey[source] = std::make_pair(eyminus, eyplus); 48 } 49 50 51 // /// Constructor from values with symmetric errors on x and asymmetric errors on y 52 // Point2D(double x, double y, double ex, const std::pair<double,double>& ey) 53 // : _x(x), _y(y), _ey(ey) 54 // { 55 // _ex = std::make_pair(ex, ex); 56 // } 57 58 59 // /// Constructor from values with asymmetric errors on x and symmetric errors on y 60 // Point2D(double x, double y, const std::pair<double,double>& ex, double ey) 61 // : _x(x), _y(y), _ex(ex) 62 // { 63 // _ey = std::make_pair(ey, ey); 64 // } 65 66 67 /// Constructor from values with asymmetric errors on both x and y 68 Point2D(double x, double y, const std::pair<double,double>& ex, const std::pair<double,double>& ey, std::string source="") _x(x)69 : _x(x), _y(y) 70 { 71 _ex = ex; 72 _ey[source] = ey; 73 } 74 75 76 /// Copy constructor Point2D(const Point2D & p)77 Point2D(const Point2D& p) 78 : _x(p._x), _y(p._y) 79 { 80 _ex = p._ex; 81 _ey = p._ey; 82 this->setParent( p.getParent() ); 83 } 84 85 86 /// Copy assignment 87 Point2D& operator = (const Point2D& p) { 88 _x = p._x; 89 _y = p._y; 90 _ex = p._ex; 91 _ey = p._ey; 92 this->setParent( p.getParent() ); 93 return *this; 94 } 95 96 /// @} 97 98 99 public: 100 101 /// Space dimension of the point dim()102 size_t dim() { return 2; } 103 104 105 /// @name Value accessors 106 /// @{ 107 108 /// Get x value x()109 double x() const { return _x; } 110 111 /// Set x value setX(double x)112 void setX(double x) { _x = x; } 113 114 /// Get y value y()115 double y() const { return _y; } 116 117 /// Set y value setY(double y)118 void setY(double y) { _y = y; } 119 120 /// @todo Uniform "coords" accessor across all Scatters: returning fixed-size tuple? 121 122 /// Get x,y value pair xy()123 std::pair<double,double> xy() const { return std::make_pair(_x, _y); } 124 125 /// Set x and y values setXY(double x,double y)126 void setXY(double x, double y) { setX(x); setY(y); } 127 128 /// Set x and y values setXY(const std::pair<double,double> & xy)129 void setXY(const std::pair<double,double>& xy) { setX(xy.first); setY(xy.second); } 130 131 /// @} 132 133 134 /// @name x error accessors 135 /// @{ 136 137 138 /// Get x-error values xErrs()139 const std::pair<double,double>& xErrs() const { 140 return _ex; 141 } 142 143 /// Get negative x-error value xErrMinus()144 double xErrMinus() const { 145 return _ex.first; 146 } 147 148 /// Get positive x-error value xErrPlus()149 double xErrPlus() const { 150 return _ex.second; 151 152 } 153 154 /// Get average x-error value xErrAvg()155 double xErrAvg() const { 156 return (_ex.first + _ex.second)/2.0; 157 } 158 159 /// Set negative x error setXErrMinus(double exminus)160 void setXErrMinus(double exminus) { 161 _ex.first = exminus; 162 } 163 164 /// Set positive x error setXErrPlus(double explus)165 void setXErrPlus(double explus) { 166 _ex.second = explus; 167 } 168 169 /// Set symmetric x error setXErr(double ex)170 void setXErr(double ex) { 171 setXErrMinus(ex); 172 setXErrPlus(ex); 173 } 174 175 /// Set symmetric x error (alias) setXErrs(double ex)176 void setXErrs(double ex) { 177 setXErr(ex); 178 } 179 180 /// Set asymmetric x error setXErrs(double exminus,double explus)181 void setXErrs(double exminus, double explus) { 182 setXErrMinus(exminus); 183 setXErrPlus(explus); 184 } 185 186 /// Set asymmetric x error setXErrs(const std::pair<double,double> & ex)187 void setXErrs(const std::pair<double,double>& ex) { 188 _ex = ex; 189 } 190 191 /// Get value minus negative x-error 192 /// @todo Remove (or extend) when multiple errors are supported 193 /// No: doesn't need to change since (for now) we only store multiple 194 /// errors for the highest dimentsion xMin()195 double xMin() const { 196 return _x - _ex.first; 197 } 198 199 /// Get value plus positive x-error 200 /// @todo Remove (or extend) when multiple errors are supported 201 /// No: doesn't need to change since (for now) we only store multiple 202 /// errors for the highest dimentsion xMax()203 double xMax() const { 204 return _x + _ex.second; 205 } 206 207 /// @} 208 209 210 /// @name y error accessors 211 /// @{ 212 213 /// Get y-error values 214 const std::pair<double,double>& yErrs(std::string source="") const { 215 if (source!="") getVariationsFromParent(); 216 if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); 217 return _ey.at(source); 218 } 219 220 /// Get negative y-error value 221 double yErrMinus(std::string source="") const { 222 if (source!="") getVariationsFromParent(); 223 if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); 224 return _ey.at(source).first; 225 } 226 227 /// Get positive y-error value 228 double yErrPlus(std::string source="") const { 229 if (source!="") getVariationsFromParent(); 230 if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); 231 return _ey.at(source).second; 232 } 233 234 /// Get average y-error value 235 double yErrAvg(std::string source="") const { 236 if (source!="") getVariationsFromParent(); 237 if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); 238 double res=(fabs(_ey.at(source).first) + fabs(_ey.at(source).second))/2.; 239 return res; 240 } 241 242 /// Set negative y error 243 void setYErrMinus(double eyminus, std::string source="") { 244 if (!_ey.count(source)) { 245 _ey[source] = std::make_pair(0.,0.); 246 } 247 _ey.at(source).first = eyminus; 248 } 249 250 /// Set positive y error 251 void setYErrPlus(double eyplus, std::string source="") { 252 if (!_ey.count(source)) _ey[source] = std::make_pair(0.,0.); 253 _ey.at(source).second = eyplus; 254 } 255 256 /// Set symmetric y error 257 void setYErr(double ey, std::string source="") { 258 setYErrMinus(ey, source ); 259 setYErrPlus(ey, source ); 260 } 261 262 /// Set symmetric y error (alias) 263 void setYErrs(double ey, std::string source="") { 264 setYErr(ey, source); 265 } 266 267 /// Set asymmetric y error 268 void setYErrs(double eyminus, double eyplus, std::string source="") { 269 setYErrMinus(eyminus, source); 270 setYErrPlus(eyplus, source ); 271 } 272 273 /// Set asymmetric y error 274 void setYErrs(const std::pair<double,double>& ey, std::string source="") { 275 _ey[source] = ey; 276 } 277 278 /// Get value minus negative y-error 279 double yMin(std::string source="") const { 280 if (source!="") getVariationsFromParent(); 281 if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); 282 return _y - _ey.at(source).first; 283 } 284 285 /// Get value plus positive y-error 286 double yMax(std::string source="") const { 287 if (source!="") getVariationsFromParent(); 288 if (!_ey.count(source)) throw RangeError("yErrs has no such key: "+source); 289 return _y + _ey.at(source).second; 290 } 291 292 /// @} 293 294 295 296 297 /// @name Combined x/y value and error setters 298 /// @{ 299 300 /// Set x value and symmetric error setX(double x,double ex)301 void setX(double x, double ex) { 302 setX(x); 303 setXErrs(ex); 304 } 305 306 /// Set x value and asymmetric error setX(double x,double exminus,double explus)307 void setX(double x, double exminus, double explus) { 308 setX(x); 309 setXErrs(exminus, explus); 310 } 311 312 /// Set x value and asymmetric error setX(double x,std::pair<double,double> & ex)313 void setX(double x, std::pair<double,double>& ex) { 314 setX(x); 315 setXErrs(ex); 316 } 317 318 319 /// Set y value and symmetric error 320 void setY(double y, double ey, std::string source="") { 321 setY(y); 322 setYErrs(ey, source); 323 } 324 325 /// Set y value and asymmetric error 326 void setY(double y, double eyminus, double eyplus, std::string source="") { 327 setY(y); 328 setYErrs(eyminus, eyplus, source); 329 } 330 331 /// Set y value and asymmetric error 332 void setY(double y, std::pair<double,double>& ey, std::string source="") { 333 setY(y); 334 setYErrs(ey, source); 335 } 336 337 /// @} 338 339 340 // @name Manipulations 341 /// @{ 342 343 /// Scaling of x axis scaleX(double scalex)344 void scaleX(double scalex) { 345 setX(x()*scalex); 346 setXErrs(xErrMinus()*scalex, xErrPlus()*scalex); 347 } 348 349 /// Scaling of y axis scaleY(double scaley)350 void scaleY(double scaley) { 351 setY(y()*scaley); 352 for (const auto &source : _ey){ 353 setYErrs(yErrMinus()*scaley, yErrPlus()*scaley, source.first); 354 } 355 } 356 357 /// Scaling of both axes scaleXY(double scalex,double scaley)358 void scaleXY(double scalex, double scaley) { 359 scaleX(scalex); 360 scaleY(scaley); 361 } 362 363 /// Scaling along direction @a i scale(size_t i,double scale)364 void scale(size_t i, double scale) { 365 switch (i) { 366 case 1: scaleX(scale); break; 367 case 2: scaleY(scale); break; 368 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 369 } 370 } 371 372 /// @} 373 374 375 /// @name Integer axis accessor equivalents 376 /// @{ 377 378 /// Get the point value for direction @a i val(size_t i)379 double val(size_t i) const { 380 switch (i) { 381 case 1: return x(); 382 case 2: return y(); 383 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 384 } 385 } 386 /// Set the point value for direction @a i setVal(size_t i,double val)387 void setVal(size_t i, double val) { 388 switch (i) { 389 case 1: setX(val); break; 390 case 2: setY(val); break; 391 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 392 } 393 } 394 395 /// Get error map for direction @a i 396 const std::map< std::string, std::pair<double,double>> & errMap() const; 397 398 // Parse the variations from the parent AO if it exists 399 void getVariationsFromParent() const; 400 401 // set the "" error source to the sum in quad of the existing variations updateTotalUncertainty()402 void updateTotalUncertainty() { 403 float totalUp = 0.; 404 float totalDn = 0.; 405 for (const auto& variation : getParent()->variations()) { 406 if (variation=="") continue; 407 float thisUp = yErrPlus(variation); 408 float thisDn = yErrMinus(variation); 409 totalUp += thisUp*thisUp; 410 totalDn += thisDn*thisDn; 411 } 412 413 totalUp = sqrt(totalUp); 414 totalDn = sqrt(totalDn); 415 setErrPlus(2, totalUp); 416 setErrMinus(2, totalDn); 417 } 418 419 /// Get error values for direction @a i 420 const std::pair<double,double>& errs(size_t i, std::string source="") const { 421 switch (i) { 422 case 1: return xErrs(); 423 case 2: return yErrs(source); 424 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 425 } 426 } 427 /// Get negative error value for direction @a i 428 double errMinus(size_t i, std::string source="") const { 429 switch (i) { 430 case 1: return xErrMinus(); 431 case 2: return yErrMinus(source); 432 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 433 } 434 } 435 /// Get positive error value for direction @a i 436 double errPlus(size_t i, std::string source="") const { 437 switch (i) { 438 case 1: return xErrPlus(); 439 case 2: return yErrPlus(source); 440 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 441 } 442 } 443 /// Get average error value for direction @a i 444 double errAvg(size_t i, std::string source="") const { 445 switch (i) { 446 case 1: return xErrAvg(); 447 case 2: return yErrAvg(source); 448 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 449 } 450 } 451 452 /// Set negative error for direction @a i 453 void setErrMinus(size_t i, double eminus, std::string source="") { 454 switch (i) { 455 case 1: setXErrMinus(eminus); break; 456 case 2: setYErrMinus(eminus, source); break; 457 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 458 } 459 } 460 /// Set positive error for direction @a i 461 void setErrPlus(size_t i, double eplus, std::string source="") { 462 switch (i) { 463 case 1: setXErrPlus(eplus); break; 464 case 2: setYErrPlus(eplus, source); break; 465 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 466 } 467 } 468 469 /// Set symmetric error for direction @a i 470 void setErr(size_t i, double e, std::string source="") { 471 switch (i) { 472 case 1: setXErrs(e); break; 473 case 2: setYErrs(e, source); break; 474 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 475 } 476 } 477 /// Set asymmetric error for direction @a i 478 void setErrs(size_t i, double eminus, double eplus, std::string source="") { 479 switch (i) { 480 case 1: setXErrs(eminus, eplus); break; 481 case 2: setYErrs(eminus, eplus, source); break; 482 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 483 } 484 } 485 /// Set asymmetric error for direction @a i 486 void setErrs(size_t i, std::pair<double,double>& e, std::string source="") { 487 switch (i) { 488 case 1: setXErrs(e); break; 489 case 2: setYErrs(e, source); break; 490 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 491 } 492 } 493 494 /// Set value and symmetric error for direction @a i 495 void set(size_t i, double val, double e, std::string source="") { 496 switch (i) { 497 case 1: setX(val, e); break; 498 case 2: setY(val, e, source); break; 499 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 500 } 501 } 502 /// Set value and asymmetric error for direction @a i 503 void set(size_t i, double val, double eminus, double eplus, std::string source="") { 504 switch (i) { 505 case 1: setX(val, eminus, eplus); break; 506 case 2: setY(val, eminus, eplus, source); break; 507 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 508 } 509 } 510 /// Set value and asymmetric error for direction @a i 511 void set(size_t i, double val, std::pair<double,double>& e, std::string source="") { 512 switch (i) { 513 case 1: setX(val, e); break; 514 case 2: setY(val, e, source); break; 515 default: throw RangeError("Invalid axis int, must be in range 1..dim"); 516 } 517 } 518 519 520 /// @} 521 522 523 protected: 524 525 /// @name Value and error variables 526 /// @{ 527 528 529 double _x; 530 double _y; 531 std::pair<double,double> _ex; 532 // a map of the errors for each source. Nominal stored under "" 533 // to ensure backward compatibility 534 std::map< std::string, std::pair<double,double> > _ey; 535 536 /// @} 537 538 }; 539 540 541 542 /// @name Comparison operators 543 /// @{ 544 545 /// Equality test of x & y characteristics only 546 /// @todo Base on a named fuzzyEquals(a,b,tol=1e-3) unbound function 547 inline bool operator==(const YODA::Point2D& a, const YODA::Point2D& b) { 548 if (!YODA::fuzzyEquals(a.x(), b.x()) || 549 !YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus()) || 550 !YODA::fuzzyEquals(a.xErrPlus(), b.xErrPlus()) ) return false; 551 if (!YODA::fuzzyEquals(a.y(), b.y()) || 552 !YODA::fuzzyEquals(a.yErrMinus(), b.yErrMinus()) || 553 !YODA::fuzzyEquals(a.yErrPlus(), b.yErrPlus()) ) return false; 554 return true; 555 } 556 557 /// Equality test of x characteristics only 558 inline bool operator != (const YODA::Point2D& a, const YODA::Point2D& b) { 559 return !(a == b); 560 } 561 562 /// Less-than operator used to sort bins by x-ordering 563 inline bool operator < (const YODA::Point2D& a, const YODA::Point2D& b) { 564 if (!YODA::fuzzyEquals(a.x(), b.x())) { 565 return a.x() < b.x(); 566 } 567 if (!YODA::fuzzyEquals(a.xErrMinus(), b.xErrMinus())) { 568 return a.xErrMinus() < b.xErrMinus(); 569 } 570 if (!YODA::fuzzyEquals(a.xErrPlus(), b.xErrPlus())) { 571 return a.xErrPlus() < b.xErrPlus(); 572 } 573 return false; 574 } 575 576 /// Less-than-or-equals operator used to sort bins by x-ordering 577 inline bool operator <= (const YODA::Point2D& a, const YODA::Point2D& b) { 578 if (a == b) return true; 579 return a < b; 580 } 581 582 /// Greater-than operator used to sort bins by x-ordering 583 inline bool operator > (const YODA::Point2D& a, const YODA::Point2D& b) { 584 return !(a <= b); 585 } 586 587 /// Greater-than-or-equals operator used to sort bins by x-ordering 588 inline bool operator >= (const YODA::Point2D& a, const YODA::Point2D& b) { 589 return !(a < b); 590 } 591 592 /// @} 593 594 595 } 596 597 #endif 598