14 #include "GaudiKernel/MsgStream.h"
16 #include "CLHEP/Units/SystemOfUnits.h"
17 #include "CLHEP/Random/RandomEngine.h"
18 #include "CLHEP/Random/RandFlat.h"
19 #include "CLHEP/Random/RandGaussZiggurat.h"
20 #include "CLHEP/Random/RandGamma.h"
21 #include "CLHEP/Vector/ThreeVector.h"
35 const int channelTypes,
37 bool doPadChargeSharing,
38 double stripChargeScale,
39 bool applyAsBuiltBLines)
41 m_idHelperSvc{idHelperSvc},
42 m_channelTypes{channelTypes},
43 m_meanGasGain{meanGasGain},
44 m_doPadSharing{doPadChargeSharing},
45 m_stripChargeScale{stripChargeScale},
46 m_applyAsBuiltBLines(applyAsBuiltBLines) {}
63 ATH_MSG_ERROR(
"Invalid ChannelTypes : " <<
m_channelTypes <<
" (valid values are : 1 --> strips ; 2 --> strips / wires ; 3 --> strips / wires / pads)");
64 return StatusCode::FAILURE;
66 return StatusCode::SUCCESS;
82 const Identifier layid = idHelper.channelID(offlineId,
83 idHelper.multilayer(offlineId),
84 idHelper.gasGap(offlineId),
85 sTgcIdHelper::sTgcChannelTypes::Wire, 1);
95 if( !detEl ){
return {}; }
101 constexpr
double length_tolerance = 0.01;
117 Amg::Vector2D posOnSurf_wire{0.5 * (hitOnSurface_wire + pre_pos_wire_surf).block<2,1>(0,0)};
134 if ((wire_number < 1) || (wire_number > number_wires)) {
138 if ((wire_number < 1) || (wire_number > number_wires)) {
145 double dist_wire = ionization.
distance;
146 if (dist_wire > 0.) {
156 double dist_wire_adj = ion_adj.
distance;
157 if ((dist_wire_adj > 0.) && (dist_wire_adj < dist_wire)) {
158 dist_wire = dist_wire_adj;
160 ionization = std::move(ion_adj);
163 ATH_MSG_DEBUG(
"Failed to get the distance between the wire number = " << wire_number
165 <<
". Number of wires = " << number_wires
171 posOnSurf_wire = ionization.
posOnWire.block<2,1>(0,0);
189 const double wire_pitch = detEl->
wirePitch();
190 if ((dist_wire > 0.) && (std::abs(hit.
particleEncoding()) == 13) && (dist_wire > (wire_pitch/2))) {
191 ATH_MSG_DEBUG(
"Distance to the nearest wire (" << dist_wire <<
") is greater than expected.");
201 if (dist_wire > wire_pitch) {
211 double gamma_mpv = (par_kappa - 1) * par_theta;
213 if (gamma_mpv < 0.) {gamma_mpv = 0.;}
214 const double t0_par = most_prob_time - gamma_mpv;
221 double digit_time = t0_par + CLHEP::RandGamma::shoot(condContainers.
rndmEngine, par_kappa, 1/par_theta);
227 constexpr
int shoot_limit = 4;
228 int shoot_counter = 0;
229 while (digit_time < 0.) {
230 if (shoot_counter > shoot_limit) {
234 digit_time = t0_par + CLHEP::RandGamma::shoot(condContainers.
rndmEngine, par_kappa, 1/par_theta);
239 <<
", time = " << digit_time
240 <<
", k parameter = " << par_kappa
241 <<
", theta parameter = " << par_theta
242 <<
", most probable time = " << most_prob_time);
256 const double stripPropagationTime = 0.;
258 double sDigitTimeWire = digit_time;
259 double sDigitTimePad = sDigitTimeWire;
260 double sDigitTimeStrip = sDigitTimeWire + stripPropagationTime;
268 int channelType = sTgcIdHelper::sTgcChannelTypes::Strip;
270 Identifier newId = idHelper.channelID(layid,idHelper.multilayer(layid),
271 idHelper.gasGap(layid), channelType, 1,
isValid);
283 detEl->
spacePointPosition(newId, hitOnSurface_strip.x(), hitOnSurface_strip.y(), posAfterAsBuilt);
284 posOnSurf_strip = posAfterAsBuilt.block<2,1>(0,0);
286 posOnSurf_strip = hitOnSurface_strip.block<2,1>(0,0);
289 bool insideBounds = SURF_STRIP.
insideBounds(posOnSurf_strip);
297 int stripNumber = detEl->
stripNumber(posOnSurf_strip, newId);
298 if( stripNumber == -1 ){
300 const double new_posX = (posOnSurf_strip.x() > 0.0)? posOnSurf_strip.x() - length_tolerance
301 : posOnSurf_strip.x() + length_tolerance;
302 const Amg::Vector2D new_position(new_posX, posOnSurf_strip.y());
303 stripNumber = detEl->
stripNumber(new_position, newId);
305 if (stripNumber < 1) {
307 ATH_MSG_WARNING(
"Position on strip surface = (" << posOnSurf_strip.x() <<
", " << posOnSurf_strip.y() <<
")");
311 newId = idHelper.channelID(layid, idHelper.multilayer(layid),
312 idHelper.gasGap(layid), channelType, stripNumber,
isValid);
313 if(!
isValid && stripNumber != -1) {
319 const double stripHalfPitch = detEl->
channelPitch(newId)*0.5;
340 const double total_charge =
gain*ionized_charge;
345 const double tan_theta = GLODIRE.perp()/GLODIRE.z();
349 const double cluster_posX = posOnSurf_strip.x();
350 double peak_position = CLHEP::RandGaussZiggurat::shoot(condContainers.
rndmEngine, cluster_posX,
m_StripResolution*angle_dependency);
358 std::unique_ptr<TF1> clusterProfile = std::make_unique<TF1>(
"fgaus",
359 "[0]*exp(-0.5*(x/[1])^2)+[2]*exp(-0.5*(x/[3])^2)",
367 constexpr
double tolerance_charge = 0.0005;
369 constexpr
unsigned int max_neighbor = 10;
372 for (
unsigned int iStrip = 0; iStrip <= max_neighbor; ++iStrip) {
373 int currentStrip = stripNumber + iStrip;
374 if (currentStrip > NumberOfStrips)
break;
377 newId = idHelper.channelID(layid, idHelper.multilayer(layid), idHelper.gasGap(layid),
378 channelType, currentStrip,
isValid);
387 double x_relative = locpos.x() - peak_position;
389 double charge = clusterProfile->Integral(x_relative/(2*stripHalfPitch) - 0.5, x_relative/(2*stripHalfPitch) + 0.5);
391 if (
charge < tolerance_charge)
break;
394 double strip_time = sDigitTimeStrip;
399 int indexFromMiddleStrip = iStrip;
400 if ((iStrip > 0) && ((x_relative + (0.75 - iStrip * 2) * stripHalfPitch) < 0))
401 indexFromMiddleStrip = iStrip - 1;
409 <<
", time = " << strip_time <<
", time offset = " << strip_time-sDigitTimeStrip
410 <<
", neighbor index = " << iStrip
416 for (
unsigned int iStrip = 1; iStrip <= max_neighbor; ++iStrip) {
417 int currentStrip = stripNumber - iStrip;
418 if (currentStrip < 1)
break;
420 newId = idHelper.channelID(layid,idHelper.multilayer(layid),
421 idHelper.gasGap(layid), channelType, currentStrip,
isValid);
429 double x_relative = locpos.x() - peak_position;
430 double charge = clusterProfile->Integral(x_relative/(2*stripHalfPitch) - 0.5, x_relative/(2*stripHalfPitch) + 0.5);
431 if (
charge < tolerance_charge)
break;
434 double strip_time = sDigitTimeStrip;
437 int indexFromMiddleStrip = ((x_relative + (iStrip * 2 - 0.75) * stripHalfPitch) > 0)? iStrip - 1 : iStrip;
444 <<
", time = " << strip_time <<
", time offset = " << strip_time-sDigitTimeStrip
445 <<
", neighbor index = " << iStrip
460 channelType = sTgcIdHelper::sTgcChannelTypes::Pad;
462 Identifier PAD_ID = idHelper.channelID(layid, idHelper.multilayer(layid),
463 idHelper.gasGap(layid), channelType, 1);
469 const Amg::Vector2D posOnSurf_pad(hitOnSurface_pad.x(), hitOnSurface_pad.y());
475 int padNumber = detEl->
stripNumber(posOnSurf_pad, PAD_ID);
476 if( padNumber == -1 ){
478 const double new_posX = (posOnSurf_pad.x()>0.0)? posOnSurf_pad.x()-length_tolerance
479 : posOnSurf_pad.x()+length_tolerance;
480 const double new_posY = (posOnSurf_pad.y()>0.0)? posOnSurf_pad.y()-length_tolerance
481 : posOnSurf_pad.y()+length_tolerance;
483 padNumber = detEl->
stripNumber(new_position, PAD_ID);
487 ATH_MSG_WARNING(
"Position on pad surface = (" << posOnSurf_pad.x() <<
", " << posOnSurf_pad.y() <<
")");
491 newId = idHelper.channelID(layid, idHelper.multilayer(layid),
492 idHelper.gasGap(layid), channelType, padNumber,
isValid);
510 double deltaX = halfPadWidthX - std::abs(
diff.x());
511 double deltaY= halfPadWidthY - std::abs(
diff.y());
523 int newPhi =
diff.x() > 0 ? padEtaPhi.second-1 : padEtaPhi.second+1;
524 int newEta =
diff.y() > 0 ? padEtaPhi.first+1 : padEtaPhi.first-1;
528 if (isNeighX && isNeighY && validEta && validPhi){
530 Identifier neigh_ID_X = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
531 channelType, padEtaPhi.first, newPhi,
isValid);
532 Identifier neigh_ID_Y = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
533 channelType, newEta, padEtaPhi.second,
isValid);
534 Identifier neigh_ID_XY = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
535 channelType, newEta, newPhi,
isValid);
540 addDigit(digits, neigh_ID_X, bctag, sDigitTimePad, xQfraction*(1.-yQfraction)*0.5*total_charge);
541 addDigit(digits, neigh_ID_Y, bctag, sDigitTimePad, yQfraction*(1.-xQfraction)*0.5*total_charge);
542 addDigit(digits, neigh_ID_XY, bctag, sDigitTimePad, xQfraction*yQfraction*0.5*total_charge);
543 addDigit(digits, newId, bctag, sDigitTimePad, (1.-xQfraction-yQfraction+xQfraction*yQfraction)*0.5*total_charge);
544 }
else if (isNeighX && validPhi){
546 Identifier neigh_ID = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
547 channelType, padEtaPhi.first, newPhi,
isValid);
552 addDigit(digits, newId, bctag, sDigitTimePad, (1.-xQfraction)*0.5*total_charge);
553 addDigit(digits, neigh_ID, bctag, sDigitTimePad, xQfraction*0.5*total_charge);
555 else if (isNeighY && validEta){
557 Identifier neigh_ID = idHelper.padID(newId, idHelper.multilayer(newId), idHelper.gasGap(newId),
558 channelType, newEta, padEtaPhi.second,
isValid);
563 addDigit(digits, newId, bctag, sDigitTimePad, (1.-yQfraction)*0.5*total_charge);
564 addDigit(digits, neigh_ID, bctag, sDigitTimePad, yQfraction*0.5*total_charge);
570 addDigit(digits, newId, bctag, sDigitTimePad, 0.5*total_charge);
574 else if(padNumber != -1) {
592 channelType = sTgcIdHelper::sTgcChannelTypes::Wire;
595 Identifier WIREGP_ID = idHelper.channelID(layid, idHelper.multilayer(layid),
596 idHelper.gasGap(layid), channelType, 1);
604 int wiregroupNumber = detEl->
stripNumber(posOnSurf_wire, WIREGP_ID);
605 if( wiregroupNumber == -1 ){
607 const double new_posX = (posOnSurf_wire.x() > 0.0)? posOnSurf_wire.x() - length_tolerance
608 : posOnSurf_wire.x() + length_tolerance;
609 const Amg::Vector2D new_position(new_posX, posOnSurf_wire.y());
610 wiregroupNumber = detEl->
stripNumber(new_position, WIREGP_ID);
612 if (wiregroupNumber < 1) {
614 ATH_MSG_WARNING(
"Position on wire surface = (" << posOnSurf_wire.x() <<
", " << posOnSurf_wire.y() <<
")");
620 newId = idHelper.channelID(layid, idHelper.multilayer(layid), idHelper.gasGap(layid),
621 channelType, wiregroupNumber,
isValid);
625 if(wiregroupNumber>=1&&wiregroupNumber<=NumberOfWiregroups) {
626 addDigit(digits, newId, bctag, sDigitTimeWire, total_charge);
629 else if (wiregroupNumber != -1){
650 constexpr
double min_length = 0.1;
656 if (wireNumber < 1)
return ionization;
659 if (wireNumber > detEl->
numberOfWires(
id))
return ionization;
674 const double wire_pitch = detEl->
wirePitch();
676 const double wire_posX = detEl->
positionFirstWire(
id) + (wireNumber - 1) * wire_pitch;
677 const Amg::Vector3D wire_position(wire_posX, postStepPos.y(), 0.);
683 const double seg_length = hit_direction.mag();
684 if (seg_length >
tolerance) hit_direction /= seg_length;
687 if (seg_length < min_length) {
690 ionization.
distance = std::hypot(postStepPos.x() - wire_posX, postStepPos.z());
695 const double cos_theta = wire_direction.dot(hit_direction);
697 const Amg::Vector3D dist_wire_hit = postStepPos - wire_position;
700 if (std::abs(cos_theta - 1.0) <
tolerance) {
701 ATH_MSG_DEBUG(
"The track segment is parallel to the wire, position of digit is undefined");
702 ionization.
posOnSegment = 0.5 * (postStepPos + preStepPos);
710 const double sin_theta_2 = 1.0 - cos_theta * cos_theta;
713 const double factor_hit = (-dist_wire_hit.dot(hit_direction)
714 + dist_wire_hit.dot(wire_direction) * cos_theta) / sin_theta_2;
715 Amg::Vector3D ionization_pos = postStepPos + factor_hit * hit_direction;
721 if (factor_hit < seg_length) {
723 const double factor_wire = (dist_wire_hit.dot(wire_direction)
724 - dist_wire_hit.dot(hit_direction) * cos_theta) / sin_theta_2;
725 pos_on_wire = wire_position + factor_wire * wire_direction;
727 ionization_pos = preStepPos;
728 pos_on_wire = wire_position - (seg_length * cos_theta) * wire_direction;
732 ionization.
distance = (ionization_pos - pos_on_wire).
mag();
743 const double digittime,
747 if (std::find_if(digits.begin(),digits.end(), [&](std::unique_ptr<sTgcDigit>&
known) {
748 return known->identify() == id && std::abs(digittime - known->time()) < tolerance;
749 }) == digits.end()) {
750 digits.push_back(std::make_unique<sTgcDigit>(
id, bctag, digittime,
charge, 0, 0));
757 const std::string
file_name =
"sTGC_Digitization_timeArrival.dat";
761 return StatusCode::FAILURE;
765 std::ifstream ifs{
file_path, std::ios::in};
768 return StatusCode::FAILURE;
775 while (std::getline(ifs,
line)) {
777 std::istringstream iss(
line);
779 if (
key.compare(
"bin") == 0) {
780 iss >> param.lowEdge >> param.kParameter >> param.thetaParameter;
782 }
else if (
key.compare(
"mpv") == 0) {
791 return StatusCode::SUCCESS;
801 if (
d <
par.lowEdge) {
823 const std::string
file_name =
"sTGC_Digitization_timeOffsetStrip.dat";
827 return StatusCode::FAILURE;
831 std::ifstream ifs{
file_path, std::ios::in};
834 return StatusCode::FAILURE;
846 while (std::getline(ifs,
line)) {
848 std::istringstream iss(
line);
850 if (
key.compare(
"strip") == 0) {
856 return StatusCode::SUCCESS;