All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ThreeViewTransverseTracksAlgorithm.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArThreeDReco/LArTransverseTrackMatching/ThreeViewTransverseTracksAlgorithm.cc
3  *
4  * @brief Implementation of the three view transverse tracks algorithm class.
5  *
6  * $Log: $
7  */
8 
9 #include "Pandora/AlgorithmHeaders.h"
10 
13 
15 
16 using namespace pandora;
17 
18 namespace lar_content
19 {
20 
21 ThreeViewTransverseTracksAlgorithm::ThreeViewTransverseTracksAlgorithm() :
22  m_nMaxTensorToolRepeats(1000),
23  m_maxFitSegmentIndex(50),
24  m_pseudoChi2Cut(3.f),
25  m_minSegmentMatchedFraction(0.1f),
26  m_minSegmentMatchedPoints(3),
27  m_minOverallMatchedFraction(0.5f),
28  m_minOverallMatchedPoints(10),
29  m_minSamplingPointsPerLayer(0.1f)
30 {
31 }
32 
33 //------------------------------------------------------------------------------------------------------------------------------------------
34 
35 void ThreeViewTransverseTracksAlgorithm::CalculateOverlapResult(const Cluster *const pClusterU, const Cluster *const pClusterV, const Cluster *const pClusterW)
36 {
37  TransverseOverlapResult overlapResult;
38  PANDORA_THROW_RESULT_IF_AND_IF(
39  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, this->CalculateOverlapResult(pClusterU, pClusterV, pClusterW, overlapResult));
40 
41  if (overlapResult.IsInitialized())
42  this->GetMatchingControl().GetOverlapTensor().SetOverlapResult(pClusterU, pClusterV, pClusterW, overlapResult);
43 }
44 
45 //------------------------------------------------------------------------------------------------------------------------------------------
46 
48  const Cluster *const pClusterU, const Cluster *const pClusterV, const Cluster *const pClusterW, TransverseOverlapResult &overlapResult)
49 {
50  const TwoDSlidingFitResult &slidingFitResultU(this->GetCachedSlidingFitResult(pClusterU));
51  const TwoDSlidingFitResult &slidingFitResultV(this->GetCachedSlidingFitResult(pClusterV));
52  const TwoDSlidingFitResult &slidingFitResultW(this->GetCachedSlidingFitResult(pClusterW));
53 
54  FitSegmentTensor fitSegmentTensor;
55  this->GetFitSegmentTensor(slidingFitResultU, slidingFitResultV, slidingFitResultW, fitSegmentTensor);
56 
57  TransverseOverlapResult transverseOverlapResult;
58  this->GetBestOverlapResult(fitSegmentTensor, transverseOverlapResult);
59 
60  if (!transverseOverlapResult.IsInitialized())
61  return STATUS_CODE_NOT_FOUND;
62 
63  if ((transverseOverlapResult.GetMatchedFraction() < m_minOverallMatchedFraction) ||
64  (transverseOverlapResult.GetNMatchedSamplingPoints() < m_minOverallMatchedPoints))
65  return STATUS_CODE_NOT_FOUND;
66 
67  const int nLayersSpannedU(slidingFitResultU.GetMaxLayer() - slidingFitResultU.GetMinLayer());
68  const int nLayersSpannedV(slidingFitResultV.GetMaxLayer() - slidingFitResultV.GetMinLayer());
69  const int nLayersSpannedW(slidingFitResultW.GetMaxLayer() - slidingFitResultW.GetMinLayer());
70  const unsigned int meanLayersSpanned(
71  static_cast<unsigned int>((1.f / 3.f) * static_cast<float>(nLayersSpannedU + nLayersSpannedV + nLayersSpannedW)));
72 
73  if (0 == meanLayersSpanned)
74  return STATUS_CODE_FAILURE;
75 
76  const float nSamplingPointsPerLayer(static_cast<float>(transverseOverlapResult.GetNSamplingPoints()) / static_cast<float>(meanLayersSpanned));
77 
78  if (nSamplingPointsPerLayer < m_minSamplingPointsPerLayer)
79  return STATUS_CODE_NOT_FOUND;
80 
81  overlapResult = transverseOverlapResult;
82  return STATUS_CODE_SUCCESS;
83 }
84 
85 //------------------------------------------------------------------------------------------------------------------------------------------
86 
88  const TwoDSlidingFitResult &slidingFitResultV, const TwoDSlidingFitResult &slidingFitResultW, FitSegmentTensor &fitSegmentTensor) const
89 {
90  FitSegmentList fitSegmentListU(slidingFitResultU.GetFitSegmentList());
91  FitSegmentList fitSegmentListV(slidingFitResultV.GetFitSegmentList());
92  FitSegmentList fitSegmentListW(slidingFitResultW.GetFitSegmentList());
93 
94  for (unsigned int indexU = 0, indexUEnd = fitSegmentListU.size(); indexU < indexUEnd; ++indexU)
95  {
96  const FitSegment &fitSegmentU(fitSegmentListU.at(indexU));
97 
98  for (unsigned int indexV = 0, indexVEnd = fitSegmentListV.size(); indexV < indexVEnd; ++indexV)
99  {
100  const FitSegment &fitSegmentV(fitSegmentListV.at(indexV));
101 
102  for (unsigned int indexW = 0, indexWEnd = fitSegmentListW.size(); indexW < indexWEnd; ++indexW)
103  {
104  const FitSegment &fitSegmentW(fitSegmentListW.at(indexW));
105 
106  TransverseOverlapResult segmentOverlap;
107  if (STATUS_CODE_SUCCESS != this->GetSegmentOverlap(fitSegmentU, fitSegmentV, fitSegmentW, slidingFitResultU,
108  slidingFitResultV, slidingFitResultW, segmentOverlap))
109  continue;
110 
111  if ((segmentOverlap.GetMatchedFraction() < m_minSegmentMatchedFraction) ||
112  (segmentOverlap.GetNMatchedSamplingPoints() < m_minSegmentMatchedPoints))
113  continue;
114 
115  fitSegmentTensor[indexU][indexV][indexW] = segmentOverlap;
116  }
117  }
118  }
119 }
120 
121 //------------------------------------------------------------------------------------------------------------------------------------------
122 
123 StatusCode ThreeViewTransverseTracksAlgorithm::GetSegmentOverlap(const FitSegment &fitSegmentU, const FitSegment &fitSegmentV,
124  const FitSegment &fitSegmentW, const TwoDSlidingFitResult &slidingFitResultU, const TwoDSlidingFitResult &slidingFitResultV,
125  const TwoDSlidingFitResult &slidingFitResultW, TransverseOverlapResult &transverseOverlapResult) const
126 {
127  // Assess x-overlap
128  const float xSpanU(fitSegmentU.GetMaxX() - fitSegmentU.GetMinX());
129  const float xSpanV(fitSegmentV.GetMaxX() - fitSegmentV.GetMinX());
130  const float xSpanW(fitSegmentW.GetMaxX() - fitSegmentW.GetMinX());
131 
132  const float minX(std::max(fitSegmentU.GetMinX(), std::max(fitSegmentV.GetMinX(), fitSegmentW.GetMinX())));
133  const float maxX(std::min(fitSegmentU.GetMaxX(), std::min(fitSegmentV.GetMaxX(), fitSegmentW.GetMaxX())));
134  const float xOverlap(maxX - minX);
135 
136  if (xOverlap < std::numeric_limits<float>::epsilon())
137  return STATUS_CODE_NOT_FOUND;
138 
139  // Sampling in x
140  const float nPointsU(std::fabs((xOverlap / xSpanU) * static_cast<float>(fitSegmentU.GetEndLayer() - fitSegmentU.GetStartLayer())));
141  const float nPointsV(std::fabs((xOverlap / xSpanV) * static_cast<float>(fitSegmentV.GetEndLayer() - fitSegmentV.GetStartLayer())));
142  const float nPointsW(std::fabs((xOverlap / xSpanW) * static_cast<float>(fitSegmentW.GetEndLayer() - fitSegmentW.GetStartLayer())));
143 
144  const unsigned int nPoints(1 + static_cast<unsigned int>((nPointsU + nPointsV + nPointsW) / 3.f));
145 
146  // Chi2 calculations
147  float pseudoChi2Sum(0.f);
148  unsigned int nSamplingPoints(0), nMatchedSamplingPoints(0);
149 
150  for (unsigned int n = 0; n <= nPoints; ++n)
151  {
152  const float x(minX + (maxX - minX) * static_cast<float>(n) / static_cast<float>(nPoints));
153 
154  CartesianVector fitUVector(0.f, 0.f, 0.f), fitVVector(0.f, 0.f, 0.f), fitWVector(0.f, 0.f, 0.f);
155  CartesianVector fitUDirection(0.f, 0.f, 0.f), fitVDirection(0.f, 0.f, 0.f), fitWDirection(0.f, 0.f, 0.f);
156 
157  if ((STATUS_CODE_SUCCESS != slidingFitResultU.GetTransverseProjection(x, fitSegmentU, fitUVector, fitUDirection)) ||
158  (STATUS_CODE_SUCCESS != slidingFitResultV.GetTransverseProjection(x, fitSegmentV, fitVVector, fitVDirection)) ||
159  (STATUS_CODE_SUCCESS != slidingFitResultW.GetTransverseProjection(x, fitSegmentW, fitWVector, fitWDirection)))
160  {
161  continue;
162  }
163 
164  const float u(fitUVector.GetZ()), v(fitVVector.GetZ()), w(fitWVector.GetZ());
165  const float uv2w(LArGeometryHelper::MergeTwoPositions(this->GetPandora(), TPC_VIEW_U, TPC_VIEW_V, u, v));
166  const float uw2v(LArGeometryHelper::MergeTwoPositions(this->GetPandora(), TPC_VIEW_U, TPC_VIEW_W, u, w));
167  const float vw2u(LArGeometryHelper::MergeTwoPositions(this->GetPandora(), TPC_VIEW_V, TPC_VIEW_W, v, w));
168 
169  ++nSamplingPoints;
170  const float deltaU((vw2u - u) * fitUDirection.GetX());
171  const float deltaV((uw2v - v) * fitVDirection.GetX());
172  const float deltaW((uv2w - w) * fitWDirection.GetX());
173 
174  const float pseudoChi2(deltaW * deltaW + deltaV * deltaV + deltaU * deltaU);
175  pseudoChi2Sum += pseudoChi2;
176 
177  if (pseudoChi2 < m_pseudoChi2Cut)
178  ++nMatchedSamplingPoints;
179  }
180 
181  if (0 == nSamplingPoints)
182  return STATUS_CODE_NOT_FOUND;
183 
184  const XOverlap xOverlapObject(fitSegmentU.GetMinX(), fitSegmentU.GetMaxX(), fitSegmentV.GetMinX(), fitSegmentV.GetMaxX(),
185  fitSegmentW.GetMinX(), fitSegmentW.GetMaxX(), xOverlap);
186 
187  transverseOverlapResult = TransverseOverlapResult(nMatchedSamplingPoints, nSamplingPoints, pseudoChi2Sum, xOverlapObject);
188  return STATUS_CODE_SUCCESS;
189 }
190 
191 //------------------------------------------------------------------------------------------------------------------------------------------
192 
194  const FitSegmentTensor &fitSegmentTensor, TransverseOverlapResult &bestTransverseOverlapResult) const
195 {
196  if (fitSegmentTensor.empty())
197  {
198  bestTransverseOverlapResult = TransverseOverlapResult();
199  return;
200  }
201 
202  unsigned int maxIndexU(0), maxIndexV(0), maxIndexW(0);
203  for (FitSegmentTensor::const_iterator tIter = fitSegmentTensor.begin(), tIterEnd = fitSegmentTensor.end(); tIter != tIterEnd; ++tIter)
204  {
205  maxIndexU = std::max(tIter->first, maxIndexU);
206 
207  for (FitSegmentMatrix::const_iterator mIter = tIter->second.begin(), mIterEnd = tIter->second.end(); mIter != mIterEnd; ++mIter)
208  {
209  maxIndexV = std::max(mIter->first, maxIndexV);
210 
211  for (FitSegmentToOverlapResultMap::const_iterator rIter = mIter->second.begin(), rIterEnd = mIter->second.end(); rIter != rIterEnd; ++rIter)
212  maxIndexW = std::max(rIter->first, maxIndexW);
213  }
214  }
215 
216  // ATTN Protect against longitudinal tracks winding back and forth; can cause large number of memory allocations
217  maxIndexU = std::min(m_maxFitSegmentIndex, maxIndexU);
218  maxIndexV = std::min(m_maxFitSegmentIndex, maxIndexV);
219  maxIndexW = std::min(m_maxFitSegmentIndex, maxIndexW);
220 
221  FitSegmentTensor fitSegmentSumTensor;
222  for (unsigned int indexU = 0; indexU <= maxIndexU; ++indexU)
223  {
224  for (unsigned int indexV = 0; indexV <= maxIndexV; ++indexV)
225  {
226  for (unsigned int indexW = 0; indexW <= maxIndexW; ++indexW)
227  {
228  TransverseOverlapResultVector transverseOverlapResultVector;
229  this->GetPreviousOverlapResults(indexU, indexV, indexW, fitSegmentSumTensor, transverseOverlapResultVector);
230 
231  TransverseOverlapResultVector::const_iterator maxElement =
232  std::max_element(transverseOverlapResultVector.begin(), transverseOverlapResultVector.end());
233  TransverseOverlapResult maxTransverseOverlapResult(
234  (transverseOverlapResultVector.end() != maxElement) ? *maxElement : TransverseOverlapResult());
235 
236  TransverseOverlapResult thisOverlapResult;
237  if (fitSegmentTensor.count(indexU) && (fitSegmentTensor.find(indexU))->second.count(indexV) &&
238  ((fitSegmentTensor.find(indexU))->second.find(indexV))->second.count(indexW))
239  thisOverlapResult = (((fitSegmentTensor.find(indexU))->second.find(indexV))->second.find(indexW))->second;
240 
241  if (!thisOverlapResult.IsInitialized() && !maxTransverseOverlapResult.IsInitialized())
242  continue;
243 
244  fitSegmentSumTensor[indexU][indexV][indexW] = thisOverlapResult + maxTransverseOverlapResult;
245  }
246  }
247  }
248 
249  for (unsigned int indexU = 0; indexU <= maxIndexU; ++indexU)
250  {
251  for (unsigned int indexV = 0; indexV <= maxIndexV; ++indexV)
252  {
253  for (unsigned int indexW = 0; indexW <= maxIndexW; ++indexW)
254  {
255  const TransverseOverlapResult &transverseOverlapResult(fitSegmentSumTensor[indexU][indexV][indexW]);
256 
257  if (!transverseOverlapResult.IsInitialized())
258  continue;
259 
260  if (transverseOverlapResult > bestTransverseOverlapResult)
261  bestTransverseOverlapResult = transverseOverlapResult;
262  }
263  }
264  }
265 }
266 
267 //------------------------------------------------------------------------------------------------------------------------------------------
268 
269 void ThreeViewTransverseTracksAlgorithm::GetPreviousOverlapResults(const unsigned int indexU, const unsigned int indexV,
270  const unsigned int indexW, FitSegmentTensor &fitSegmentSumTensor, TransverseOverlapResultVector &transverseOverlapResultVector) const
271 {
272  for (unsigned int iPermutation = 1; iPermutation < 8; ++iPermutation)
273  {
274  const bool decrementU((iPermutation >> 0) & 0x1);
275  const bool decrementV((iPermutation >> 1) & 0x1);
276  const bool decrementW((iPermutation >> 2) & 0x1);
277 
278  if ((decrementU && (0 == indexU)) || (decrementV && (0 == indexV)) || (decrementW && (0 == indexW)))
279  continue;
280 
281  const unsigned int newIndexU(decrementU ? indexU - 1 : indexU);
282  const unsigned int newIndexV(decrementV ? indexV - 1 : indexV);
283  const unsigned int newIndexW(decrementW ? indexW - 1 : indexW);
284 
285  const TransverseOverlapResult &transverseOverlapResult(fitSegmentSumTensor[newIndexU][newIndexV][newIndexW]);
286 
287  if (transverseOverlapResult.IsInitialized())
288  transverseOverlapResultVector.push_back(transverseOverlapResult);
289  }
290 
291  if (transverseOverlapResultVector.empty())
292  transverseOverlapResultVector.push_back(TransverseOverlapResult());
293 }
294 
295 //------------------------------------------------------------------------------------------------------------------------------------------
296 
298 {
299  unsigned int repeatCounter(0);
300 
301  for (TensorToolVector::const_iterator iter = m_algorithmToolVector.begin(), iterEnd = m_algorithmToolVector.end(); iter != iterEnd;)
302  {
303  if ((*iter)->Run(this, this->GetMatchingControl().GetOverlapTensor()))
304  {
305  iter = m_algorithmToolVector.begin();
306 
307  if (++repeatCounter > m_nMaxTensorToolRepeats)
308  break;
309  }
310  else
311  {
312  ++iter;
313  }
314  }
315 }
316 
317 //------------------------------------------------------------------------------------------------------------------------------------------
318 //------------------------------------------------------------------------------------------------------------------------------------------
319 
320 StatusCode ThreeViewTransverseTracksAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
321 {
322  AlgorithmToolVector algorithmToolVector;
323  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ProcessAlgorithmToolList(*this, xmlHandle, "TrackTools", algorithmToolVector));
324 
325  for (AlgorithmToolVector::const_iterator iter = algorithmToolVector.begin(), iterEnd = algorithmToolVector.end(); iter != iterEnd; ++iter)
326  {
327  TransverseTensorTool *const pTransverseTensorTool(dynamic_cast<TransverseTensorTool *>(*iter));
328 
329  if (!pTransverseTensorTool)
330  return STATUS_CODE_INVALID_PARAMETER;
331 
332  m_algorithmToolVector.push_back(pTransverseTensorTool);
333  }
334 
335  PANDORA_RETURN_RESULT_IF_AND_IF(
336  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "NMaxTensorToolRepeats", m_nMaxTensorToolRepeats));
337 
338  PANDORA_RETURN_RESULT_IF_AND_IF(
339  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxFitSegmentIndex", m_maxFitSegmentIndex));
340 
341  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "PseudoChi2Cut", m_pseudoChi2Cut));
342 
343  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
344  XmlHelper::ReadValue(xmlHandle, "MinSegmentMatchedFraction", m_minSegmentMatchedFraction));
345 
346  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
347  XmlHelper::ReadValue(xmlHandle, "MinSegmentMatchedPoints", m_minSegmentMatchedPoints));
348 
349  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
350  XmlHelper::ReadValue(xmlHandle, "MinOverallMatchedFraction", m_minOverallMatchedFraction));
351 
352  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
353  XmlHelper::ReadValue(xmlHandle, "MinOverallMatchedPoints", m_minOverallMatchedPoints));
354 
355  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
356  XmlHelper::ReadValue(xmlHandle, "MinSamplingPointsPerLayer", m_minSamplingPointsPerLayer));
357 
358  return BaseAlgorithm::ReadSettings(xmlHandle);
359 }
360 
361 } // namespace lar_content
virtual pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
int GetStartLayer() const
Get start layer.
process_name opflash particleana ie x
TensorType & GetOverlapTensor()
Get the overlap tensor.
std::map< unsigned int, FitSegmentMatrix > FitSegmentTensor
bool IsInitialized() const
Whether the track overlap result has been initialized.
const TwoDSlidingFitResult & GetCachedSlidingFitResult(const pandora::Cluster *const pCluster) const
Get a sliding fit result from the algorithm cache.
float m_minSegmentMatchedFraction
The minimum segment matched sampling fraction to allow segment grouping.
float m_minOverallMatchedFraction
The minimum matched sampling fraction to allow particle creation.
std::vector< FitSegment > FitSegmentList
float m_pseudoChi2Cut
The pseudo chi2 cut to identify matched sampling points.
std::vector< TransverseOverlapResult > TransverseOverlapResultVector
void GetPreviousOverlapResults(const unsigned int indexU, const unsigned int indexV, const unsigned int indexW, FitSegmentTensor &fitSegmentSumTensor, TransverseOverlapResultVector &transverseOverlapResultVector) const
Get track overlap results for possible connected segments.
Header file for the geometry helper class.
float m_minSamplingPointsPerLayer
The minimum number of sampling points per layer to allow particle creation.
void SetOverlapResult(const pandora::Cluster *const pClusterU, const pandora::Cluster *const pClusterV, const pandora::Cluster *const pClusterW, const OverlapResult &overlapResult)
Set overlap result.
void CalculateOverlapResult(const pandora::Cluster *const pClusterU, const pandora::Cluster *const pClusterV, const pandora::Cluster *const pClusterW)
Calculate cluster overlap result and store in container.
Header file for the cluster helper class.
Header file for the three view transverse tracks algorithm class.
static float MergeTwoPositions(const pandora::Pandora &pandora, const pandora::HitType view1, const pandora::HitType view2, const float position1, const float position2)
Merge two views (U,V) to give a third view (Z).
const FitSegmentList & GetFitSegmentList() const
Get the fit segment list.
TransverseOverlapResult class.
unsigned int m_minSegmentMatchedPoints
The minimum number of matched segment sampling points to allow segment grouping.
createEngine this
TensorToolVector m_algorithmToolVector
The algorithm tool vector.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
required by fuzzyCluster table::sbnd_g4_services gaushitTruthMatch pandora
Definition: reco_sbnd.fcl:182
unsigned int m_maxFitSegmentIndex
The maximum number of fit segments used when identifying best overlap result.
double GetMinX() const
Get the minimum x value.
unsigned int m_minOverallMatchedPoints
The minimum number of matched segment sampling points to allow particle creation. ...
pandora::StatusCode GetTransverseProjection(const float x, const FitSegment &fitSegment, pandora::CartesianVector &position) const
Get projected position for a given input x coordinate and fit segment.
unsigned int m_nMaxTensorToolRepeats
The maximum number of repeat loops over tensor tools.
void GetFitSegmentTensor(const TwoDSlidingFitResult &slidingFitResultU, const TwoDSlidingFitResult &slidingFitResultV, const TwoDSlidingFitResult &slidingFitResultW, FitSegmentTensor &fitSegmentTensor) const
Get the number of matched points for three fit segments and accompanying sliding fit results...
void ExamineOverlapContainer()
Examine contents of overlap container, collect together best-matching 2D particles and modify cluster...
XOverlap class.
Definition: LArXOverlap.h:17
pandora::StatusCode GetSegmentOverlap(const FitSegment &fitSegmentU, const FitSegment &fitSegmentV, const FitSegment &fitSegmentW, const TwoDSlidingFitResult &slidingFitResultU, const TwoDSlidingFitResult &slidingFitResultV, const TwoDSlidingFitResult &slidingFitResultW, TransverseOverlapResult &transverseOverlapResult) const
Get the overlap result for three fit segments and the accompanying sliding fit results.
int GetEndLayer() const
Get end layer.
double GetMaxX() const
Get the maximum x value.
void GetBestOverlapResult(const FitSegmentTensor &fitSegmentTensor, TransverseOverlapResult &bestTransverseOverlapResult) const
Get the best overlap result, by examining the fit segment tensor.