2 Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
5 ///////////////////////////////////////////////////////////////////
6 // StaticEngine.icc, (c) ATLAS Detector software
7 ///////////////////////////////////////////////////////////////////
9 #include "TrkExInterfaces/INavigationEngine.h"
10 #include "TrkExInterfaces/IPropagationEngine.h"
11 #include "TrkExInterfaces/IMaterialEffectsEngine.h"
12 #include "TrkSurfaces/Surface.h"
13 #include "TrkGeometry/TrackingGeometry.h"
14 #include "TrkGeometry/TrackingVolume.h"
15 #include "TrkGeometry/Layer.h"
16 #include "TrkGeometry/MaterialLayer.h"
20 template <class T> Trk::ExtrapolationCode Trk::StaticEngine::extrapolateT(Trk::ExtrapolationCell<T>& eCell,
21 const Trk::Surface* sf,
22 Trk::PropDirection pDir,
23 const Trk::BoundaryCheck& bcheck) const
25 Trk::ExtrapolationCode eCode = Trk::ExtrapolationCode::InProgress;
26 // ---- [0] check the direct propagation exit
28 // obviously need a surface to exercise the fallback & need to be configured to do so
29 if (sf && eCell.checkConfigurationMode(Trk::ExtrapolationMode::Direct)){
30 EX_MSG_DEBUG(++eCell.navigationStep, "extrapolate", "", "direct extapolation in volume : " << eCell.leadVolume->volumeName());
31 // propagate to the surface, possible return codes are : SuccessPathLimit, SucessDestination, FailureDestination
32 eCode = m_propagationEngine->propagate(eCell,*sf,pDir,bcheck,eCell.destinationCurvilinear);
33 // eCode can be directly returned
36 EX_MSG_DEBUG(++eCell.navigationStep, "extrapolate", "", "extrapolation in static environment in volume : " << eCell.leadVolume->volumeName());
37 // evoke or finish the navigation initialization, possible return codes are:
38 // - InProgress : everything is fine, extrapolation in static volume is in progress
39 // - FailureNavigation : navigation setup could not be resolved, but reovery was not configured
40 // - Recovered : navigation setup could not be resolved, recovery by fallback to directly kicked in (and worked)
41 eCode = initNavigationT<T>(eCell,sf,pDir,bcheck);
42 CHECK_ECODE_CONTINUE(eCell, eCode);
43 // ----- [1] handle the ( leadLayer == endLayer )case :
45 // - this case does not need a layer to layer loop
46 if (sf && eCell.leadLayer == eCell.endLayer && eCell.initialVolume()){
47 // screen output for startLayer == endLayer
48 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "start and destination layer are identical -> jumping to final propagation.");
49 // set the leadLayerSurface to the parameters surface
50 eCell.leadLayerSurface = &(eCell.leadParameters->associatedSurface());
51 // resolve the layer, it is the final extrapolation
52 // - InProgress : layer resolving went without problem
53 // - SuccessPathLimit : path limit reached & configured to stop (rather unlikely within a layer)
54 // - SuccessMaterialLimit : material limit reached & configured to stop there
55 // if the lead layer is also the startLayer found by initialization -> no material handling
56 eCode = resolveLayerT<T>(eCell,
60 eCell.leadLayer->hasSubStructure(eCell.checkConfigurationMode(Trk::ExtrapolationMode::CollectSensitive)),
61 (eCell.leadLayer == eCell.startLayer && eCell.leadVolume == eCell.startVolume),
63 // Success triggers a return
64 CHECK_ECODE_SUCCESS_NODEST(eCell, eCode);
65 // extrapolation to destination was not successful
66 // - handle the return as configured (e.g. fallback)
67 return handleReturnT<T>(eCode, eCell, sf, pDir, bcheck);
69 // ----- [2] now do the layer-to-layer loop
71 // the volume returns the layers ordered by distance :
72 // - give potential start and end layer (latter only for the final volume)
73 // - start and end layer will be part of the loop
74 // - surface on approach is not yet resolved
75 const Trk::Layer* fLayer = eCell.finalVolumeReached() ? eCell.endLayer : 0;
76 auto layerIntersections = eCell.leadVolume->materialLayersOrdered(eCell.leadLayer, fLayer,
77 *eCell.leadParameters,
81 EX_MSG_VERBOSE(eCell.navigationStep, "layer", "loop", "found " << layerIntersections.size() << " layers for the layer-to-layer loop.");
82 // layer-to-layer loop starts here
83 for (auto& layerCandidate : layerIntersections ) {
84 // assign the leadLayer
85 eCell.leadLayer = layerCandidate.object;
86 // screen output for layer-to-layer loop
87 EX_MSG_VERBOSE(eCell.navigationStep, "layer", "loop", "processing layer with index : " << eCell.leadLayer->layerIndex().value());
88 // resolve the approach surface situation
89 // - it is the approaching surface for all layers but the very first one (where it's the parameter surface)
90 eCell.leadLayerSurface = (eCell.leadLayer == eCell.startLayer) ? &(eCell.leadParameters->associatedSurface()) :
91 &(eCell.leadLayer->surfaceOnApproach(eCell.leadParameters->position(),
92 eCell.leadParameters->momentum(),
96 // handle the layer, possible returns are :
97 // - InProgress : fine, whatever happened on the lead layer, may also be missed
98 // - SuccessWithPathLimit : propagation towards layer exceeded path limit
99 // - SuccessWithMaterialLimit : material interaction killed track
100 // - FailureDestination : destination was not hit appropriately
101 eCode = handleLayerT<T>(eCell, sf, pDir, bcheck);
102 EX_MSG_VERBOSE(eCell.navigationStep, "layer", layerCandidate.object->layerIndex().value(), "handleLayerT returned extrapolation code : " << eCode.toString());
103 // Possibilities are:
104 // - SuccessX -> return (via handleReturnT)
105 // - FailureX -> return (via handleReturnT that might evoke a fallback)
106 // - InProgess -> continue layer-to-layer loop
107 if (!eCode.inProgress()) return handleReturnT<T>(eCode, eCell, sf, pDir, bcheck);
109 // the layer 2 layer loop is done, the lead parameters are at the last valid option
110 // ----- [3] now resolve the boundary situation, call includes information wheather one is alreay at a boundary
112 // the navigaiton engine ca trigger different return codes
113 // - InProgress : fine, boundary surface has been found
114 // - SuccessWithPathLimit : propagation towards boundary surface exceeded path limit
115 // - FailureLoop/Navigation : problem in boundary resolving
116 eCode = m_navigationEngine->resolveBoundary(eCell, pDir);
117 // SuccessX and FailureX trigger a return
118 CHECK_ECODE_SUCCESS_NODEST(eCell, eCode);
119 // handle the return of the boudnary resolving
120 return handleReturnT<T>(eCode, eCell, sf, pDir, bcheck);
123 template <class T> Trk::ExtrapolationCode Trk::StaticEngine::initNavigationT(Trk::ExtrapolationCell<T>& eCell,
124 const Trk::Surface* sf,
125 Trk::PropDirection pDir,
126 Trk::BoundaryCheck bcheck) const
128 // initialize the Navigation stream ----------------------------------------------------------------------------------------
130 // this is the global initialization, it only associated direct objects
131 // detailed navigation search needs to be done by the sub engines (since they know best)
132 EX_MSG_DEBUG(++eCell.navigationStep, "navigation", "", "complete for static environment.");
133 // [A] the initial volume
134 if (eCell.startVolume == eCell.leadVolume && eCell.startLayer) {
135 // - found the initial start layer through association
136 EX_MSG_VERBOSE(eCell.navigationStep, "navigation", "", "this is the initial volume, everything set up already.");
137 // assigning it to the leadLayer
138 eCell.leadLayer = eCell.startLayer;
140 return Trk::ExtrapolationCode::InProgress;
142 // [B] any volume if we don't have a leadLayer
143 if (!eCell.leadLayer){
144 // - finding it through global search, never a boundary layer ... convention says that you update by exit
145 eCell.leadLayer = eCell.leadVolume->associatedLayer(eCell.leadParameters->position());
146 EX_MSG_VERBOSE(eCell.navigationStep, "navigation", "", "no start layer found yet, looking for it ..." << OH_CHECKFOUND(eCell.leadLayer) );
148 // [C] the final volume - everything's fine
149 if (eCell.leadVolume == eCell.endVolume && sf) {
150 if (eCell.endLayer) {
151 // the end layer had been found already by association
152 EX_MSG_VERBOSE(eCell.navigationStep, "navigation", "", "this is the final volume, everything set up already.");
153 return Trk::ExtrapolationCode::InProgress;
155 // make a straight line intersection
156 Trk::Intersection sfI = sf->straightLineIntersection(eCell.leadParameters->position(), pDir*eCell.leadParameters->momentum(), true);
157 // use this to find endVolume and endLayer
158 eCell.endLayer = eCell.leadVolume->associatedLayer(sfI.position);
159 // if you have a surface you need to require an end layer for the validation, otherwise you need to do a fallbac
160 return eCell.endLayer ? Trk::ExtrapolationCode::InProgress : handleReturnT<T>(Trk::ExtrapolationCode::FailureNavigation, eCell, sf, pDir, bcheck);
163 // return that you're in progress
164 return Trk::ExtrapolationCode::InProgress;
167 /** handle the layer */
168 template <class T> Trk::ExtrapolationCode Trk::StaticEngine::handleLayerT(Trk::ExtrapolationCell<T>& eCell,
169 const Trk::Surface* sf,
170 Trk::PropDirection pDir,
171 Trk::BoundaryCheck bcheck) const
173 Trk::ExtrapolationCode eCode = Trk::ExtrapolationCode::InProgress;
174 EX_MSG_DEBUG(++eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "handle this layer" );
175 // layer has sub structure - this can be (and the layer will tell you):
176 // - sensitive surface which should be tried to hit
177 // - material sub structure to be resolved (independent of sensitive surface)
178 bool hasSubStructure = eCell.leadLayer->hasSubStructure(eCell.checkConfigurationMode(Trk::ExtrapolationMode::CollectSensitive));
179 // [A] layer is a pure navigation layer and has no sub structure -> skip it, but only if it is not the final layer
180 if (!hasSubStructure && !eCell.leadLayer->layerIndex().value() && eCell.leadLayer != eCell.endLayer){
181 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "layer is a navigation layer -> skipping it ...");
182 return Trk::ExtrapolationCode::InProgress;
184 // [B] layer resolving is necessary -> resolve it
185 // - (a) layer has sub structure - this can be (and the layer will tell you):
186 // - sensitive surface which should be tried to hit
187 // - material sub structure to be resolved (independent of sensitive surface)
188 // - (b) layer is start layer (can not be if there was a volume switch)
189 bool isStartLayer = eCell.initialVolume() && eCell.leadLayer->onLayer(*eCell.leadParameters);
190 // - (c) layer is destination layer
191 // - final propagation to the layer and update if necessary
192 bool isDestinationLayer = (sf && eCell.leadLayer == eCell.endLayer);
193 // sub structure, start and destination need resolving of the layer setp
194 if (hasSubStructure || isStartLayer || isDestinationLayer ){
195 // screen output for sub strucutred layer
196 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "has sub structure, is start layer, or destination layer -> resolving it ..." );
197 // resolve the layer, it handles all possible return types and gives them directly to extrapolateT<T>
198 // - InProgress : layer resolving went without problem
199 // - SuccessPathLimit : path limit reached & configured to stop
200 // - SuccessMaterialLimit : material limit reached & configured to stop there
201 // - SuccessDestination : destination reached & everything is fine
202 return resolveLayerT<T>(eCell,sf,pDir,bcheck,hasSubStructure,isStartLayer,isDestinationLayer);
204 // [C] layer is a material layer without sub structure but material -> pass through
205 // no resolving ob sub structure to be done, an intermediate layer to be crossed
206 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "intermediate layer without sub structure -> passing through ...");
207 // propagate to it, possible return codes ( with the default of finalPropagation = false):
208 // - SuccessPathLimit : propagation to layer exceeded path limit
209 // - InProgress : layer was hit successfuly, try to handle the material and sub structure, these are new parameters
210 // - Recovered : layer was not hit, so can be ignored in the layer to layer loop
211 eCode = m_propagationEngine->propagate(eCell,*eCell.leadLayerSurface,pDir,true,eCell.navigationCurvilinear);
212 CHECK_ECODE_SUCCESS_NODEST(eCell, eCode);
213 // record the passive parameters
214 if (eCode!=Trk::ExtrapolationCode::Recovered) eCell.stepParameters(eCell.leadParameters, Trk::ExtrapolationMode::CollectPassive);
215 // check if the layer was actually hit
216 if (eCode.inProgress()){
217 // successful layer hit
218 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "has been succesful hit, handling material update.");
219 // layer has no sub-structure : it is an intermediate layer that just needs pass-throgh
220 // return possbilities:
221 // - InProgress : material update performed or not (depending on material)
222 // - SuccessMaterialLimit : material limit reached & configured to stop there
223 eCode = m_materialEffectsEngine->handleMaterial(eCell,pDir,Trk::fullUpdate);
224 CHECK_ECODE_CONTINUE(eCell, eCode);
225 // return the progress eCode back to the extrapolateT
228 // hit or not hit : it's always in progress since we are in the layer to layer loop
229 return Trk::ExtrapolationCode::InProgress;
233 /** main sub structure layer handling */
234 template <class T> Trk::ExtrapolationCode Trk::StaticEngine::resolveLayerT(Trk::ExtrapolationCell<T>& eCell,
235 const Trk::Surface* sf,
236 Trk::PropDirection pDir,
237 Trk::BoundaryCheck bcheck,
238 bool hasSubStructure,
240 bool isDestinationLayer) const
242 Trk::ExtrapolationCode eCode = Trk::ExtrapolationCode::InProgress;
243 EX_MSG_DEBUG(++eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "resolve it with" << (hasSubStructure ? " " : "out ") << "sub structure"
244 << (isDestinationLayer ? " -> destination layer." : (isStartLayer ? " -> start layer." : "") ) );
246 // cache the leadLayer - this is needed for the layer-to-layer loop not to be broken
247 const Trk::Layer* initialLayer = eCell.leadLayer;
248 // ----- [0] the start situation on the layer needs to be resolved:
249 // - either for sensitive parameters
250 // - or for material substructure
251 // [A] the layer is not the start layer and not the destination layer
252 // - the surfaceOnApproach() call should have sorted out that this is actually an approaching representation
253 // - the destination layer is excluded from the propagation because it can lead to punch-through to the other side of layers
254 if (!isStartLayer && !isDestinationLayer){
255 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "not the start layer (with sub structue), propagate to it.");
256 // propagate to the representing surface of this layer
257 // - InProgress : propagation to approaching surface worked - check material update
258 // - SuccessPathLimit : propagation to approaching surface reached the path limit
259 // - Recovered : layer was not hit, so can be ignored in the layer to layer loop
260 eCode = m_propagationEngine->propagate(eCell,*eCell.leadLayerSurface,pDir,true,eCell.sensitiveCurvilinear);
261 CHECK_ECODE_SUCCESS_NODEST(eCell,eCode);
262 // the extrapolation to the initial layer did not succeed - skip this layer in the layer-to-layer loop
263 if (eCode == Trk::ExtrapolationCode::Recovered) {
264 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "has not been hit, skipping it.");
265 return Trk::ExtrapolationCode::InProgress;
267 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "successfuly hit.");
268 // fill the corresponding parameters, the material effects updator can attach material to them
269 eCell.stepParameters(eCell.leadParameters, Trk::ExtrapolationMode::CollectPassive);
270 // the correct material layer needs to be assigned - in case of the approach surface not being hit, his can be the layer surface
271 if (eCell.leadLayerSurface->materialLayer() ||
272 (eCell.leadLayerSurface == &(eCell.leadLayer->surfaceRepresentation()) && eCell.leadLayer->layerMaterialProperties()) ){
273 // screen output for debugging
274 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "needs material update.");
275 // assign the right lead layer for the material update
276 if (eCell.leadLayerSurface->materialLayer())
277 eCell.leadLayer = eCell.leadLayerSurface->materialLayer();
278 // now handle the material (full update when passing approach surface), return codes are:
279 // - SuccessMaterialLimit : material limit reached, return back
280 // - InProgress : material update done or not (depending on the material description)
281 eCode = m_materialEffectsEngine->handleMaterial(eCell,pDir,Trk::fullUpdate);
282 CHECK_ECODE_CONTINUE(eCell, eCode);
284 } else if (isStartLayer) {
285 // [B] the layer is the start layer
286 // - let's check if a post update on the start surface has to be done
287 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "start layer (with sub structure), no propagation to be done.");
288 // the start surface could have a material layer attached
289 if (eCell.leadParameters->associatedSurface().materialLayer()){
290 eCell.leadLayer = eCell.leadParameters->associatedSurface().materialLayer();
291 // now handle the material (post update on start layer), return codes are:
292 // - SuccessMaterialLimit : material limit reached, return back
293 // - InProgress : material update done or not (depending on the material description)
294 eCode = m_materialEffectsEngine->handleMaterial(eCell,pDir,Trk::postUpdate);
295 CHECK_ECODE_CONTINUE(eCell, eCode);
296 // let's reset the lead layer
297 eCell.leadLayer = initialLayer;
300 // ----- [1] the sub structure of the layer needs to be resolved:
301 // resolve the substructure
302 std::vector<Trk::SurfaceIntersection> cSurfaces;
303 // this will give you the compatible surfaces of the layer : provided start and destination surface are excluded
304 // - surfaces without material are only provided if they are active and CollectSensitive is configured
305 // - surfaces with material are provided in order to make the necessary material update
306 size_t ncSurfaces = eCell.leadLayer->compatibleSurfaces(cSurfaces,
307 *eCell.leadParameters,
310 !eCell.checkConfigurationMode(Trk::ExtrapolationMode::CollectSensitive),
311 (isStartLayer ? &(eCell.leadParameters->associatedSurface()) : eCell.leadLayerSurface),
312 (isDestinationLayer ? sf : 0) );
313 // some screen output for the sub structure
314 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "found " << ncSurfaces << " sub structrue surfaces to test.");
315 // check if you have to do something
317 // now loop over the surfaces:
318 // the surfaces will be sorted
319 for (auto& csf : cSurfaces ) {
320 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "trying candidate surfaces with straight line path length " << csf.intersection.pathLength);
321 // propagate to the compatible surface, return types are
322 // - InProgress : propagation to compatible surface worked
323 // - Recovered : propagation to compatible surface did not work, leadParameters stay the same
324 // - SuccessPathLimit : propagation to compatible surface reached the path limit
325 eCode = m_propagationEngine->propagate(eCell,*(csf.object),pDir,true,eCell.sensitiveCurvilinear);
326 CHECK_ECODE_SUCCESS_NODEST(eCell,eCode);
327 // check if the propagation was successful
328 if (eCode.inProgress()){
329 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "successfully hit sub structure surface.");
330 // record the parameters as sensitive or passive depending on the surface
331 Trk::ExtrapolationMode::eMode emode = csf.object->isActive() ? Trk::ExtrapolationMode::CollectSensitive : Trk::ExtrapolationMode::CollectPassive;
332 // fill the corresponding parameters, the material effects updator can attach material to them
333 eCell.stepParameters(eCell.leadParameters, emode);
334 // check if the surface holds a material layer
335 // - yes : it is a sub surface that has a material layer attached
336 // - yes : it is any other layer for the material integration
337 if (csf.object->materialLayer() || ( csf.object->associatedLayer() && csf.object == &(csf.object->associatedLayer()->surfaceRepresentation()) ) ) {
338 // the resolved surface has material, set the leadLayer to
339 // - the materialLayer ( has higher priority than the associatedLayer() )
340 // - the associatedLayer ( it is the representing layer )
341 eCell.leadLayer = csf.object->materialLayer() ? csf.object->materialLayer() : csf.object->associatedLayer();
342 // now handle the material, return codes are:
343 // - SuccessMaterialLimit : material limit reached,return back
344 // - InProgress : material update done or not (depending on the material description)
345 eCode = m_materialEffectsEngine->handleMaterial(eCell,pDir,Trk::fullUpdate);
346 CHECK_ECODE_CONTINUE(eCell, eCode);
348 } else if (eCode== Trk::ExtrapolationCode::SuccessPathLimit) {
349 eCell.stepParameters(eCell.leadParameters, Trk::ExtrapolationMode::CollectPassive);
351 } // loop over test surfaces done
352 } // there are compatible surfaces
354 // ----- [3] the destination situation on the layer needs to be resolved:
355 // the layer is a destination layer
356 // - the final propagation call is indepenent of whether sub structure was resolved or not
357 // - the eCell.leadParameters are at the last possible parameters
358 if (sf && isDestinationLayer) {
359 // [B] the layer is start and destination layer but has no sub-structure
360 // -> propagation to destination surface
361 // (a) the starting layer is the same layer :
362 // - neither preUpdate nore postUpdate to be done, this is old-style within-layer extrapolation
363 // - material will be taken into account either when the layer was reached from another layer
364 // or when the layer is left to another destination
365 // (b) the starting layer is not the same layer :
366 // - apply the preUpdate on the parameters whein they reached the surface
367 // Possible return types:
368 // - SuccessDestination : great, desintation surface hit - but post-update needs to be done
369 // - SuccessPathLimit : pathlimit was reached on the way to the destination surface
370 eCode = m_propagationEngine->propagate(eCell,*sf,pDir,false,eCell.destinationCurvilinear);
371 // check for success return path limit
372 CHECK_ECODE_SUCCESS_NODEST(eCell,eCode);
373 EX_MSG_VERBOSE(eCell.navigationStep, "layer", eCell.leadLayer->layerIndex().value(), "attempt to hit destination surface resulted in " << eCode.toString() );
374 // check for a potential preUpdate
375 // - in case teh destination surface has material and the surface was hit
376 if ( sf->materialLayer() && eCode.isSuccess() ){
377 // the resolved surface has material, set the leadLayer to
378 eCell.leadLayer = sf->materialLayer();
379 // finally do the material update
380 // - this is the final call - still check for SuccessMaterialLimit
381 // the material effects updator usually returns inProgress, this needs to be ingored
382 m_materialEffectsEngine->handleMaterial(eCell,pDir,Trk::preUpdate);
383 // check if success was triggered through path limit reached on the way to the layer
384 CHECK_ECODE_SUCCESS(eCell,eCode);
386 // return what you have handleLayerT or extrapolateT will resolve that
389 // reset the lead layer to ensure the layer-to-layer loop
390 eCell.leadLayer = initialLayer;
392 // - if it came until here, return InProgress to not break the layer-to-layer loop
393 return Trk::ExtrapolationCode::InProgress;
396 /** handle the failure - as configured */
397 template <class T> Trk::ExtrapolationCode Trk::StaticEngine::handleReturnT(Trk::ExtrapolationCode eCode,
398 Trk::ExtrapolationCell<T>& eCell,
399 const Trk::Surface* sf,
400 Trk::PropDirection pDir,
401 Trk::BoundaryCheck bcheck) const
403 EX_MSG_DEBUG(++eCell.navigationStep, "return", "", "handleReturnT with code " << eCode.toString() << " called." );
404 if (eCode.isSuccessOrRecovered() || eCode.inProgress() ){
405 EX_MSG_VERBOSE(eCell.navigationStep, "return", "", "leaving static extrapolator successfully with code " << eCode.toString());
408 EX_MSG_VERBOSE(eCell.navigationStep, "return", "", "failure detected as " << eCode.toString() << " - checking fallback configuration.");
409 // obviously we need a surface to exercise the fallback
410 if (sf && !eCell.checkConfigurationMode(Trk::ExtrapolationMode::AvoidFallback)){
411 EX_MSG_VERBOSE(eCell.navigationStep, "return", "", "fallback configured. Trying to hit destination surface from last valid parameters.");
412 // check if you hit the surface, could still be stopped by PathLimit, but would also count as recovered
413 eCode = m_propagationEngine->propagate(eCell,*sf,pDir,bcheck,eCell.destinationCurvilinear);