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