File indexing completed on 2025-01-18 09:15:58
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include "DD4hep/DetFactoryHelper.h"
0014 #include "DD4hep/Printout.h"
0015 #include "DD4hep/Shapes.h"
0016 #include "DDRec/DetectorData.h"
0017 #include "DDRec/Surface.h"
0018 #include "XML/Layering.h"
0019 #include "XML/Utilities.h"
0020 #include <array>
0021 #include "DD4hepDetectorHelper.h"
0022
0023 using namespace std;
0024 using namespace dd4hep;
0025 using namespace dd4hep::rec;
0026 using namespace dd4hep::detail;
0027
0028 #include "Math/Vector2D.h"
0029 using ROOT::Math::XYVector;
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050 static Ref_t create_MPGDCylinderBarrelTracker(Detector& description, xml_h e,
0051 SensitiveDetector sens) {
0052 xml_det_t x_det = e;
0053 Material air = description.air();
0054 int det_id = x_det.id();
0055 string det_name = x_det.nameStr();
0056 DetElement sdet(det_name, det_id);
0057
0058 vector<Volume> volumes;
0059 vector<PlacedVolume> sensitives;
0060 vector<VolPlane> volplane_surfaces;
0061
0062 PlacedVolume pv;
0063
0064
0065 #ifdef DEBUG_MPGDCylinderBarrelTracker
0066
0067 PrintLevel priorPrintLevel = printLevel();
0068 setPrintLevel(DEBUG);
0069 #endif
0070
0071
0072 dd4hep::xml::setDetectorTypeFlag(x_det, sdet);
0073 auto& params = DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(sdet);
0074
0075
0076 for (xml_coll_t bmat(x_det, _Unicode(boundary_material)); bmat; ++bmat) {
0077 xml_comp_t x_boundary_material = bmat;
0078 DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_boundary_material, params,
0079 "boundary_material");
0080 }
0081
0082 dd4hep::xml::Dimension dimensions(x_det.dimensions());
0083 Assembly assembly(det_name);
0084
0085 sens.setType("tracker");
0086
0087
0088
0089 xml_coll_t modules(x_det, _U(module));
0090 if (modules.size() != 1) {
0091
0092 printout(ERROR, "MPGDCylinderBarrelTracker", "Number of modules = %u. Must be = 1",
0093 modules.size());
0094 throw runtime_error("Logics error in building modules.");
0095 }
0096 xml_comp_t x_mod = modules;
0097 string m_nam = x_mod.nameStr();
0098 double stave_width = dimensions.width(), det_length = dimensions.length();
0099 printout(DEBUG, "MPGDCylinderBarrelTracker", "Module \"%s\" width = %.2f cm length = %.2f cm",
0100 m_nam.c_str(), stave_width / cm, det_length / cm);
0101
0102
0103
0104 xml_coll_t li(x_det, _U(layer));
0105 if (li.size() != 1) {
0106 printout(ERROR, "MPGDCylinderBarrelTracker", "Number of layers = %d. Must be = 1",
0107 (int)li.size());
0108 throw runtime_error("Logics error in building modules.");
0109 }
0110
0111 xml_comp_t x_layer = li;
0112 int lay_id = x_layer.id();
0113 if (x_layer.moduleStr() != m_nam) {
0114 printout(ERROR, "MPGDCylinderBarrelTracker", "Layer \"%s\" does not match module \"%s\"",
0115 x_layer.moduleStr().c_str(), m_nam.c_str());
0116 throw runtime_error("Logics error in building layer.");
0117 }
0118 xml_comp_t x_barrel = x_layer.child(_U(barrel_envelope));
0119 double barrel_length = x_barrel.z_length();
0120 double barrel_z0 = getAttrOrDefault(x_barrel, _U(z0), 0.);
0121
0122 xml_comp_t x_layout = x_layer.child(_U(rphi_layout));
0123 double phi0 = x_layout.phi0();
0124 xml_comp_t z_layout = x_layer.child(_U(z_layout));
0125 double z_gap = z_layout.gap();
0126
0127
0128
0129
0130
0131 const int nUnvalids = 4;
0132 const xml::Tag_t unvalidTags[nUnvalids] = {_U(phi_tilt), _U(nphi), _U(rc), _U(dr)};
0133 for (int uv = 0; uv < nUnvalids; uv++) {
0134 if (x_barrel.hasChild(unvalidTags[uv])) {
0135 const string tag = _U(nphi);
0136 printout(ERROR, "MPGDCylinderBarrelTracker",
0137 "Layer \"%s\": Unvalid property \"%s\" in \"rphi_layout\"", m_nam.c_str(),
0138 tag.c_str());
0139 throw runtime_error("Logics error in building modules.");
0140 }
0141 }
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152 typedef struct {
0153 string name;
0154 double rmin;
0155
0156
0157
0158 double rsensor;
0159 int nphi;
0160 double offset;
0161 int io;
0162 } StaveModel;
0163 StaveModel staveModels[4];
0164 int sector2Models[2][2];
0165 xml_coll_t mi(x_mod, _U(model));
0166 if (mi.size() != 2) {
0167 printout(ERROR, "MPGDCylinderBarrelTracker", "Number of models = %d. Must be = 2",
0168 (int)mi.size());
0169 throw runtime_error("Logics error in building modules.");
0170 }
0171 int nStaveModels;
0172 for (nStaveModels = 0; mi; ++mi) {
0173 xml_comp_t x_model = mi;
0174 double rmin1 = x_model.rmin1(), rmin2 = x_model.rmin2();
0175 double rsensor = x_model.attr<double>(_Unicode(rsensor));
0176
0177 int nphi = int(2 * M_PI * rmin1 / stave_width + .5),
0178 nphi2 = int(2 * M_PI * rmin2 / stave_width + .5);
0179 if (nphi2 != nphi) {
0180 printout(ERROR, "MPGDCylinderBarrelTracker",
0181 "Model \"%s\": rmin1,2 = %.2f,%.2f cm are incompatible", x_model.nameStr().c_str(),
0182 rmin1, rmin2);
0183 throw runtime_error("Logics error in building modules.");
0184 }
0185 double offset = x_model.offset();
0186 StaveModel& staveModel = staveModels[nStaveModels++];
0187 staveModel.rmin = rmin1;
0188 staveModel.rsensor = rsensor;
0189 staveModel.nphi = nphi;
0190 staveModel.offset = offset;
0191 int io;
0192 if (nStaveModels == 1)
0193 io = 0;
0194 else
0195 io = 1;
0196 staveModel.io = io;
0197 sector2Models[io][0] = nStaveModels - 1;
0198 if (fabs(rmin2 - rmin1) > 1.e6) {
0199 StaveModel& staveMode2 = staveModels[nStaveModels++];
0200 staveMode2.rmin = rmin2;
0201 staveModel.rsensor = rsensor + rmin2 - rmin1;
0202 staveModel.nphi = nphi;
0203 staveMode2.offset = offset;
0204 staveMode2.io = io;
0205 staveModel.name = x_model.nameStr() + string("_1");
0206 staveMode2.name = x_model.nameStr() + string("_2");
0207 } else
0208 staveModel.name = x_model.nameStr();
0209 sector2Models[io][1] = nStaveModels - 1;
0210 }
0211 printout(DEBUG, "MPGDCylinderBarrelTracker", "%d Stave Models:", nStaveModels);
0212 for (int iSM = 0; iSM < nStaveModels; iSM++) {
0213 StaveModel& sM = staveModels[iSM];
0214 printout(DEBUG, "MPGDCylinderBarrelTracker",
0215 "Stave Model #%d \"%s\": rmin = %.2f cm offset = ±%.2f cm %s", iSM, sM.name.c_str(),
0216 sM.rmin, sM.offset / 2, sM.io ? "Outer" : "Inner");
0217 }
0218
0219
0220
0221
0222
0223
0224 typedef struct {
0225 string name;
0226 double width;
0227 string material;
0228 string vis;
0229 } Frame;
0230 Frame frames[2];
0231 xml_coll_t fi(x_mod, _U(frame));
0232 if (fi.size() != 2) {
0233 printout(ERROR, "MPGDCylinderBarrelTracker", "Number of frames = %d. Must be = 2",
0234 (int)fi.size());
0235 throw runtime_error("Logics error in building modules.");
0236 }
0237 printout(DEBUG, "MPGDCylinderBarrelTracker", "2 Frames:");
0238 int iFr;
0239 for (iFr = 0; fi; ++fi, iFr++) {
0240 xml_comp_t x_frame = fi;
0241 string name = x_frame.nameStr();
0242 double width = x_frame.width();
0243 string material = x_frame.materialStr(), vis = x_frame.visStr();
0244 Frame& frame = frames[iFr];
0245 frame.name = name;
0246 frame.width = width;
0247 frame.material = material;
0248 frame.vis = vis;
0249 }
0250 if (frames[0].width < frames[1].width)
0251 swap(frames[0], frames[1]);
0252 for (iFr = 0; iFr < 2; iFr++) {
0253 Frame& frame = frames[iFr];
0254 printout(DEBUG, "MPGDCylinderBarrelTracker",
0255 "Frame #%d \"%s\": width = %.2f cm material = \"%s\" vis = \"%s\"", iFr,
0256 frame.name.c_str(), frame.width, frame.material.c_str(), frame.vis.c_str());
0257 }
0258
0259
0260 xml_coll_t si(x_mod, _Unicode(service));
0261 if (si.size() != 1) {
0262 printout(ERROR, "MPGDCylinderBarrelTracker", "Number of services = %d. Must be = 1",
0263 (int)si.size());
0264 throw runtime_error("Logics error in building modules.");
0265 }
0266 xml_comp_t x_service = si;
0267 double service_thickness = x_service.thickness();
0268 printout(DEBUG, "MPGDCylinderBarrelTracker",
0269 "1 Service \"%s\": thickness = %.4f cm, material = \"%s\"", x_service.nameStr().c_str(),
0270 service_thickness, x_service.materialStr().c_str());
0271
0272
0273 double total_thickness = 0;
0274 xml_coll_t ci(x_mod, _U(module_component));
0275 for (ci.reset(), total_thickness = 0.0; ci; ++ci) {
0276 const xml_comp_t x_comp = ci;
0277 printout(DEBUG, "MPGDCylinderBarrelTracker", "\"%s\": \t total_thickness %.4f cm",
0278 x_comp.nameStr().c_str(), total_thickness / cm);
0279 total_thickness += x_comp.thickness();
0280 }
0281 printout(DEBUG, "MPGDCylinderBarrelTracker", " => total_thickness %.4f cm", total_thickness / cm);
0282
0283
0284
0285 printout(DEBUG, "MPGDCylinderBarrelTracker", "2 Sector Models:");
0286 double outerPhiSuperpos = 0;
0287 for (int io = 0; io < 2; io++) {
0288 StaveModel& sM0 = staveModels[sector2Models[io][0]];
0289 StaveModel& sM1 = staveModels[sector2Models[io][1]];
0290 XYVector vOffset(sM0.offset / 2, 0);
0291 double phiEdge0 = stave_width / 2 / sM0.rmin;
0292 XYVector vEdge0(sM0.rmin * cos(phiEdge0), sM0.rmin * sin(phiEdge0));
0293 vEdge0 -= vOffset;
0294
0295
0296 double RMn = sM0.rmin - sM0.offset / 2, Mag0 = sqrt(vEdge0.Mag2());
0297 double phiEdge1 = stave_width / 2 / sM1.rmin;
0298 XYVector vEdge1(sM1.rmin * cos(phiEdge1), sM1.rmin * sin(phiEdge1));
0299 vEdge1 += vOffset;
0300
0301
0302 double RMx = sM1.rmin + sM1.offset / 2, Mag1 = sqrt(vEdge1.Mag2());
0303 double dPhi = acos(vEdge0.Dot(vEdge1) / Mag0 / Mag1);
0304 RMx += total_thickness;
0305 if (io == 1) {
0306 RMn -= service_thickness;
0307 outerPhiSuperpos = dPhi;
0308 }
0309 printout(
0310 DEBUG, "MPGDCylinderBarrelTracker",
0311 "Sector Model #%d,\"%s\" = \"%s\"+\"%s\": phi overlap = %.1f deg, Extrema R = %.2f,%.2f",
0312 io, io ? "Outer" : "Inner", sM0.name.c_str(), sM1.name.c_str(), dPhi / M_PI * 180, RMn,
0313 RMx);
0314 }
0315
0316
0317 double total_length = 0;
0318 for (int iSM = 0; iSM < nStaveModels; iSM++) {
0319
0320
0321
0322 int sensor_number = iSM;
0323 StaveModel& staveModel = staveModels[iSM];
0324
0325 double stave_rmin = staveModel.rmin;
0326 double dphi = stave_width / stave_rmin / 2;
0327 double phi_start = -dphi, phi_end = dphi;
0328
0329
0330 Assembly m_vol(staveModel.name);
0331 volumes.push_back(m_vol);
0332 m_vol.setVisAttributes(description.visAttributes(x_mod.visStr()));
0333
0334
0335 double zthickness, stave_rmax = stave_rmin + total_thickness;
0336 Frame &out_frame = frames[0], &frame = frames[1];
0337 total_length = det_length + out_frame.width + frame.width;
0338
0339 zthickness = out_frame.width;
0340 Tube frame_tube_O(stave_rmin, stave_rmax, zthickness / 2, phi_start, phi_end);
0341 Volume frame_vol_O(out_frame.name, frame_tube_O, description.material(out_frame.material));
0342 m_vol.placeVolume(frame_vol_O, Position(0, 0, -(total_length - zthickness) / 2));
0343
0344 zthickness = frame.width;
0345 Tube frame_tube_I(stave_rmin, stave_rmax, zthickness / 2, phi_start, phi_end);
0346 Volume frame_vol_I(frame.name, frame_tube_I, description.material(frame.material));
0347 m_vol.placeVolume(frame_vol_I, Position(0, 0, (total_length - zthickness) / 2));
0348
0349
0350 double frame_dphi =
0351 zthickness / stave_rmin;
0352 Tube frame_tube_3(stave_rmin, stave_rmax, total_length / 2, phi_start - frame_dphi, phi_start);
0353 const string start_frame_nam("StartFrame");
0354 Volume frame_vol_3(start_frame_nam, frame_tube_3, description.material(frame.material));
0355 m_vol.placeVolume(frame_vol_3);
0356
0357 Tube frame_tube_4(stave_rmin, stave_rmax, total_length / 2, phi_end, phi_end + frame_dphi);
0358 const string stop_frame_nam("StopFrame");
0359 Volume frame_vol_4(stop_frame_nam, frame_tube_4, description.material(frame.material));
0360 m_vol.placeVolume(frame_vol_4);
0361
0362 frame_vol_O.setVisAttributes(description, out_frame.vis);
0363 frame_vol_I.setVisAttributes(description, frame.vis);
0364 frame_vol_3.setVisAttributes(description, frame.vis);
0365 frame_vol_4.setVisAttributes(description, frame.vis);
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375 if (staveModel.io == 1) {
0376
0377 double outerPos = barrel_length / 2 - total_length / 2;
0378 double innerPos = (total_length + z_gap) / 2;
0379 double zSuperpos = total_length - outerPos + innerPos;
0380
0381 double serv_thickness = service_thickness;
0382 double serv_rmin = stave_rmin - service_thickness;
0383 double serv_length = total_length - zSuperpos;
0384
0385 double dPhi = outerPhiSuperpos / 2 + (frame.width + .1) / stave_rmin;
0386 double serv_start = phi_start + dPhi, serv_stop = phi_end - dPhi;
0387 Tube c_tube(serv_rmin, serv_rmin + serv_thickness, serv_length / 2, serv_start, serv_stop);
0388 Volume c_vol(x_service.nameStr(), c_tube, description.material(x_service.materialStr()));
0389 pv = m_vol.placeVolume(c_vol, Position(0, 0, -zSuperpos / 2));
0390 c_vol.setRegion(description, x_service.regionStr());
0391 c_vol.setLimitSet(description, x_service.limitsStr());
0392 c_vol.setVisAttributes(description, x_service.visStr());
0393 }
0394
0395
0396 double comp_rmin = stave_rmin;
0397 double thickness_so_far = 0;
0398 xml_comp_t* sensitiveComp = 0;
0399 for (xml_coll_t mci(x_mod, _U(module_component)); mci; ++mci) {
0400 xml_comp_t x_comp = mci;
0401 const string c_nam = x_comp.nameStr();
0402 double comp_thickness = x_comp.thickness();
0403 Tube c_tube(comp_rmin, comp_rmin + comp_thickness, det_length / 2, phi_start, phi_end);
0404 Volume c_vol(c_nam, c_tube, description.material(x_comp.materialStr()));
0405 pv = m_vol.placeVolume(c_vol, Position(0, 0, (out_frame.width - frame.width) / 2));
0406 c_vol.setRegion(description, x_comp.regionStr());
0407 c_vol.setLimitSet(description, x_comp.limitsStr());
0408 c_vol.setVisAttributes(description, x_comp.visStr());
0409 if (x_comp.isSensitive()) {
0410
0411 if (sensitiveComp) {
0412 printout(ERROR, "MPGDCylinderBarrelTracker",
0413 "Component \"%s\": 2nd sensitive component in module \"%s\" (1st is \"%s\"). "
0414 "One only allowed.",
0415 c_nam.c_str(), m_nam.c_str(), sensitiveComp->nameStr().c_str());
0416 throw runtime_error("Logics error in building modules.");
0417 }
0418 sensitiveComp = &x_comp;
0419 pv.addPhysVolID("sensor", sensor_number);
0420 c_vol.setSensitiveDetector(sens);
0421 sensitives.push_back(pv);
0422
0423
0424 Vector3D u(-1., 0., 0.);
0425 Vector3D v(0., -1., 0.);
0426 Vector3D n(0., 0., 1.);
0427
0428
0429
0430
0431
0432 double inner_thickness = thickness_so_far + comp_thickness / 2;
0433 double outer_thickness = total_thickness - inner_thickness;
0434
0435 double rXCheck = comp_rmin + comp_thickness / 2;
0436 if (fabs(staveModel.rsensor - rXCheck) > .0001 / cm) {
0437 printout(ERROR, "MPGDCylinderBarrelTracker",
0438 "Sensitive Component \"%s\" of StaveModel #%d,\"%s\": rsensor(%.4f cm) != "
0439 "radius @ sensitive surface(%.4f cm)",
0440 iSM, c_nam.c_str(), staveModel.name.c_str(), staveModel.rsensor / cm,
0441 rXCheck / cm);
0442 throw runtime_error("Logics error in building modules.");
0443 }
0444 printout(DEBUG, "MPGDCylinderBarrelTracker",
0445 "Stave Model #%d,\"%s\": Sensitive surface @ R = %.4f (%.4f,%.4f) cm", iSM,
0446 staveModel.name.c_str(), staveModel.rsensor / cm, inner_thickness / cm,
0447 outer_thickness / cm);
0448
0449 SurfaceType type(SurfaceType::Sensitive);
0450 VolPlane surf(c_vol, type, inner_thickness, outer_thickness, u, v, n);
0451 volplane_surfaces.push_back(surf);
0452 }
0453 comp_rmin += comp_thickness;
0454 thickness_so_far += comp_thickness;
0455 }
0456 }
0457
0458
0459
0460 string lay_nam = det_name + _toString(x_layer.id(), "_layer%d");
0461 Tube lay_tub(x_barrel.inner_r(), x_barrel.outer_r(), barrel_length / 2);
0462 Volume lay_vol(lay_nam, lay_tub, air);
0463 Position lay_pos(0, 0, barrel_z0);
0464 lay_vol.setVisAttributes(description.visAttributes(x_layer.visStr()));
0465 printout(DEBUG, "MPGDCylinderBarrelTracker",
0466 "Layer \"%s\": rmin,max = %.2f,%.2f cm 1/2length = %.2f cm", lay_nam.c_str(),
0467 x_barrel.inner_r(), x_barrel.outer_r(), barrel_length / 2);
0468
0469 DetElement lay_elt(sdet, lay_nam, lay_id);
0470
0471
0472
0473 auto& layerParams =
0474 DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(lay_elt);
0475
0476
0477
0478
0479
0480 double modz_pos[4] = {-barrel_length / 2 + (total_length) / 2, -(total_length + z_gap) / 2,
0481 +(total_length + z_gap) / 2, +barrel_length / 2 - (total_length) / 2};
0482 int nModules = 0;
0483 for (int iz = 0; iz < 4; iz++) {
0484 int io = (iz == 1 || iz == 2) ? 0 : 1;
0485 int iSMs[2] = {sector2Models[io][0], sector2Models[io][1]};
0486 int iSM0 = iSMs[0];
0487 const StaveModel& sm0 = staveModels[iSM0];
0488 int nphi = sm0.nphi;
0489 double offset = sm0.offset;
0490 double phi_incr = 2 * M_PI / nphi;
0491
0492 int iphi;
0493 double phic;
0494 for (iphi = 0, phic = phi0; iphi < nphi; iphi++, phic += phi_incr, nModules++) {
0495
0496 int jphi = iphi % 2, iV = iSMs[jphi];
0497 double rc = (2 * jphi - 1) * offset / 2;
0498 if (iz >= 2)
0499 rc *= -1;
0500 double x1, y1;
0501 x1 = rc * std::cos(phic);
0502 y1 = rc * std::sin(phic);
0503 string module_name = _toString(8 * iz + iphi, "module%02d");
0504 DetElement mod_elt(lay_elt, module_name, nModules);
0505 printout(DEBUG, "MPGDCylinderBarrelTracker",
0506 "System %d Layer \"%s\",id=%d Module \"%s\",id=%d: x,y,r: %7.4f,%7.4f,%7.4f cm",
0507 det_id, lay_nam.c_str(), lay_id, module_name.c_str(), nModules, x1 / cm, y1 / cm,
0508 sqrt(x1 * x1 + y1 * y1) / cm);
0509 RotationZYX rot;
0510 rot = RotationZYX(phic, 0, 0);
0511 if (iz >= 2)
0512
0513
0514
0515
0516
0517 rot *= RotationZYX(0, M_PI, 0);
0518 Transform3D tr(rot, Position(x1, y1, modz_pos[iz]));
0519 Volume& module_vol = volumes[iV];
0520 pv = lay_vol.placeVolume(module_vol, tr);
0521 pv.addPhysVolID("module", nModules);
0522 mod_elt.setPlacement(pv);
0523
0524 PlacedVolume& sens_pv = sensitives[iV];
0525 DetElement comp_de(
0526 mod_elt, std::string("de_") + sens_pv.volume().name() + _toString(8 * iz + iphi, "%02d"),
0527 nModules);
0528 comp_de.setPlacement(sens_pv);
0529 auto& comp_de_params =
0530 DD4hepDetectorHelper::ensureExtension<dd4hep::rec::VariantParameters>(comp_de);
0531 comp_de_params.set<string>("axis_definitions", "XYZ");
0532 volSurfaceList(comp_de)->push_back(volplane_surfaces[iV]);
0533 }
0534 }
0535
0536 for (xml_coll_t lmat(x_layer, _Unicode(layer_material)); lmat; ++lmat) {
0537 xml_comp_t x_layer_material = lmat;
0538 DD4hepDetectorHelper::xmlToProtoSurfaceMaterial(x_layer_material, layerParams,
0539 "layer_material");
0540 }
0541
0542
0543 pv = assembly.placeVolume(lay_vol, lay_pos);
0544 pv.addPhysVolID("layer", lay_id);
0545 lay_elt.setAttributes(description, lay_vol, x_layer.regionStr(), x_layer.limitsStr(),
0546 x_layer.visStr());
0547 lay_elt.setPlacement(pv);
0548
0549 sdet.setAttributes(description, assembly, x_det.regionStr(), x_det.limitsStr(), x_det.visStr());
0550 assembly.setVisAttributes(description.invisible());
0551 pv = description.pickMotherVolume(sdet).placeVolume(assembly);
0552 pv.addPhysVolID("system", det_id);
0553 sdet.setPlacement(pv);
0554
0555 #ifdef DEBUG_MPGDCylinderBarrelTracker
0556
0557 setPrintLevel(priorPrintLevel);
0558 #endif
0559
0560 return sdet;
0561 }
0562
0563
0564
0565 DECLARE_DETELEMENT(epic_CylinderMPGDBarrel, create_MPGDCylinderBarrelTracker)