15 #include "GaudiKernel/MsgStream.h"
17 #include "CLHEP/Units/SystemOfUnits.h"
18 #include "CLHEP/Random/RandFlat.h"
19 #include "CLHEP/Random/RandGaussZiggurat.h"
20 #include "CLHEP/Random/RandGamma.h"
34 const int channelTypes,
36 bool doPadChargeSharing,
37 bool applyAsBuiltBLines)
39 m_idHelperSvc{idHelperSvc},
40 m_channelTypes{channelTypes},
41 m_meanGasGain{meanGasGain},
42 m_doPadSharing{doPadChargeSharing},
43 m_applyAsBuiltBLines(applyAsBuiltBLines) {}
60 ATH_MSG_ERROR(
"Invalid ChannelTypes : " <<
m_channelTypes <<
" (valid values are : 1 --> strips ; 2 --> strips / wires ; 3 --> strips / wires / pads)");
61 return StatusCode::FAILURE;
63 return StatusCode::SUCCESS;
79 const Identifier layid = idHelper.channelID(offlineId,
80 idHelper.multilayer(offlineId),
81 idHelper.gasGap(offlineId),
82 sTgcIdHelper::sTgcChannelTypes::Wire, 1);
92 if( !detEl ){
return {}; }
98 constexpr
double length_tolerance = 0.01;
114 Amg::Vector2D posOnSurf_wire{0.5 * (hitOnSurface_wire + pre_pos_wire_surf).block<2,1>(0,0)};
131 if ((wire_number < 1) || (wire_number > number_wires)) {
135 if ((wire_number < 1) || (wire_number > number_wires)) {
142 double dist_wire = ionization.
distance;
143 if (dist_wire > 0.) {
153 double dist_wire_adj = ion_adj.
distance;
154 if ((dist_wire_adj > 0.) && (dist_wire_adj < dist_wire)) {
155 dist_wire = dist_wire_adj;
157 ionization = std::move(ion_adj);
160 ATH_MSG_DEBUG(
"Failed to get the distance between the wire number = " << wire_number
162 <<
". Number of wires = " << number_wires
168 posOnSurf_wire = ionization.
posOnWire.block<2,1>(0,0);
186 const double wire_pitch = detEl->
wirePitch();
187 if ((dist_wire > 0.) && (std::abs(hit.
particleEncoding()) == 13) && (dist_wire > (wire_pitch/2))) {
188 ATH_MSG_DEBUG(
"Distance to the nearest wire (" << dist_wire <<
") is greater than expected.");
198 if (dist_wire > wire_pitch) {
208 double gamma_mpv = (par_kappa - 1) * par_theta;
210 if (gamma_mpv < 0.) {gamma_mpv = 0.;}
211 const double t0_par = most_prob_time - gamma_mpv;
218 double digit_time = t0_par + CLHEP::RandGamma::shoot(condContainers.
rndmEngine, par_kappa, 1/par_theta);
224 constexpr
int shoot_limit = 4;
225 int shoot_counter = 0;
226 while (digit_time < 0.) {
227 if (shoot_counter > shoot_limit) {
231 digit_time = t0_par + CLHEP::RandGamma::shoot(condContainers.
rndmEngine, par_kappa, 1/par_theta);
236 <<
", time = " << digit_time
237 <<
", k parameter = " << par_kappa
238 <<
", theta parameter = " << par_theta
239 <<
", most probable time = " << most_prob_time);
253 const double stripPropagationTime = 0.;
255 double sDigitTimeWire = digit_time;
256 double sDigitTimePad = sDigitTimeWire;
257 double sDigitTimeStrip = sDigitTimeWire + stripPropagationTime;
265 int channelType = sTgcIdHelper::sTgcChannelTypes::Strip;
267 Identifier newId = idHelper.channelID(layid,idHelper.multilayer(layid),
268 idHelper.gasGap(layid), channelType, 1,
isValid);
280 detEl->
spacePointPosition(newId, hitOnSurface_strip.x(), hitOnSurface_strip.y(), posAfterAsBuilt);
281 posOnSurf_strip = posAfterAsBuilt.block<2,1>(0,0);
283 posOnSurf_strip = hitOnSurface_strip.block<2,1>(0,0);
286 bool insideBounds = SURF_STRIP.
insideBounds(posOnSurf_strip);
294 int stripNumber = detEl->
stripNumber(posOnSurf_strip, newId);
295 if( stripNumber == -1 ){
297 const double new_posX = (posOnSurf_strip.x() > 0.0)? posOnSurf_strip.x() - length_tolerance
298 : posOnSurf_strip.x() + length_tolerance;
299 const Amg::Vector2D new_position(new_posX, posOnSurf_strip.y());
300 stripNumber = detEl->
stripNumber(new_position, newId);
302 if (stripNumber < 1) {
304 ATH_MSG_WARNING(
"Position on strip surface = (" << posOnSurf_strip.x() <<
", " << posOnSurf_strip.y() <<
")");
308 newId = idHelper.channelID(layid, idHelper.multilayer(layid),
309 idHelper.gasGap(layid), channelType, stripNumber,
isValid);
310 if(!
isValid && stripNumber != -1) {
316 const double stripHalfPitch = detEl->
channelPitch(newId)*0.5;
337 const double total_charge =
gain*ionized_charge;
341 const double tan_theta = GLODIRE.perp()/GLODIRE.z();
345 const double cluster_posX = posOnSurf_strip.x();
346 double peak_position = CLHEP::RandGaussZiggurat::shoot(condContainers.
rndmEngine, cluster_posX,
m_StripResolution*angle_dependency);
350 const double norm = 0.5 * total_charge;
354 std::unique_ptr<TF1> clusterProfile = std::make_unique<TF1>(
"fgaus",
355 "0.5 * [2] / (sqrt(2 * TMath::Pi()) * [0]) * exp(-0.5 * (x / [0])^2) + "
356 "0.5 * [2] / (sqrt(2 * TMath::Pi()) * [1]) * exp(-0.5 * (x / [1])^2)", -300., 300.);
362 constexpr
double tolerance_charge = 0.0005;
364 constexpr
unsigned int max_neighbor = 10;
367 for (
unsigned int iStrip = 0; iStrip <= max_neighbor; ++iStrip) {
368 int currentStrip = stripNumber + iStrip;
369 if (currentStrip > NumberOfStrips)
break;
372 newId = idHelper.channelID(layid, idHelper.multilayer(layid), idHelper.gasGap(layid),
373 channelType, currentStrip,
isValid);
382 double x_relative = locpos.x() - peak_position;
384 double charge = std::hypot(1,
m_chargeAngularFactor * tan_theta) * clusterProfile->Integral(x_relative/(2*stripHalfPitch) - 0.5, x_relative/(2*stripHalfPitch) + 0.5);
386 if (
charge < tolerance_charge)
break;
389 double strip_time = sDigitTimeStrip;
394 int indexFromMiddleStrip = iStrip;
395 if ((iStrip > 0) && ((x_relative + (0.75 - iStrip * 2) * stripHalfPitch) < 0))
396 indexFromMiddleStrip = iStrip - 1;
404 <<
", time = " << strip_time <<
", time offset = " << strip_time-sDigitTimeStrip
405 <<
", neighbor index = " << iStrip
411 for (
unsigned int iStrip = 1; iStrip <= max_neighbor; ++iStrip) {
412 int currentStrip = stripNumber - iStrip;
413 if (currentStrip < 1)
break;
415 newId = idHelper.channelID(layid,idHelper.multilayer(layid),
416 idHelper.gasGap(layid), channelType, currentStrip,
isValid);
424 double x_relative = locpos.x() - peak_position;
425 double charge = std::hypot(1,
m_chargeAngularFactor * tan_theta) * clusterProfile->Integral(x_relative/(2*stripHalfPitch) - 0.5, x_relative/(2*stripHalfPitch) + 0.5);
426 if (
charge < tolerance_charge)
break;
429 double strip_time = sDigitTimeStrip;
432 int indexFromMiddleStrip = ((x_relative + (iStrip * 2 - 0.75) * stripHalfPitch) > 0)? iStrip - 1 : iStrip;
439 <<
", time = " << strip_time <<
", time offset = " << strip_time-sDigitTimeStrip
440 <<
", neighbor index = " << iStrip
455 channelType = sTgcIdHelper::sTgcChannelTypes::Pad;
457 Identifier PAD_ID = idHelper.channelID(layid, idHelper.multilayer(layid),
458 idHelper.gasGap(layid), channelType, 1);
464 const Amg::Vector2D posOnSurf_pad(hitOnSurface_pad.x(), hitOnSurface_pad.y());
470 int padNumber = detEl->
stripNumber(posOnSurf_pad, PAD_ID);
471 if( padNumber == -1 ){
473 const double new_posX = (posOnSurf_pad.x()>0.0)? posOnSurf_pad.x()-length_tolerance
474 : posOnSurf_pad.x()+length_tolerance;
475 const double new_posY = (posOnSurf_pad.y()>0.0)? posOnSurf_pad.y()-length_tolerance
476 : posOnSurf_pad.y()+length_tolerance;
478 padNumber = detEl->
stripNumber(new_position, PAD_ID);
482 ATH_MSG_WARNING(
"Position on pad surface = (" << posOnSurf_pad.x() <<
", " << posOnSurf_pad.y() <<
")");
486 newId = idHelper.channelID(layid, idHelper.multilayer(layid),
487 idHelper.gasGap(layid), channelType, padNumber,
isValid);
505 double deltaX = halfPadWidthX - std::abs(
diff.x());
506 double deltaY= halfPadWidthY - std::abs(
diff.y());
518 int newPhi =
diff.x() > 0 ? padEtaPhi.second-1 : padEtaPhi.second+1;
519 int newEta =
diff.y() > 0 ? padEtaPhi.first+1 : padEtaPhi.first-1;
523 if (isNeighX && isNeighY && validEta && validPhi){
525 Identifier neigh_ID_X = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
526 channelType, padEtaPhi.first, newPhi,
isValid);
527 Identifier neigh_ID_Y = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
528 channelType, newEta, padEtaPhi.second,
isValid);
529 Identifier neigh_ID_XY = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
530 channelType, newEta, newPhi,
isValid);
535 addDigit(digits, neigh_ID_X, bctag, sDigitTimePad, xQfraction*(1.-yQfraction)*0.5*total_charge);
536 addDigit(digits, neigh_ID_Y, bctag, sDigitTimePad, yQfraction*(1.-xQfraction)*0.5*total_charge);
537 addDigit(digits, neigh_ID_XY, bctag, sDigitTimePad, xQfraction*yQfraction*0.5*total_charge);
538 addDigit(digits, newId, bctag, sDigitTimePad, (1.-xQfraction-yQfraction+xQfraction*yQfraction)*0.5*total_charge);
539 }
else if (isNeighX && validPhi){
541 Identifier neigh_ID = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
542 channelType, padEtaPhi.first, newPhi,
isValid);
547 addDigit(digits, newId, bctag, sDigitTimePad, (1.-xQfraction)*0.5*total_charge);
548 addDigit(digits, neigh_ID, bctag, sDigitTimePad, xQfraction*0.5*total_charge);
550 else if (isNeighY && validEta){
552 Identifier neigh_ID = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
553 channelType, newEta, padEtaPhi.second,
isValid);
558 addDigit(digits, newId, bctag, sDigitTimePad, (1.-yQfraction)*0.5*total_charge);
559 addDigit(digits, neigh_ID, bctag, sDigitTimePad, yQfraction*0.5*total_charge);
565 addDigit(digits, newId, bctag, sDigitTimePad, 0.5*total_charge);
569 else if(padNumber != -1) {
587 channelType = sTgcIdHelper::sTgcChannelTypes::Wire;
590 Identifier WIREGP_ID = idHelper.channelID(layid, idHelper.multilayer(layid),
591 idHelper.gasGap(layid), channelType, 1);
599 int wiregroupNumber = detEl->
stripNumber(posOnSurf_wire, WIREGP_ID);
600 if( wiregroupNumber == -1 ){
602 const double new_posX = (posOnSurf_wire.x() > 0.0)? posOnSurf_wire.x() - length_tolerance
603 : posOnSurf_wire.x() + length_tolerance;
604 const Amg::Vector2D new_position(new_posX, posOnSurf_wire.y());
605 wiregroupNumber = detEl->
stripNumber(new_position, WIREGP_ID);
607 if (wiregroupNumber < 1) {
609 ATH_MSG_WARNING(
"Position on wire surface = (" << posOnSurf_wire.x() <<
", " << posOnSurf_wire.y() <<
")");
615 newId = idHelper.channelID(layid, idHelper.multilayer(layid), idHelper.gasGap(layid),
616 channelType, wiregroupNumber,
isValid);
620 if(wiregroupNumber>=1&&wiregroupNumber<=NumberOfWiregroups) {
621 addDigit(digits, newId, bctag, sDigitTimeWire, total_charge);
624 else if (wiregroupNumber != -1){
645 constexpr
double min_length = 0.1;
651 if (wireNumber < 1)
return ionization;
654 if (wireNumber > detEl->
numberOfWires(
id))
return ionization;
669 const double wire_pitch = detEl->
wirePitch();
671 const double wire_posX = detEl->
positionFirstWire(
id) + (wireNumber - 1) * wire_pitch;
672 const Amg::Vector3D wire_position(wire_posX, postStepPos.y(), 0.);
678 const double seg_length = hit_direction.mag();
679 if (seg_length >
tolerance) hit_direction /= seg_length;
682 if (seg_length < min_length) {
685 ionization.
distance = std::hypot(postStepPos.x() - wire_posX, postStepPos.z());
690 const double cos_theta = wire_direction.dot(hit_direction);
692 const Amg::Vector3D dist_wire_hit = postStepPos - wire_position;
695 if (std::abs(cos_theta - 1.0) <
tolerance) {
696 ATH_MSG_DEBUG(
"The track segment is parallel to the wire, position of digit is undefined");
697 ionization.
posOnSegment = 0.5 * (postStepPos + preStepPos);
705 const double sin_theta_2 = 1.0 - cos_theta * cos_theta;
708 const double factor_hit = (-dist_wire_hit.dot(hit_direction)
709 + dist_wire_hit.dot(wire_direction) * cos_theta) / sin_theta_2;
710 Amg::Vector3D ionization_pos = postStepPos + factor_hit * hit_direction;
716 if (factor_hit < seg_length) {
718 const double factor_wire = (dist_wire_hit.dot(wire_direction)
719 - dist_wire_hit.dot(hit_direction) * cos_theta) / sin_theta_2;
720 pos_on_wire = wire_position + factor_wire * wire_direction;
722 ionization_pos = preStepPos;
723 pos_on_wire = wire_position - (seg_length * cos_theta) * wire_direction;
727 ionization.
distance = (ionization_pos - pos_on_wire).
mag();
738 const double digittime,
742 if (std::find_if(digits.begin(),digits.end(), [&](std::unique_ptr<sTgcDigit>&
known) {
743 return known->identify() == id && std::abs(digittime - known->time()) < tolerance;
744 }) == digits.end()) {
745 digits.push_back(std::make_unique<sTgcDigit>(
id, bctag, digittime,
charge, 0, 0));
752 const std::string
file_name =
"sTGC_Digitization_timeArrival.dat";
756 return StatusCode::FAILURE;
760 std::ifstream ifs{
file_path, std::ios::in};
763 return StatusCode::FAILURE;
770 while (std::getline(ifs,
line)) {
772 std::istringstream iss(
line);
774 if (
key.compare(
"bin") == 0) {
775 iss >> param.lowEdge >> param.kParameter >> param.thetaParameter;
777 }
else if (
key.compare(
"mpv") == 0) {
786 return StatusCode::SUCCESS;
796 if (
d <
par.lowEdge) {
818 const std::string
file_name =
"sTGC_Digitization_timeOffsetStrip.dat";
822 return StatusCode::FAILURE;
826 std::ifstream ifs{
file_path, std::ios::in};
829 return StatusCode::FAILURE;
841 while (std::getline(ifs,
line)) {
843 std::istringstream iss(
line);
845 if (
key.compare(
"strip") == 0) {
851 return StatusCode::SUCCESS;