File indexing completed on 2024-04-06 12:26:41
0001 #include "FWCore/Framework/interface/ConsumesCollector.h"
0002 #include "RecoMET/METAlgorithms/interface/HcalHaloAlgo.h"
0003 #include <map>
0004
0005
0006
0007
0008
0009
0010
0011 namespace {
0012 constexpr float c_cm_per_ns = 29.9792458;
0013 constexpr float zseparation_HBHE = 380.;
0014 };
0015
0016 using namespace reco;
0017
0018 #include <iomanip>
0019 bool CompareTime(const HBHERecHit* x, const HBHERecHit* y) { return x->time() < y->time(); }
0020 bool CompareTowers(const CaloTower* x, const CaloTower* y) {
0021 return x->iphi() * 1000 + x->ieta() < y->iphi() * 1000 + y->ieta();
0022 }
0023
0024 HcalHaloAlgo::HcalHaloAlgo(edm::ConsumesCollector iC) : geoToken_(iC.esConsumes()), geo_(nullptr), hgeo_(nullptr) {
0025 HBRecHitEnergyThreshold = 0.;
0026 HERecHitEnergyThreshold = 0.;
0027 SumEnergyThreshold = 0.;
0028 NHitsThreshold = 0;
0029 }
0030
0031 HcalHaloData HcalHaloAlgo::Calculate(const CaloGeometry& TheCaloGeometry,
0032 edm::Handle<HBHERecHitCollection>& TheHBHERecHits,
0033 edm::Handle<EBRecHitCollection>& TheEBRecHits,
0034 edm::Handle<EERecHitCollection>& TheEERecHits,
0035 const edm::EventSetup& TheSetup) {
0036 edm::Handle<CaloTowerCollection> TheCaloTowers;
0037 return Calculate(TheCaloGeometry, TheHBHERecHits, TheCaloTowers, TheEBRecHits, TheEERecHits, TheSetup);
0038 }
0039
0040 HcalHaloData HcalHaloAlgo::Calculate(const CaloGeometry& TheCaloGeometry,
0041 edm::Handle<HBHERecHitCollection>& TheHBHERecHits,
0042 edm::Handle<CaloTowerCollection>& TheCaloTowers,
0043 edm::Handle<EBRecHitCollection>& TheEBRecHits,
0044 edm::Handle<EERecHitCollection>& TheEERecHits,
0045 const edm::EventSetup& TheSetup) {
0046 HcalHaloData TheHcalHaloData;
0047
0048 const int iEtaOverlap = 22;
0049 const int nPhiMax = 73;
0050
0051 float SumE[nPhiMax];
0052
0053 int NumHits[nPhiMax];
0054
0055 float MinTimeHits[nPhiMax];
0056
0057 float MaxTimeHits[nPhiMax];
0058 for (unsigned int i = 0; i < nPhiMax; i++) {
0059 SumE[i] = 0;
0060 NumHits[i] = 0;
0061 MinTimeHits[i] = 0.;
0062 MaxTimeHits[i] = 0.;
0063 }
0064
0065 for (const auto& hit : (*TheHBHERecHits)) {
0066 HcalDetId id = HcalDetId(hit.id());
0067 switch (id.subdet()) {
0068 case HcalBarrel:
0069 if (hit.energy() < HBRecHitEnergyThreshold)
0070 continue;
0071 break;
0072 case HcalEndcap:
0073 if (hit.energy() < HERecHitEnergyThreshold)
0074 continue;
0075 break;
0076 default:
0077 continue;
0078 }
0079
0080 int iEta = id.ieta();
0081 int iPhi = id.iphi();
0082 if (iPhi < nPhiMax && std::abs(iEta) <= iEtaOverlap) {
0083 SumE[iPhi] += hit.energy();
0084 NumHits[iPhi]++;
0085
0086 float time = hit.time();
0087 MinTimeHits[iPhi] = time < MinTimeHits[iPhi] ? time : MinTimeHits[iPhi];
0088 MaxTimeHits[iPhi] = time > MaxTimeHits[iPhi] ? time : MaxTimeHits[iPhi];
0089 }
0090 }
0091
0092 for (int iPhi = 1; iPhi < nPhiMax; iPhi++) {
0093 if (SumE[iPhi] >= SumEnergyThreshold && NumHits[iPhi] > NHitsThreshold) {
0094
0095 PhiWedge wedge(SumE[iPhi], iPhi, NumHits[iPhi], MinTimeHits[iPhi], MaxTimeHits[iPhi]);
0096
0097
0098 std::vector<const HBHERecHit*> Hits;
0099 for (const auto& hit : (*TheHBHERecHits)) {
0100 HcalDetId id = HcalDetId(hit.id());
0101 if (id.iphi() != iPhi)
0102 continue;
0103 if (std::abs(id.ieta()) > iEtaOverlap)
0104 continue;
0105 switch (id.subdet()) {
0106 case HcalBarrel:
0107 if (hit.energy() < HBRecHitEnergyThreshold)
0108 continue;
0109 break;
0110 case HcalEndcap:
0111 if (hit.energy() < HERecHitEnergyThreshold)
0112 continue;
0113 break;
0114 default:
0115 continue;
0116 }
0117 Hits.push_back(&(hit));
0118 }
0119
0120 std::sort(Hits.begin(), Hits.end(), CompareTime);
0121 float MinusToPlus = 0.;
0122 float PlusToMinus = 0.;
0123 for (unsigned int i = 0; i < Hits.size(); i++) {
0124 HcalDetId id_i = HcalDetId(Hits[i]->id());
0125 int ieta_i = id_i.ieta();
0126 for (unsigned int j = (i + 1); j < Hits.size(); j++) {
0127 HcalDetId id_j = HcalDetId(Hits[j]->id());
0128 int ieta_j = id_j.ieta();
0129 if (ieta_i > ieta_j)
0130 PlusToMinus += std::abs(ieta_i - ieta_j);
0131 else
0132 MinusToPlus += std::abs(ieta_i - ieta_j);
0133 }
0134 }
0135 float PlusZOriginConfidence = (PlusToMinus + MinusToPlus) ? PlusToMinus / (PlusToMinus + MinusToPlus) : -1.;
0136 wedge.SetPlusZOriginConfidence(PlusZOriginConfidence);
0137 TheHcalHaloData.GetPhiWedges().push_back(wedge);
0138 }
0139 }
0140
0141
0142 int maxAbsIEta = 29;
0143
0144 std::map<int, float> iPhiHadEtMap;
0145 std::vector<const CaloTower*> sortedCaloTowers;
0146 for (const auto& tower : (*TheCaloTowers)) {
0147 if (std::abs(tower.ieta()) > maxAbsIEta)
0148 continue;
0149
0150 int iPhi = tower.iphi();
0151 if (!iPhiHadEtMap.count(iPhi))
0152 iPhiHadEtMap[iPhi] = 0.0;
0153 iPhiHadEtMap[iPhi] += tower.hadEt();
0154
0155 if (tower.numProblematicHcalCells() > 0)
0156 sortedCaloTowers.push_back(&(tower));
0157 }
0158
0159
0160
0161 std::sort(sortedCaloTowers.begin(), sortedCaloTowers.end(), CompareTowers);
0162
0163 HaloTowerStrip strip;
0164
0165 int prevIEta = -99, prevIPhi = -99;
0166 float prevHadEt = 0.;
0167 float prevEmEt = 0.;
0168 std::pair<uint8_t, CaloTowerDetId> prevPair, towerPair;
0169 bool wasContiguous = true;
0170
0171
0172 for (unsigned int i = 0; i < sortedCaloTowers.size(); i++) {
0173 const CaloTower* tower = sortedCaloTowers[i];
0174
0175 towerPair = std::make_pair((uint8_t)tower->numProblematicHcalCells(), tower->id());
0176
0177 bool newIPhi = tower->iphi() != prevIPhi;
0178 bool isContiguous = tower->ieta() == 1 ? tower->ieta() - 2 == prevIEta : tower->ieta() - 1 == prevIEta;
0179
0180 isContiguous = isContiguous || (tower->ieta() == -maxAbsIEta);
0181 if (newIPhi)
0182 isContiguous = false;
0183
0184 if (!wasContiguous && isContiguous) {
0185 strip.cellTowerIds.push_back(prevPair);
0186 strip.cellTowerIds.push_back(towerPair);
0187 strip.hadEt += prevHadEt + tower->hadEt();
0188 strip.emEt += prevEmEt + tower->emEt();
0189 }
0190
0191 if (wasContiguous && isContiguous) {
0192 strip.cellTowerIds.push_back(towerPair);
0193 strip.hadEt += tower->hadEt();
0194 strip.emEt += tower->emEt();
0195 }
0196
0197 if ((wasContiguous && !isContiguous) || i == sortedCaloTowers.size() - 1) {
0198
0199 if (strip.cellTowerIds.size() > 3) {
0200 int iPhi = strip.cellTowerIds.at(0).second.iphi();
0201 int iPhiLower = (iPhi == 1) ? 72 : iPhi - 1;
0202 int iPhiUpper = (iPhi == 72) ? 1 : iPhi + 1;
0203
0204 float energyRatio = 0.0;
0205 if (iPhiHadEtMap.count(iPhiLower))
0206 energyRatio += iPhiHadEtMap[iPhiLower];
0207 if (iPhiHadEtMap.count(iPhiUpper))
0208 energyRatio += iPhiHadEtMap[iPhiUpper];
0209 iPhiHadEtMap[iPhi] = std::max(iPhiHadEtMap[iPhi], 0.001F);
0210
0211 energyRatio /= iPhiHadEtMap[iPhi];
0212 strip.energyRatio = energyRatio;
0213
0214 TheHcalHaloData.getProblematicStrips().push_back(strip);
0215 }
0216 strip = HaloTowerStrip();
0217 }
0218
0219 wasContiguous = isContiguous;
0220 prevPair = towerPair;
0221 prevEmEt = tower->emEt();
0222 prevIPhi = tower->iphi();
0223 prevIEta = tower->ieta();
0224 prevHadEt = tower->hadEt();
0225 }
0226
0227 geo_ = &TheSetup.getData(geoToken_);
0228 hgeo_ = dynamic_cast<const HcalGeometry*>(geo_->getSubdetectorGeometry(DetId::Hcal, 1));
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240 std::vector<HaloClusterCandidateHCAL> haloclustercands_HB;
0241 haloclustercands_HB = GetHaloClusterCandidateHB(TheEBRecHits, TheHBHERecHits, 5);
0242
0243 std::vector<HaloClusterCandidateHCAL> haloclustercands_HE;
0244 haloclustercands_HE = GetHaloClusterCandidateHE(TheEERecHits, TheHBHERecHits, 10);
0245
0246 TheHcalHaloData.setHaloClusterCandidatesHB(haloclustercands_HB);
0247 TheHcalHaloData.setHaloClusterCandidatesHE(haloclustercands_HE);
0248
0249 return TheHcalHaloData;
0250 }
0251
0252 std::vector<HaloClusterCandidateHCAL> HcalHaloAlgo::GetHaloClusterCandidateHB(
0253 edm::Handle<EcalRecHitCollection>& ecalrechitcoll,
0254 edm::Handle<HBHERecHitCollection>& hbherechitcoll,
0255 float et_thresh_seedrh) {
0256 std::vector<HaloClusterCandidateHCAL> TheHaloClusterCandsHB;
0257
0258 reco::Vertex::Point vtx(0, 0, 0);
0259
0260 for (size_t ihit = 0; ihit < hbherechitcoll->size(); ++ihit) {
0261 HaloClusterCandidateHCAL clustercand;
0262
0263 const HBHERecHit& rechit = (*hbherechitcoll)[ihit];
0264 math::XYZPoint rhpos = getPosition(rechit.id(), vtx);
0265
0266 double rhet = rechit.energy() * sqrt(rhpos.perp2() / rhpos.mag2());
0267 if (rhet < et_thresh_seedrh)
0268 continue;
0269 if (std::abs(rhpos.z()) > zseparation_HBHE)
0270 continue;
0271 double eta = rhpos.eta();
0272 double phi = rhpos.phi();
0273
0274 bool isiso = true;
0275 double etcluster(0);
0276 int nbtowerssameeta(0);
0277 double timediscriminatorITBH(0), timediscriminatorOTBH(0);
0278 double etstrip_phiseedplus1(0), etstrip_phiseedminus1(0);
0279
0280
0281 edm::RefVector<HBHERecHitCollection> bhrhcandidates;
0282 for (size_t jhit = 0; jhit < hbherechitcoll->size(); ++jhit) {
0283 const HBHERecHit& rechitj = (*hbherechitcoll)[jhit];
0284 HBHERecHitRef rhRef(hbherechitcoll, jhit);
0285 math::XYZPoint rhposj = getPosition(rechitj.id(), vtx);
0286 double rhetj = rechitj.energy() * sqrt(rhposj.perp2() / rhposj.mag2());
0287 if (rhetj < 2)
0288 continue;
0289 if (std::abs(rhposj.z()) > zseparation_HBHE)
0290 continue;
0291 double etaj = rhposj.eta();
0292 double phij = rhposj.phi();
0293 double deta = eta - etaj;
0294 double dphi = deltaPhi(phi, phij);
0295 if (std::abs(deta) > 0.4)
0296 continue;
0297 if (std::abs(dphi) > 0.2)
0298 continue;
0299 if (std::abs(dphi) > 0.1 && std::abs(deta) < 0.2) {
0300 isiso = false;
0301 break;
0302 }
0303 if (std::abs(dphi) > 0.1)
0304 continue;
0305 if (std::abs(dphi) < 0.05)
0306 nbtowerssameeta++;
0307 if (dphi > 0.05)
0308 etstrip_phiseedplus1 += rhetj;
0309 if (dphi < -0.05)
0310 etstrip_phiseedminus1 += rhetj;
0311
0312 etcluster += rhetj;
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323 double rhtj = rechitj.time();
0324 timediscriminatorITBH +=
0325 std::log10(rhetj) *
0326 (rhtj + 0.5 * (sqrt(240. * 240. + rhposj.z() * rhposj.z()) - std::abs(rhposj.z())) / c_cm_per_ns);
0327 if (std::abs(rhposj.z()) < 300)
0328 timediscriminatorOTBH +=
0329 std::log10(rhetj) *
0330 (rhtj - 0.5 * (25 - (sqrt(330. * 330. + rhposj.z() * rhposj.z()) + std::abs(rhposj.z())) / c_cm_per_ns));
0331 bhrhcandidates.push_back(rhRef);
0332 }
0333
0334 if (!isiso)
0335 continue;
0336 if (etstrip_phiseedplus1 / etcluster > 0.2 && etstrip_phiseedminus1 / etcluster > 0.2)
0337 continue;
0338
0339
0340 double eoh(0);
0341 for (size_t jhit = 0; jhit < ecalrechitcoll->size(); ++jhit) {
0342 const EcalRecHit& rechitj = (*ecalrechitcoll)[jhit];
0343 math::XYZPoint rhposj = getPosition(rechitj.id(), vtx);
0344 double rhetj = rechitj.energy() * sqrt(rhposj.perp2() / rhposj.mag2());
0345 if (rhetj < 2)
0346 continue;
0347 double etaj = rhposj.eta();
0348 double phij = rhposj.phi();
0349 if (std::abs(eta - etaj) > 0.2)
0350 continue;
0351 if (std::abs(deltaPhi(phi, phij)) > 0.2)
0352 continue;
0353 eoh += rhetj / etcluster;
0354 }
0355
0356 if (eoh > 0.1)
0357 continue;
0358
0359 clustercand.setClusterEt(etcluster);
0360 clustercand.setSeedEt(rhet);
0361 clustercand.setSeedEta(eta);
0362 clustercand.setSeedPhi(phi);
0363 clustercand.setSeedZ(rhpos.Z());
0364 clustercand.setSeedR(sqrt(rhpos.perp2()));
0365 clustercand.setSeedTime(rechit.time());
0366 clustercand.setEoverH(eoh);
0367 clustercand.setNbTowersInEta(nbtowerssameeta);
0368 clustercand.setEtStripPhiSeedPlus1(etstrip_phiseedplus1);
0369 clustercand.setEtStripPhiSeedMinus1(etstrip_phiseedminus1);
0370 clustercand.setTimeDiscriminatorITBH(timediscriminatorITBH);
0371 clustercand.setTimeDiscriminatorOTBH(timediscriminatorOTBH);
0372 clustercand.setBeamHaloRecHitsCandidates(bhrhcandidates);
0373
0374 bool isbeamhalofrompattern = HBClusterShapeandTimeStudy(clustercand, false);
0375 clustercand.setIsHaloFromPattern(isbeamhalofrompattern);
0376 bool isbeamhalofrompattern_hlt = HBClusterShapeandTimeStudy(clustercand, true);
0377 clustercand.setIsHaloFromPattern_HLT(isbeamhalofrompattern_hlt);
0378
0379 TheHaloClusterCandsHB.push_back(clustercand);
0380 }
0381
0382 return TheHaloClusterCandsHB;
0383 }
0384
0385 std::vector<HaloClusterCandidateHCAL> HcalHaloAlgo::GetHaloClusterCandidateHE(
0386 edm::Handle<EcalRecHitCollection>& ecalrechitcoll,
0387 edm::Handle<HBHERecHitCollection>& hbherechitcoll,
0388 float et_thresh_seedrh) {
0389 std::vector<HaloClusterCandidateHCAL> TheHaloClusterCandsHE;
0390
0391 reco::Vertex::Point vtx(0, 0, 0);
0392
0393 for (size_t ihit = 0; ihit < hbherechitcoll->size(); ++ihit) {
0394 HaloClusterCandidateHCAL clustercand;
0395
0396 const HBHERecHit& rechit = (*hbherechitcoll)[ihit];
0397 math::XYZPoint rhpos = getPosition(rechit.id(), vtx);
0398
0399 double rhet = rechit.energy() * sqrt(rhpos.perp2() / rhpos.mag2());
0400 if (rhet < et_thresh_seedrh)
0401 continue;
0402 if (std::abs(rhpos.z()) < zseparation_HBHE)
0403 continue;
0404 double eta = rhpos.eta();
0405 double phi = rhpos.phi();
0406 double rhr = sqrt(rhpos.perp2());
0407 bool isiso = true;
0408 double etcluster(0), hdepth1(0);
0409 int clustersize(0);
0410 double etstrip_phiseedplus1(0), etstrip_phiseedminus1(0);
0411
0412
0413 edm::RefVector<HBHERecHitCollection> bhrhcandidates;
0414 for (size_t jhit = 0; jhit < hbherechitcoll->size(); ++jhit) {
0415 const HBHERecHit& rechitj = (*hbherechitcoll)[jhit];
0416 HBHERecHitRef rhRef(hbherechitcoll, jhit);
0417 math::XYZPoint rhposj = getPosition(rechitj.id(), vtx);
0418 double rhetj = rechitj.energy() * sqrt(rhposj.perp2() / rhposj.mag2());
0419 if (rhetj < 2)
0420 continue;
0421 if (std::abs(rhposj.z()) < zseparation_HBHE)
0422 continue;
0423 if (rhpos.z() * rhposj.z() < 0)
0424 continue;
0425 double phij = rhposj.phi();
0426 double dphi = deltaPhi(phi, phij);
0427 if (std::abs(dphi) > 0.4)
0428 continue;
0429 double rhrj = sqrt(rhposj.perp2());
0430 if (std::abs(rhr - rhrj) > 50)
0431 continue;
0432 if (std::abs(dphi) > 0.2 || std::abs(rhr - rhrj) > 20) {
0433 isiso = false;
0434 break;
0435 }
0436 if (dphi > 0.05)
0437 etstrip_phiseedplus1 += rhetj;
0438 if (dphi < -0.05)
0439 etstrip_phiseedminus1 += rhetj;
0440 clustersize++;
0441 etcluster += rhetj;
0442 if (std::abs(rhposj.z()) < 405)
0443 hdepth1 += rhetj;
0444
0445 bhrhcandidates.push_back(rhRef);
0446 }
0447
0448 if (!isiso)
0449 continue;
0450 if (etstrip_phiseedplus1 / etcluster > 0.1 && etstrip_phiseedminus1 / etcluster > 0.1)
0451 continue;
0452
0453
0454 double eoh(0);
0455 for (size_t jhit = 0; jhit < ecalrechitcoll->size(); ++jhit) {
0456 const EcalRecHit& rechitj = (*ecalrechitcoll)[jhit];
0457 math::XYZPoint rhposj = getPosition(rechitj.id(), vtx);
0458 double rhetj = rechitj.energy() * sqrt(rhposj.perp2() / rhposj.mag2());
0459 if (rhetj < 2)
0460 continue;
0461 if (rhpos.z() * rhposj.z() < 0)
0462 continue;
0463 double etaj = rhposj.eta();
0464 double phij = rhposj.phi();
0465 double dr = sqrt((eta - etaj) * (eta - etaj) + deltaPhi(phi, phij) * deltaPhi(phi, phij));
0466 if (dr > 0.3)
0467 continue;
0468
0469 eoh += rhetj / etcluster;
0470 }
0471
0472 if (eoh > 0.1)
0473 continue;
0474
0475 clustercand.setClusterEt(etcluster);
0476 clustercand.setSeedEt(rhet);
0477 clustercand.setSeedEta(eta);
0478 clustercand.setSeedPhi(phi);
0479 clustercand.setSeedZ(rhpos.Z());
0480 clustercand.setSeedR(sqrt(rhpos.perp2()));
0481 clustercand.setSeedTime(rechit.time());
0482 clustercand.setEoverH(eoh);
0483 clustercand.setH1overH123(hdepth1 / etcluster);
0484 clustercand.setClusterSize(clustersize);
0485 clustercand.setEtStripPhiSeedPlus1(etstrip_phiseedplus1);
0486 clustercand.setEtStripPhiSeedMinus1(etstrip_phiseedminus1);
0487 clustercand.setTimeDiscriminator(0);
0488 clustercand.setBeamHaloRecHitsCandidates(bhrhcandidates);
0489
0490 bool isbeamhalofrompattern = HEClusterShapeandTimeStudy(clustercand, false);
0491 clustercand.setIsHaloFromPattern(isbeamhalofrompattern);
0492 bool isbeamhalofrompattern_hlt = HEClusterShapeandTimeStudy(clustercand, true);
0493 clustercand.setIsHaloFromPattern_HLT(isbeamhalofrompattern_hlt);
0494
0495 TheHaloClusterCandsHE.push_back(clustercand);
0496 }
0497
0498 return TheHaloClusterCandsHE;
0499 }
0500
0501 bool HcalHaloAlgo::HBClusterShapeandTimeStudy(HaloClusterCandidateHCAL hcand, bool ishlt) {
0502
0503
0504
0505
0506 if (hcand.getSeedEt() < 10)
0507 return false;
0508
0509 if (hcand.getNbTowersInEta() < 3)
0510 return false;
0511
0512 if (hcand.getNbTowersInEta() == 3 && (hcand.getEtStripPhiSeedPlus1() > 0.1 || hcand.getEtStripPhiSeedMinus1() > 0.1))
0513 return false;
0514 if (hcand.getNbTowersInEta() <= 5 && (hcand.getEtStripPhiSeedPlus1() > 0.1 && hcand.getEtStripPhiSeedMinus1() > 0.1))
0515 return false;
0516
0517
0518 if (hcand.getNbTowersInEta() == 3 && hcand.getTimeDiscriminatorITBH() >= 0.)
0519 return false;
0520 if (hcand.getNbTowersInEta() <= 6 && hcand.getTimeDiscriminatorITBH() >= 5. && hcand.getTimeDiscriminatorOTBH() < 0.)
0521 return false;
0522
0523
0524 if (ishlt && hcand.getNbTowersInEta() < 7)
0525 return false;
0526
0527 hcand.setIsHaloFromPattern(true);
0528
0529 return true;
0530 }
0531
0532 bool HcalHaloAlgo::HEClusterShapeandTimeStudy(HaloClusterCandidateHCAL hcand, bool ishlt) {
0533
0534
0535
0536
0537
0538 if (hcand.getSeedEt() < 20)
0539 return false;
0540 if (hcand.getSeedR() > 170)
0541 return false;
0542
0543 if (hcand.getH1overH123() > 0.02 && hcand.getH1overH123() < 0.98)
0544 return false;
0545
0546
0547
0548
0549 if (ishlt && hcand.getSeedEt() < 50)
0550 return false;
0551
0552 hcand.setIsHaloFromPattern(true);
0553
0554 return true;
0555 }
0556
0557 math::XYZPoint HcalHaloAlgo::getPosition(const DetId& id, reco::Vertex::Point vtx) {
0558 const GlobalPoint pos = ((id.det() == DetId::Hcal) ? hgeo_->getPosition(id) : GlobalPoint(geo_->getPosition(id)));
0559 math::XYZPoint posV(pos.x() - vtx.x(), pos.y() - vtx.y(), pos.z() - vtx.z());
0560 return posV;
0561 }