@@ -479,6 +479,50 @@ bool staveMidpointAndMirror(const Constants::StaveConfig& staveConfig, int stave
479479 return true ;
480480}
481481
482+ /*
483+ * FR4 + Cu end-of-stave card at the outer-radius tip of a disk stave.
484+ * Mirrors the TRK barrel card: an FR4 board with evenly spaced copper planes.
485+ * Local card axes in the (layer) mother frame: x = stave width, y = stave axis
486+ * (radial at the tip), z = beam. The card sits downstream of the stave (away
487+ * from the IP), just past the disk envelope, in front of the connection disk.
488+ */
489+ void FT3Module::addEndOfStaveCard (
490+ TGeoVolume* motherVolume, const std::string& name, int direction,
491+ unsigned volume_count, double x_mid, double y_tip, double z_sensor,
492+ bool isML, double cuThickness)
493+ {
494+ const Constants::EosCardParams& c = Constants::getEosCardParams (isML);
495+ if (cuThickness * c.nCopperLayers >= c.thickness ) {
496+ LOG (fatal) << " FT3 disk EoS card: Cu " << cuThickness << " cm x " << c.nCopperLayers
497+ << " planes does not fit in the " << c.thickness << " cm card" ;
498+ }
499+
500+ // FR4 board (x = width, y = thickness, z = length)
501+ TGeoVolume* cardVol = gGeoManager ->MakeBox (name.c_str (), getMedium (Materials::MaterialID::FR4 ),
502+ c.width / 2 , c.thickness / 2 , c.length / 2 );
503+ cardVol->SetLineColor (kGreen + 3 );
504+
505+ // copper planes spread over the thickness (y), outermost two flush with the faces
506+ TGeoVolume* planeVol = gGeoManager ->MakeBox ((name + " _Cu" ).c_str (), getMedium (Materials::MaterialID::Copper),
507+ c.width / 2 , cuThickness / 2 , c.length / 2 );
508+ planeVol->SetLineColor (kOrange + 7 );
509+ const double span = c.thickness - cuThickness;
510+ for (int i = 0 ; i < c.nCopperLayers ; ++i) {
511+ const double y = (c.nCopperLayers > 1 ) ? -span / 2 + i * span / (c.nCopperLayers - 1 ) : 0 .;
512+ cardVol->AddNode (planeVol, i, new TGeoTranslation (0 , y, 0 ));
513+ }
514+
515+ // z: IP-facing edge starts just after the sensors (z_sensor), body extends
516+ // downstream (away from the IP), toward the connection disk.
517+ // y: sit just radially OUTSIDE the stave outer tip so the long card runs past
518+ // the carbon stave along z without overlapping it.
519+ const double s = (direction == 1 ) ? 1.0 : -1.0 ; // downstream sign
520+ const double zCard = z_sensor + s * (c.zGap + c.length / 2 );
521+ const double yClear = 0.05 ; // radial clearance to the stave tip
522+ const double yCard = y_tip + (y_tip >= 0 ? 1.0 : -1.0 ) * (c.thickness / 2 + yClear);
523+ motherVolume->AddNode (cardVol, volume_count, new TGeoTranslation (x_mid, yCard, zCard));
524+ }
525+
482526/*
483527 * Create the carbon shell of one stave, staggered in z, plus the mirrored one
484528 * when the stave is built as two pieces.
@@ -509,6 +553,47 @@ void FT3Module::add_stave_volumes(
509553 staveConfig.y_lengths [i_stave], staveTriangles, absAllowedYRange,
510554 staveConfig.x_midpoints [i_stave], -y_midpoint, z_stave_shift_forward);
511555 }
556+
557+ // End-of-stave card at the outer-radius tip of each stave PIECE, placed
558+ // downstream of the stave in front of the connection disk. A whole stave gets
559+ // one card (the +y "top" tip). A split stave is built as two pieces (main +
560+ // mirror), each reaching the outer radius at opposite ends, so each piece gets
561+ // its own card at its outer tip (+y for the main, -y for the mirror).
562+ // Shared by the exact and greedy paths; the disk layer envelope is extended
563+ // downstream in FT3Layer::createLayer to contain the cards.
564+ auto & ft3Params = o2::ft3::FT3BaseParam::Instance ();
565+ if (ft3Params.addDiskEosCards ) {
566+ const bool isML = staveConfig.isML ;
567+ const double cuT = isML ? ft3Params.ft3EosCardCuThicknessML : ft3Params.ft3EosCardCuThicknessOT ;
568+ // Actual outer-radius tip of the stave: greedy clamps the stave to
569+ // absAllowedYRange.second, exact leaves it unbounded (DBL_MAX), so take the
570+ // smaller of the geometric end and the radial clamp.
571+ const double yTip = std::min (y_midpoint + staveConfig.y_lengths [i_stave] / 2 ,
572+ absAllowedYRange.second );
573+ // Per-stave sensor silicon z in the layer frame, reconstructed from the
574+ // carbon-face offset exactly as the sensor-placement loop does, so the card
575+ // starts right after the sensors.
576+ const double z_offset_to_silicon = z_offset_to_carbon_face +
577+ Constants::epoxyThickness + Constants::kaptonThickness +
578+ Constants::copperThickness + Constants::epoxyThickness +
579+ Constants::siliconThickness / 2 ;
580+ const double zOffMult = (direction == 1 ) ? -1.0 : 1.0 ;
581+ double zStaveShiftSensors = 0.0 ;
582+ if (!staveConfig.staveOnFront [i_stave]) {
583+ zStaveShiftSensors = (direction == 1 ) ? Constants::z_offsetStave (staveConfig.x_midpoint_spacing )
584+ : -Constants::z_offsetStave (staveConfig.x_midpoint_spacing );
585+ }
586+ const double zSensor = z_offset_to_silicon * zOffMult + zStaveShiftSensors;
587+ const std::string cardBase = " FT3_EOSdiskCard_" + std::to_string (direction) + " _" +
588+ std::to_string (layerNumber) + " _" + std::to_string (i_stave);
589+ addEndOfStaveCard (motherVolume, cardBase + " _pos" , direction, (*staveVolumeCount)++,
590+ staveConfig.x_midpoints [i_stave], +yTip, zSensor, isML, cuT);
591+ // split stave: the mirror piece reaches the outer radius at -y, give it a card too
592+ if (mirrorStaveAroundX) {
593+ addEndOfStaveCard (motherVolume, cardBase + " _neg" , direction, (*staveVolumeCount)++,
594+ staveConfig.x_midpoints [i_stave], -yTip, zSensor, isML, cuT);
595+ }
596+ }
512597}
513598
514599/*
0 commit comments