@@ -72,10 +72,146 @@ bool TRACCC_HOST_DEVICE doublet_finding_helper::isCompatible(
7272 // actually zOrigin * deltaR to avoid division by 0 statements
7373 zOrigin = sp1.z () * deltaR - sp1.radius () * cotTheta;
7474 }
75- return ((deltaR < config.deltaRMax ) && (deltaR > config.deltaRMin ) &&
76- (math::fabs (cotTheta) < config.cotThetaMax * deltaR) &&
77- (zOrigin > config.collisionRegionMin * deltaR) &&
78- (zOrigin < config.collisionRegionMax * deltaR));
75+
76+ if ((deltaR >= config.deltaRMax ) || (deltaR <= config.deltaRMin ) ||
77+ (math::fabs (cotTheta) >= config.cotThetaMax * deltaR) ||
78+ (zOrigin <= config.collisionRegionMin * deltaR) ||
79+ (zOrigin >= config.collisionRegionMax * deltaR)) {
80+ return false ;
81+ }
82+
83+ /*
84+ * The following cut is capable of discriminating some doublets on the
85+ * basis that it is impossible to find a third spacepoint for the doublet
86+ * that will keep the resulting triplet inside the helix radius bound.
87+ * This explanation is enhanced with Geogebra commands with can be entered
88+ * into the application directly to provide a visual "proof" of why this
89+ * cut works.
90+ *
91+ * We will start by creating two spacepoints at arbitrary locations (they
92+ * can be moved as desired):
93+ *
94+ * ```
95+ * A = (2, 4)
96+ * B = (3, 12)
97+ * ```
98+ *
99+ * We will also define a radius $R$:
100+ *
101+ * ```
102+ * R = 10
103+ * ```
104+ *
105+ * Next, we consider the fact that two points and a radius define exactly
106+ * two circles through those points and with that radius. This makes sense
107+ * because three points (six degrees of freedom) precisely define a single
108+ * circle, and two points and a radius (five DoFs) define two circles. We
109+ * will construct those circles now, with the radius being the minimum
110+ * helix radius from the configuration.
111+ *
112+ * To find the midpoints of these circles, we will first find the
113+ * perpendicular bisector of points $A$ and $B$:
114+ *
115+ * ```
116+ * M = 0.5 * (A + B)
117+ * ```
118+ */
119+ scalar midX = 0 .5f * (sp1.x () + sp2.x ());
120+ scalar midY = 0 .5f * (sp1.y () + sp2.y ());
121+
122+ /*
123+ * Then we will compute the slope of the perpendicular bisector:
124+ *
125+ * ```
126+ * s = (y(B) - y(A)) / (x(B) - x(A))
127+ * ```
128+ */
129+ scalar slope = (sp2.y () - sp1.y ()) / (sp2.x () - sp1.x ());
130+
131+ /*
132+ * Next, we can simply place circle midpoints on our perpendicular
133+ * bisector, but we cannot simply use the radius $R$ as the distance from
134+ * the midpoint of $A$ and $B$, we have account for the length of the
135+ * sagitta:
136+ *
137+ * ```
138+ * dX = x(B) - x(A)
139+ * dY = y(B) - y(A)
140+ * dXY2 = dX * dX + dY * dY
141+ * q = sqrt(R * R - dXY2 / 4)
142+ * ```
143+ */
144+ scalar deltaX = sp2.x () - sp1.x ();
145+ scalar deltaY = sp2.y () - sp1.y ();
146+ scalar deltaXY2 = deltaX * deltaX + deltaY * deltaY;
147+ scalar sagittaLength = math::sqrt (
148+ config.minHelixRadius * config.minHelixRadius - deltaXY2 / 4 .f );
149+
150+ /*
151+ * We then compute the central angle between the points $A$, $B$, and the
152+ * midpoints of the circles we want to construct. Naively, this can be
153+ * done using the trigonomic functions:
154+ *
155+ * ```
156+ * theta = atan(1 / slope)
157+ * ```
158+ *
159+ * After which we can compute the delta between the midpoint of $A$ and
160+ * $B$ and the midpoints of our circles:
161+ *
162+ * ```
163+ * mdX = (R - q) * cos(theta)
164+ * mdY = (R - q) * sin(theta)
165+ * ```
166+ *
167+ * However, some trigonomy allows us to reduce this:
168+ *
169+ * ```
170+ * denom = sqrt((s * s + 1) / (s * s))
171+ * cosTheta = 1 / denom
172+ * sinTheta = -1 / (s * denom)
173+ * mdX = q * cosTheta
174+ * mdY = q * sinTheta
175+ * ```
176+ */
177+ scalar denom = math::sqrt ((slope * slope + 1 ) / (slope * slope));
178+ scalar cosCentralAngle = 1 .f / denom;
179+ scalar sinCentralAngle = -1 .f / (slope * denom);
180+
181+ scalar mpDeltaX = sagittaLength * cosCentralAngle;
182+ scalar mpDeltaY = sagittaLength * sinCentralAngle;
183+
184+ /*
185+ * We now easily find the midpoints of the required circles:
186+ *
187+ * ```
188+ * V = (x(M) + mdX, y(M) + mdY)
189+ * W = (x(M) - mdX, y(M) - mdY)
190+ * ```
191+ */
192+ scalar mp1X = midX + mpDeltaX;
193+ scalar mp2X = midX - mpDeltaX;
194+ scalar mp1Y = midY + mpDeltaY;
195+ scalar mp2Y = midY - mpDeltaY;
196+
197+ /*
198+ * Finally, we compute the radii of the circles. The crucial intuition
199+ * here is that if either of the newly constructed circles _completely_
200+ * contain a circle of radius $R$ around the origin, then we can never
201+ * find a third spacepoint to complete the triplet. Thus, we compute the
202+ * radii. We leave them squared in the C++ code to avoid an unnecessary
203+ * square root.
204+ */
205+ scalar mp1R2 = mp1X * mp1X + mp1Y * mp1Y;
206+ scalar mp2R2 = mp2X * mp2X + mp2Y * mp2Y;
207+
208+ if (math::min (mp1R2, mp2R2) <=
209+ ((config.minHelixRadius - config.impactMax ) *
210+ (config.minHelixRadius - config.impactMax ))) {
211+ return false ;
212+ }
213+
214+ return true ;
79215}
80216
81217template <details::spacepoint_type otherSpType, typename T1, typename T2>
0 commit comments