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