// // ******************************************************************** // * License and Disclaimer * // * * // * The Geant4 software is copyright of the Copyright Holders of * // * the Geant4 Collaboration. It is provided under the terms and * // * conditions of the Geant4 Software License, included in the file * // * LICENSE and available at http://cern.ch/geant4/license . These * // * include a list of copyright holders. * // * * // * Neither the authors of this software system, nor their employing * // * institutes,nor the agencies providing financial support for this * // * work make any representation or warranty, express or implied, * // * regarding this software system or assume any liability for its * // * use. Please see the license in the file LICENSE and URL above * // * for the full disclaimer and the limitation of liability. * // * * // * This code implementation is the result of the scientific and * // * technical work of the GEANT4 collaboration. * // * By using, copying, modifying or distributing the software (or * // * any work based on the software) you agree to acknowledge its * // * use in resulting scientific publications, and indicate your * // * acceptance of all terms of the Geant4 Software license. * // ******************************************************************** // // $Id: G4ITNavigator.cc 66872 2013-01-15 01:25:57Z japost $ // // class G4ITNavigator Implementation // // Original author: Paul Kent, July 95/96 // // G4ITNavigator is a duplicate version of G4Navigator starting from Geant4.9.5 // initially written by Paul Kent and colleagues. // The only difference resides in the way the information is saved and managed // // History: // - Created. Paul Kent, Jul 95/96 // - Zero step protections J.A. / G.C., Nov 2004 // - Added check mode G. Cosmo, Mar 2004 // - Made Navigator Abstract G. Cosmo, Nov 2003 // - G4ITNavigator created M.K., Nov 2012 // -------------------------------------------------------------------- #include #include "G4ITNavigator.hh" #include "G4ios.hh" #include "G4SystemOfUnits.hh" #include "G4GeometryTolerance.hh" #include "G4VPhysicalVolume.hh" #define G4DEBUG_NAVIGATION 1 // ******************************************************************** // Constructor // ******************************************************************** // G4ITNavigator::G4ITNavigator() : fWasLimitedByGeometry(false), fVerbose(0), fTopPhysical(0), fCheck(false), fPushed(false), fWarnPush(true) { fActive= false; fLastTriedStepComputation= false; ResetStackAndState(); fActionThreshold_NoZeroSteps = 10; fAbandonThreshold_NoZeroSteps = 25; kCarTolerance = G4GeometryTolerance::GetInstance()->GetSurfaceTolerance(); fregularNav.SetNormalNavigation( &fnormalNav ); fStepEndPoint = G4ThreeVector( kInfinity, kInfinity, kInfinity ); fLastStepEndPointLocal = G4ThreeVector( kInfinity, kInfinity, kInfinity ); fpSaveState = 0; // this->SetVerboseLevel(3); // this->CheckMode(true); } // !> G4ITNavigator::G4SaveNavigatorState::G4SaveNavigatorState() : G4ITNavigatorState_Lock() { sWasLimitedByGeometry = false; sEntering = false; sExiting = false; sLocatedOnEdge = false; sLastStepWasZero = false; sEnteredDaughter = false; sExitedMother = false; sPushed = false; sValidExitNormal = false; sExitNormal = G4ThreeVector(0,0,0); sPreviousSftOrigin = G4ThreeVector(0,0,0); sPreviousSafety = 0.0; sNumberZeroSteps = 0; spBlockedPhysicalVolume = 0; sBlockedReplicaNo = -1; sLastLocatedPointLocal = G4ThreeVector( kInfinity, -kInfinity, 0.0 ); sLocatedOutsideWorld = false; } // 2 ) { G4int oldcoutPrec = G4cout.precision(8); G4cout << "*** G4ITNavigator::LocateGlobalPointAndSetup: ***" << G4endl; G4cout << " Called with arguments: " << G4endl << " Globalpoint = " << globalPoint << G4endl << " RelativeSearch = " << relativeSearch << G4endl; if( fVerbose == 4 ) { G4cout << " ----- Upon entering:" << G4endl; PrintState(); } G4cout.precision(oldcoutPrec); } #endif if ( !relativeSearch ) { ResetStackAndState(); } else { if ( fWasLimitedByGeometry ) { fWasLimitedByGeometry = false; fEnteredDaughter = fEntering; // Remember fExitedMother = fExiting; // Remember if ( fExiting ) { if ( fHistory.GetDepth() ) { fBlockedPhysicalVolume = fHistory.GetTopVolume(); fBlockedReplicaNo = fHistory.GetTopReplicaNo(); fHistory.BackLevel(); } else { fLastLocatedPointLocal = localPoint; fLocatedOutsideWorld = true; return 0; // Have exited world volume } // A fix for the case where a volume is "entered" at an edge // and a coincident surface exists outside it. // - This stops it from exiting further volumes and cycling // - However ReplicaNavigator treats this case itself // if ( fLocatedOnEdge && (VolumeType(fBlockedPhysicalVolume)!=kReplica )) { fExiting= false; } } else if ( fEntering ) { switch (VolumeType(fBlockedPhysicalVolume)) { case kNormal: fHistory.NewLevel(fBlockedPhysicalVolume, kNormal, fBlockedPhysicalVolume->GetCopyNo()); break; case kReplica: freplicaNav.ComputeTransformation(fBlockedReplicaNo, fBlockedPhysicalVolume); fHistory.NewLevel(fBlockedPhysicalVolume, kReplica, fBlockedReplicaNo); fBlockedPhysicalVolume->SetCopyNo(fBlockedReplicaNo); break; case kParameterised: if( fBlockedPhysicalVolume->GetRegularStructureId() == 0 ) { G4VSolid *pSolid; G4VPVParameterisation *pParam; G4TouchableHistory parentTouchable( fHistory ); pParam = fBlockedPhysicalVolume->GetParameterisation(); pSolid = pParam->ComputeSolid(fBlockedReplicaNo, fBlockedPhysicalVolume); pSolid->ComputeDimensions(pParam, fBlockedReplicaNo, fBlockedPhysicalVolume); pParam->ComputeTransformation(fBlockedReplicaNo, fBlockedPhysicalVolume); fHistory.NewLevel(fBlockedPhysicalVolume, kParameterised, fBlockedReplicaNo); fBlockedPhysicalVolume->SetCopyNo(fBlockedReplicaNo); // // Set the correct solid and material in Logical Volume // G4LogicalVolume *pLogical; pLogical = fBlockedPhysicalVolume->GetLogicalVolume(); pLogical->SetSolid( pSolid ); pLogical->UpdateMaterial(pParam -> ComputeMaterial(fBlockedReplicaNo, fBlockedPhysicalVolume, &parentTouchable)); } break; } fEntering = false; fBlockedPhysicalVolume = 0; localPoint = fHistory.GetTopTransform().TransformPoint(globalPoint); notKnownContained = false; } } else { fBlockedPhysicalVolume = 0; fEntering = false; fEnteredDaughter = false; // Full Step was not taken, did not enter fExiting = false; fExitedMother = false; // Full Step was not taken, did not exit } } // // Search from top of history up through geometry until // containing volume found: // If on // o OUTSIDE - Back up level, not/no longer exiting volumes // o SURFACE and EXITING - Back up level, setting new blocking no.s // else // o containing volume found // while (notKnownContained) { if ( fHistory.GetTopVolumeType()!=kReplica ) { targetSolid = fHistory.GetTopVolume()->GetLogicalVolume()->GetSolid(); localPoint = fHistory.GetTopTransform().TransformPoint(globalPoint); insideCode = targetSolid->Inside(localPoint); #ifdef G4VERBOSE if(( fVerbose == 1 ) && ( fCheck )) { G4String solidResponse = "-kInside-"; if (insideCode == kOutside) solidResponse = "-kOutside-"; else if (insideCode == kSurface) solidResponse = "-kSurface-"; G4cout << "*** G4ITNavigator::LocateGlobalPointAndSetup(): ***" << G4endl << " Invoked Inside() for solid: " << targetSolid->GetName() << ". Solid replied: " << solidResponse << G4endl << " For local point p: " << localPoint << G4endl; } #endif } else { insideCode = freplicaNav.BackLocate(fHistory, globalPoint, localPoint, fExiting, notKnownContained); // !CARE! if notKnownContained returns false then the point is within // the containing placement volume of the replica(s). If insidecode // will result in the history being backed up one level, then the // local point returned is the point in the system of this new level } if ( insideCode==kOutside ) { if ( fHistory.GetDepth() ) { fBlockedPhysicalVolume = fHistory.GetTopVolume(); fBlockedReplicaNo = fHistory.GetTopReplicaNo(); fHistory.BackLevel(); fExiting = false; } else { fLastLocatedPointLocal = localPoint; fLocatedOutsideWorld = true; return 0; // Have exited world volume } } else if ( insideCode==kSurface ) { G4bool isExiting = fExiting; if( (!fExiting)&&considerDirection ) { // Figure out whether we are exiting this level's volume // by using the direction // G4bool directionExiting = false; G4ThreeVector localDirection = fHistory.GetTopTransform().TransformAxis(globalDirection); if ( fHistory.GetTopVolumeType()!=kReplica ) { G4ThreeVector normal = targetSolid->SurfaceNormal(localPoint); directionExiting = normal.dot(localDirection) > 0.0; isExiting = isExiting || directionExiting; } } if( isExiting ) { if ( fHistory.GetDepth() ) { fBlockedPhysicalVolume = fHistory.GetTopVolume(); fBlockedReplicaNo = fHistory.GetTopReplicaNo(); fHistory.BackLevel(); // // Still on surface but exited volume not necessarily convex // fValidExitNormal = false; } else { fLastLocatedPointLocal = localPoint; fLocatedOutsideWorld = true; return 0; // Have exited world volume } } else { notKnownContained=false; } } else { notKnownContained=false; } } // END while (notKnownContained) // // Search downwards until deepest containing volume found, // blocking fBlockedPhysicalVolume/BlockedReplicaNum // // 3 Cases: // // o Parameterised daughters // =>Must be one G4PVParameterised daughter & voxels // o Positioned daughters & voxels // o Positioned daughters & no voxels noResult = true; // noResult should be renamed to // something like enteredLevel, as that is its meaning. do { // Determine `type' of current mother volume // targetPhysical = fHistory.GetTopVolume(); if (!targetPhysical) { break; } targetLogical = targetPhysical->GetLogicalVolume(); switch( CharacteriseDaughters(targetLogical) ) { case kNormal: if ( targetLogical->GetVoxelHeader() ) // use optimised navigation { noResult = fvoxelNav.LevelLocate(fHistory, fBlockedPhysicalVolume, fBlockedReplicaNo, globalPoint, pGlobalDirection, considerDirection, localPoint); } else // do not use optimised navigation { noResult = fnormalNav.LevelLocate(fHistory, fBlockedPhysicalVolume, fBlockedReplicaNo, globalPoint, pGlobalDirection, considerDirection, localPoint); } break; case kReplica: noResult = freplicaNav.LevelLocate(fHistory, fBlockedPhysicalVolume, fBlockedReplicaNo, globalPoint, pGlobalDirection, considerDirection, localPoint); break; case kParameterised: if( GetDaughtersRegularStructureId(targetLogical) != 1 ) { noResult = fparamNav.LevelLocate(fHistory, fBlockedPhysicalVolume, fBlockedReplicaNo, globalPoint, pGlobalDirection, considerDirection, localPoint); } else // Regular structure { noResult = fregularNav.LevelLocate(fHistory, fBlockedPhysicalVolume, fBlockedReplicaNo, globalPoint, pGlobalDirection, considerDirection, localPoint); } break; } // LevelLocate returns true if it finds a daughter volume // in which globalPoint is inside (or on the surface). if ( noResult ) { // Entering a daughter after ascending // // The blocked volume is no longer valid - it was for another level // fBlockedPhysicalVolume = 0; fBlockedReplicaNo = -1; // fEntering should be false -- else blockedVolume is assumed good. // fEnteredDaughter is used for ExitNormal // fEntering = false; fEnteredDaughter = true; #ifdef G4DEBUG_NAVIGATION if( fVerbose > 2 ) { G4VPhysicalVolume* enteredPhysical = fHistory.GetTopVolume(); G4cout << "*** G4ITNavigator::LocateGlobalPointAndSetup() ***" << G4endl; G4cout << " Entering volume: " << enteredPhysical->GetName() << G4endl; } #endif } } while (noResult); fLastLocatedPointLocal = localPoint; #ifdef G4VERBOSE if( fVerbose == 4 ) { G4int oldcoutPrec = G4cout.precision(8); G4String curPhysVol_Name("None"); if (targetPhysical) { curPhysVol_Name = targetPhysical->GetName(); } G4cout << " Return value = new volume = " << curPhysVol_Name << G4endl; G4cout << " ----- Upon exiting:" << G4endl; PrintState(); #ifdef G4DEBUG_NAVIGATION G4cout << "Upon exiting LocateGlobalPointAndSetup():" << G4endl; G4cout << " History = " << G4endl << fHistory << G4endl << G4endl; #endif G4cout.precision(oldcoutPrec); } #endif fLocatedOutsideWorld= false; return targetPhysical; } // ******************************************************************** // LocateGlobalPointWithinVolume // // -> the state information of this Navigator and its subNavigators // is updated in order to start the next step at pGlobalpoint // -> no check is performed whether pGlobalpoint is inside the // original volume (this must be the case). // // Note: a direction could be added to the arguments, to aid in future // optional checking (via the old code below, flagged by OLD_LOCATE). // [ This would be done only in verbose mode ] // ******************************************************************** // void G4ITNavigator::LocateGlobalPointWithinVolume(const G4ThreeVector& pGlobalpoint) { fLastLocatedPointLocal = ComputeLocalPoint(pGlobalpoint); fLastTriedStepComputation= false; #ifdef G4DEBUG_NAVIGATION if( fVerbose > 2 ) { G4cout << "Entering LocateGlobalWithinVolume(): History = " << G4endl; G4cout << fHistory << G4endl; } #endif // For the case of Voxel (or Parameterised) volume the respective // Navigator must be messaged to update its voxel information etc // Update the state of the Sub Navigators // - in particular any voxel information they store/cache // G4VPhysicalVolume* motherPhysical = fHistory.GetTopVolume(); G4LogicalVolume* motherLogical = motherPhysical->GetLogicalVolume(); G4SmartVoxelHeader* pVoxelHeader = motherLogical->GetVoxelHeader(); if ( fHistory.GetTopVolumeType()!=kReplica ) { switch( CharacteriseDaughters(motherLogical) ) { case kNormal: if ( pVoxelHeader ) { fvoxelNav.VoxelLocate( pVoxelHeader, fLastLocatedPointLocal ); } break; case kParameterised: if( GetDaughtersRegularStructureId(motherLogical) != 1 ) { // Resets state & returns voxel node // fparamNav.ParamVoxelLocate( pVoxelHeader, fLastLocatedPointLocal ); } break; case kReplica: G4Exception("G4ITNavigator::LocateGlobalPointWithinVolume()", "GeomNav0001", FatalException, "Not applicable for replicated volumes."); break; } } // Reset the state variables // - which would have been affected // by the 'equivalent' call to LocateGlobalPointAndSetup // - who's values have been invalidated by the 'move'. // fBlockedPhysicalVolume = 0; fBlockedReplicaNo = -1; fEntering = false; fEnteredDaughter = false; // Boundary not encountered, did not enter fExiting = false; fExitedMother = false; // Boundary not encountered, did not exit } // !> G4ITNavigatorState_Lock* G4ITNavigator::GetNavigatorState() { SetSavedState(); return fpSaveState; } void G4ITNavigator::SetNavigatorState(G4ITNavigatorState_Lock* navState) { fpSaveState = (G4SaveNavigatorState*) navState; if(navState) RestoreSavedState(); } void G4ITNavigator::NewNavigatorState() { fpSaveState = new G4SaveNavigatorState(); ResetState(); } // ******************************************************************** // SetSavedState // // Save the state, in case this is a parasitic call // Save fValidExitNormal, fExitNormal, fExiting, fEntering, // fBlockedPhysicalVolume, fBlockedReplicaNo, fLastStepWasZero; // ******************************************************************** // void G4ITNavigator::SetSavedState() { // !> // This check can be avoid if instead, at every first step of a track, // the IT tracking uses NewNavigatorSate // The normal tracking would just call once NewNavigatorState() before tracking // if(fpSaveState == 0) // fpSaveState = new G4SaveNavigatorState; // sExitNormal = fExitNormal; fpSaveState->sValidExitNormal = fValidExitNormal; fpSaveState->sExiting = fExiting; fpSaveState->sEntering = fEntering; fpSaveState->spBlockedPhysicalVolume = fBlockedPhysicalVolume; fpSaveState->sBlockedReplicaNo = fBlockedReplicaNo, fpSaveState->sLastStepWasZero = fLastStepWasZero; // !> fpSaveState->sPreviousSftOrigin = fPreviousSftOrigin; fpSaveState->sPreviousSafety = fPreviousSafety; fpSaveState->sNumberZeroSteps = fNumberZeroSteps; fpSaveState->sLocatedOnEdge = fLocatedOnEdge; fpSaveState->sWasLimitedByGeometry= fWasLimitedByGeometry; fpSaveState->sPushed=fPushed; fpSaveState->sNumberZeroSteps=fNumberZeroSteps; fpSaveState->sEnteredDaughter = fEnteredDaughter; fpSaveState->sExitedMother = fExitedMother; fpSaveState->sLastLocatedPointLocal = fLastLocatedPointLocal; fpSaveState->sLocatedOutsideWorld = fLocatedOutsideWorld; // sExitNormal; fValidExitNormal = fpSaveState->sValidExitNormal; fExiting = fpSaveState->sExiting; fEntering = fpSaveState->sEntering; fBlockedPhysicalVolume = fpSaveState->spBlockedPhysicalVolume; fBlockedReplicaNo = fpSaveState->sBlockedReplicaNo, fLastStepWasZero = fpSaveState->sLastStepWasZero; // !> fPreviousSftOrigin = fpSaveState->sPreviousSftOrigin ; fPreviousSafety = fpSaveState->sPreviousSafety ; fNumberZeroSteps = fpSaveState->sNumberZeroSteps ; fLocatedOnEdge = fpSaveState->sLocatedOnEdge ; fWasLimitedByGeometry = fpSaveState->sWasLimitedByGeometry; fPushed = fpSaveState->sPushed; fNumberZeroSteps = fpSaveState->sNumberZeroSteps; fEnteredDaughter= fpSaveState->sEnteredDaughter ; fExitedMother = fpSaveState->sExitedMother ; fLastLocatedPointLocal = fpSaveState->sLastLocatedPointLocal ; fLocatedOutsideWorld = fpSaveState->sLocatedOutsideWorld; // GetLogicalVolume(); static G4ThreadLocal G4int sNavCScalls=0; sNavCScalls++; fLastTriedStepComputation= true; #ifdef G4VERBOSE if( fVerbose > 0 ) { G4cout << "*** G4ITNavigator::ComputeStep: ***" << G4endl; G4cout << " Volume = " << motherPhysical->GetName() << " - Proposed step length = " << pCurrentProposedStepLength << G4endl; #ifdef G4DEBUG_NAVIGATION if( fVerbose >= 4 ) { G4cout << " Called with the arguments: " << G4endl << " Globalpoint = " << std::setw(25) << pGlobalpoint << G4endl << " Direction = " << std::setw(25) << pDirection << G4endl; G4cout << " ---- Upon entering :" << G4endl; PrintState(); } #endif } #endif G4ThreeVector newLocalPoint = ComputeLocalPoint(pGlobalpoint); if( newLocalPoint != fLastLocatedPointLocal ) { // Check whether the relocation is within safety // G4ThreeVector oldLocalPoint = fLastLocatedPointLocal; G4double moveLenSq = (newLocalPoint-oldLocalPoint).mag2(); if ( moveLenSq >= kCarTolerance*kCarTolerance ) { #ifdef G4VERBOSE ComputeStepLog(pGlobalpoint, moveLenSq); #endif // Relocate the point within the same volume // LocateGlobalPointWithinVolume( pGlobalpoint ); fLastTriedStepComputation= true; // Ensure that this is set again !! } } if ( fHistory.GetTopVolumeType()!=kReplica ) { switch( CharacteriseDaughters(motherLogical) ) { case kNormal: if ( motherLogical->GetVoxelHeader() ) { Step = fvoxelNav.ComputeStep(fLastLocatedPointLocal, localDirection, pCurrentProposedStepLength, pNewSafety, fHistory, fValidExitNormal, fExitNormal, fExiting, fEntering, &fBlockedPhysicalVolume, fBlockedReplicaNo); } else { if( motherPhysical->GetRegularStructureId() == 0 ) { Step = fnormalNav.ComputeStep(fLastLocatedPointLocal, localDirection, pCurrentProposedStepLength, pNewSafety, fHistory, fValidExitNormal, fExitNormal, fExiting, fEntering, &fBlockedPhysicalVolume, fBlockedReplicaNo); } else // Regular (non-voxelised) structure { LocateGlobalPointAndSetup( pGlobalpoint, &pDirection, true, true ); fLastTriedStepComputation= true; // Ensure that this is set again !! // // if physical process limits the step, the voxel will not be the // one given by ComputeStepSkippingEqualMaterials() and the local // point will be wrongly calculated. // There is a problem: when msc limits the step and the point is // assigned wrongly to phantom in previous step (while it is out // of the container volume). Then LocateGlobalPointAndSetup() has // reset the history topvolume to world. // if(fHistory.GetTopVolume()->GetRegularStructureId() == 0 ) { G4Exception("G4ITNavigator::ComputeStep()", "GeomNav1001", JustWarning, "Point is relocated in voxels, while it should be outside!"); Step = fnormalNav.ComputeStep(fLastLocatedPointLocal, localDirection, pCurrentProposedStepLength, pNewSafety, fHistory, fValidExitNormal, fExitNormal, fExiting, fEntering, &fBlockedPhysicalVolume, fBlockedReplicaNo); } else { Step = fregularNav. ComputeStepSkippingEqualMaterials(fLastLocatedPointLocal, localDirection, pCurrentProposedStepLength, pNewSafety, fHistory, fValidExitNormal, fExitNormal, fExiting, fEntering, &fBlockedPhysicalVolume, fBlockedReplicaNo, motherPhysical); } } } break; case kParameterised: if( GetDaughtersRegularStructureId(motherLogical) != 1 ) { Step = fparamNav.ComputeStep(fLastLocatedPointLocal, localDirection, pCurrentProposedStepLength, pNewSafety, fHistory, fValidExitNormal, fExitNormal, fExiting, fEntering, &fBlockedPhysicalVolume, fBlockedReplicaNo); } else // Regular structure { Step = fregularNav.ComputeStep(fLastLocatedPointLocal, localDirection, pCurrentProposedStepLength, pNewSafety, fHistory, fValidExitNormal, fExitNormal, fExiting, fEntering, &fBlockedPhysicalVolume, fBlockedReplicaNo); } break; case kReplica: G4Exception("G4ITNavigator::ComputeStep()", "GeomNav0001", FatalException, "Not applicable for replicated volumes."); break; } } else { // In the case of a replica, it must handle the exiting // edge/corner problem by itself // G4bool exitingReplica = fExitedMother; G4bool calculatedExitNormal= false; Step = freplicaNav.ComputeStep(pGlobalpoint, pDirection, fLastLocatedPointLocal, localDirection, pCurrentProposedStepLength, pNewSafety, fHistory, fValidExitNormal, calculatedExitNormal, fExitNormal, exitingReplica, fEntering, &fBlockedPhysicalVolume, fBlockedReplicaNo); fExiting= exitingReplica; // still ok to set it ?? } // Remember last safety origin & value. // fPreviousSftOrigin = pGlobalpoint; fPreviousSafety = pNewSafety; // Count zero steps - one can occur due to changing momentum at a boundary // - one, two (or a few) can occur at common edges between // volumes // - more than two is likely a problem in the geometry // description or the Navigation // Rule of thumb: likely at an Edge if two consecutive steps are zero, // because at least two candidate volumes must have been // checked // fLocatedOnEdge = fLastStepWasZero && (Step==0.0); fLastStepWasZero = (Step==0.0); if (fPushed) fPushed = fLastStepWasZero; // Handle large number of consecutive zero steps // if ( fLastStepWasZero ) { fNumberZeroSteps++; #ifdef G4DEBUG_NAVIGATION if( fNumberZeroSteps > 1 ) { G4cout << "G4ITNavigator::ComputeStep(): another zero step, # " << fNumberZeroSteps << " at " << pGlobalpoint << " in volume " << motherPhysical->GetName() << " nav-comp-step calls # " << sNavCScalls << G4endl; } #endif if( fNumberZeroSteps > fActionThreshold_NoZeroSteps-1 ) { // Act to recover this stuck track. Pushing it along direction // Step += 100*kCarTolerance; #ifdef G4VERBOSE if ((!fPushed) && (fWarnPush)) { std::ostringstream message; message << "Track stuck or not moving." << G4endl << " Track stuck, not moving for " << fNumberZeroSteps << " steps" << G4endl << " in volume -" << motherPhysical->GetName() << "- at point " << pGlobalpoint << G4endl << " direction: " << pDirection << "." << G4endl << " Potential geometry or navigation problem !" << G4endl << " Trying pushing it of " << Step << " mm ..."; G4Exception("G4ITNavigator::ComputeStep()", "GeomNav1002", JustWarning, message, "Potential overlap in geometry!"); } #endif fPushed = true; } if( fNumberZeroSteps > fAbandonThreshold_NoZeroSteps-1 ) { // Must kill this stuck track // std::ostringstream message; message << "Stuck Track: potential geometry or navigation problem." << G4endl << " Track stuck, not moving for " << fNumberZeroSteps << " steps" << G4endl << " in volume -" << motherPhysical->GetName() << "- at point " << pGlobalpoint << G4endl << " direction: " << pDirection << "."; motherPhysical->CheckOverlaps(5000, false); G4Exception("G4ITNavigator::ComputeStep()", "GeomNav0003", EventMustBeAborted, message); } } else { if (!fPushed) fNumberZeroSteps = 0; } fEnteredDaughter = fEntering; // I expect to enter a volume in this Step fExitedMother = fExiting; fStepEndPoint = pGlobalpoint + Step * pDirection; fLastStepEndPointLocal = fLastLocatedPointLocal + Step * localDirection; if( fExiting ) { #ifdef G4DEBUG_NAVIGATION if( fVerbose > 2 ) { G4cout << " At G4Nav CompStep End - if(exiting) - fExiting= " << fExiting << " fValidExitNormal = " << fValidExitNormal << G4endl; G4cout << " fExitNormal= " << fExitNormal << G4endl; } #endif if(fValidExitNormal) { // Convention: fExitNormal is in the 'grand-mother' coordinate system // fGrandMotherExitNormal= fExitNormal; } else { // We must calculate the normal anyway (in order to have it if requested) // G4ThreeVector finalLocalPoint = fLastLocatedPointLocal + localDirection*Step; // Now fGrandMotherExitNormal is in the 'grand-mother' coordinate system // fGrandMotherExitNormal = motherLogical->GetSolid()->SurfaceNormal(finalLocalPoint); const G4RotationMatrix* mRot = motherPhysical->GetRotation(); if( mRot ) { fGrandMotherExitNormal *= (*mRot).inverse(); } // Do not set fValidExitNormal -- this signifies that the solid is convex! } } fStepEndPoint= pGlobalpoint+Step*pDirection; if( (Step == pCurrentProposedStepLength) && (!fExiting) && (!fEntering) ) { // This if Step is not really limited by the geometry. // The Navigator is obliged to return "infinity" // Step = kInfinity; } #ifdef G4VERBOSE if( fVerbose > 1 ) { if( fVerbose >= 4 ) { G4cout << " ----- Upon exiting :" << G4endl; PrintState(); } G4cout <<" Returned step = " << Step << G4endl; if( Step == kInfinity ) { G4cout << " Original proposed step = " << pCurrentProposedStepLength << G4endl; } G4cout << " Safety = " << pNewSafety << G4endl; } #endif return Step; } // ******************************************************************** // CheckNextStep // // Compute the step without altering the navigator state // ******************************************************************** // G4double G4ITNavigator::CheckNextStep( const G4ThreeVector& pGlobalpoint, const G4ThreeVector& pDirection, const G4double pCurrentProposedStepLength, G4double& pNewSafety) { G4double step; // Save the state, for this parasitic call // SetSavedState(); step = ComputeStep ( pGlobalpoint, pDirection, pCurrentProposedStepLength, pNewSafety ); // If a parasitic call, then attempt to restore the key parts of the state // RestoreSavedState(); return step; } // ******************************************************************** // ResetState // // Resets stack and minimum of navigator state `machine' // ******************************************************************** // void G4ITNavigator::ResetState() { fWasLimitedByGeometry = false; fEntering = false; fExiting = false; fLocatedOnEdge = false; fLastStepWasZero = false; fEnteredDaughter = false; fExitedMother = false; fPushed = false; fValidExitNormal = false; fExitNormal = G4ThreeVector(0,0,0); fPreviousSftOrigin = G4ThreeVector(0,0,0); fPreviousSafety = 0.0; fNumberZeroSteps = 0; fBlockedPhysicalVolume = 0; fBlockedReplicaNo = -1; fLastLocatedPointLocal = G4ThreeVector( kInfinity, -kInfinity, 0.0 ); fLocatedOutsideWorld = false; } // ******************************************************************** // SetupHierarchy // // Renavigates & resets hierarchy described by current history // o Reset volumes // o Recompute transforms and/or solids of replicated/parameterised volumes // ******************************************************************** // void G4ITNavigator::SetupHierarchy() { G4int i; const G4int cdepth = fHistory.GetDepth(); G4VPhysicalVolume *current; G4VSolid *pSolid; G4VPVParameterisation *pParam; for ( i=1; i<=cdepth; i++ ) { current = fHistory.GetVolume(i); switch ( fHistory.GetVolumeType(i) ) { case kNormal: break; case kReplica: freplicaNav.ComputeTransformation(fHistory.GetReplicaNo(i), current); break; case kParameterised: G4int replicaNo; pParam = current->GetParameterisation(); replicaNo = fHistory.GetReplicaNo(i); pSolid = pParam->ComputeSolid(replicaNo, current); // Set up dimensions & transform in solid/physical volume // pSolid->ComputeDimensions(pParam, replicaNo, current); pParam->ComputeTransformation(replicaNo, current); G4TouchableHistory touchable( fHistory ); touchable.MoveUpHistory(); // move up to the parent level // Set up the correct solid and material in Logical Volume // G4LogicalVolume *pLogical = current->GetLogicalVolume(); pLogical->SetSolid( pSolid ); pLogical->UpdateMaterial( pParam -> ComputeMaterial(replicaNo, current, &touchable) ); break; } } } // ******************************************************************** // GetLocalExitNormal // // Obtains the Normal vector to a surface (in local coordinates) // pointing out of previous volume and into current volume // ******************************************************************** // G4ThreeVector G4ITNavigator::GetLocalExitNormal( G4bool* valid ) { G4ThreeVector ExitNormal(0.,0.,0.); G4VSolid *currentSolid=0; G4LogicalVolume *candidateLogical; if ( fLastTriedStepComputation ) { // use fLastLocatedPointLocal // and next candidate volume G4ThreeVector nextSolidExitNormal(0.,0.,0.); if( fEntering && (fBlockedPhysicalVolume!=0) ) { candidateLogical= fBlockedPhysicalVolume->GetLogicalVolume(); if( candidateLogical ) { // fLastStepEndPointLocal is in the coordinates of the mother // we need it in the daughter's coordinate system. if( CharacteriseDaughters(candidateLogical) != kReplica ) { // First transform fLastLocatedPointLocal to the new daughter // coordinates G4AffineTransform MotherToDaughterTransform= GetMotherToDaughterTransform( fBlockedPhysicalVolume, fBlockedReplicaNo, VolumeType(fBlockedPhysicalVolume) ); G4ThreeVector daughterPointOwnLocal= MotherToDaughterTransform.TransformPoint( fLastStepEndPointLocal ); // OK if it is a parameterised volume // EInside inSideIt; G4bool onSurface; G4double safety= -1.0; currentSolid= candidateLogical->GetSolid(); inSideIt = currentSolid->Inside(daughterPointOwnLocal); onSurface = (inSideIt == kSurface); if( ! onSurface ) { if( inSideIt == kOutside ) { safety = (currentSolid->DistanceToIn(daughterPointOwnLocal)); onSurface = safety < 100.0 * kCarTolerance; } else if (inSideIt == kInside ) { safety = (currentSolid->DistanceToOut(daughterPointOwnLocal)); onSurface = safety < 100.0 * kCarTolerance; } } if( onSurface ) { nextSolidExitNormal = currentSolid->SurfaceNormal(daughterPointOwnLocal); // Entering the solid ==> opposite // ExitNormal = -nextSolidExitNormal; } else { #ifdef G4VERBOSE if(( fVerbose == 1 ) && ( fCheck )) { std::ostringstream message; message << "Point not on surface ! " << G4endl << " Point = " << daughterPointOwnLocal << G4endl << " Physical volume = " << fBlockedPhysicalVolume->GetName() << G4endl << " Logical volume = " << candidateLogical->GetName() << G4endl << " Solid = " << currentSolid->GetName() << " Type = " << currentSolid->GetEntityType() << G4endl << *currentSolid << G4endl; if( inSideIt == kOutside ) { message << "Point is Outside. " << G4endl << " Safety (from outside) = " << safety << G4endl; } else // if( inSideIt == kInside ) { message << "Point is Inside. " << G4endl << " Safety (from inside) = " << safety << G4endl; } G4Exception("G4ITNavigator::GetLocalExitNormal()", "GeomNav1001", JustWarning, message); } #endif } *valid = onSurface; // was =true; } else { *valid = false; // TODO: Need Separate code for replica!!!! #ifdef G4DEBUG_NAVIGATION G4Exception("G4ITNavigator::GetLocalExitNormal()", "GeomNav0001", FatalException, "Local normal not (yet) available for replica volumes."); #endif } } } else if ( fExiting ) { ExitNormal = fGrandMotherExitNormal; *valid = true; } else // ie ( fBlockedPhysicalVolume == 0 ) { *valid = false; } } else { if ( EnteredDaughterVolume() ) { ExitNormal= -(fHistory.GetTopVolume()->GetLogicalVolume()-> GetSolid()->SurfaceNormal(fLastLocatedPointLocal)); *valid = true; } else { if( fExitedMother ) { ExitNormal = fGrandMotherExitNormal; *valid = true; } else // We are not at a boundary. ExitNormal remains (0,0,0) { *valid = false; } } } return ExitNormal; } // ******************************************************************** // GetMotherToDaughterTransform // // Obtains the mother to daughter affine transformation // ******************************************************************** // G4AffineTransform G4ITNavigator::GetMotherToDaughterTransform( G4VPhysicalVolume *pEnteringPhysVol, // not Const G4int enteringReplicaNo, EVolume enteringVolumeType ) { switch (enteringVolumeType) { case kNormal: // Nothing is needed to prepare the transformation break; // It is stored already in the physical volume (placement) case kReplica: // Sets the transform in the Replica - tbc G4Exception("G4ITNavigator::GetMotherToDaughterTransform()", "GeomNav0001", FatalException, "Method NOT Implemented yet for replica volumes."); break; case kParameterised: if( pEnteringPhysVol->GetRegularStructureId() == 0 ) { G4VPVParameterisation *pParam = pEnteringPhysVol->GetParameterisation(); G4VSolid* pSolid = pParam->ComputeSolid(enteringReplicaNo, pEnteringPhysVol); pSolid->ComputeDimensions(pParam, enteringReplicaNo, pEnteringPhysVol); // Sets the transform in the Parameterisation // pParam->ComputeTransformation(enteringReplicaNo, pEnteringPhysVol); // Set the correct solid and material in Logical Volume // G4LogicalVolume* pLogical = pEnteringPhysVol->GetLogicalVolume(); pLogical->SetSolid( pSolid ); } break; } return G4AffineTransform(pEnteringPhysVol->GetRotation(), pEnteringPhysVol->GetTranslation()).Invert(); } // ******************************************************************** // GetLocalExitNormalAndCheck // // Obtains the Normal vector to a surface (in local coordinates) // pointing out of previous volume and into current volume, and // checks the current point against expected 'local' value. // ******************************************************************** // G4ThreeVector G4ITNavigator:: GetLocalExitNormalAndCheck(const G4ThreeVector& ExpectedBoundaryPointGlobal, G4bool* pValid) { G4ThreeVector ExpectedBoundaryPointLocal; // Check Current point against expected 'local' value // if ( fLastTriedStepComputation ) { const G4AffineTransform& GlobalToLocal= GetGlobalToLocalTransform(); ExpectedBoundaryPointLocal = GlobalToLocal.TransformPoint( ExpectedBoundaryPointGlobal ); } return GetLocalExitNormal( pValid); } // ******************************************************************** // GetGlobalExitNormal // // Obtains the Normal vector to a surface (in global coordinates) // pointing out of previous volume and into current volume // ******************************************************************** // G4ThreeVector G4ITNavigator::GetGlobalExitNormal(const G4ThreeVector& IntersectPointGlobal, G4bool* pValidNormal) { G4bool validNormal; G4ThreeVector localNormal, globalNormal; localNormal = GetLocalExitNormalAndCheck( IntersectPointGlobal, &validNormal); *pValidNormal = validNormal; G4AffineTransform localToGlobal = GetLocalToGlobalTransform(); globalNormal = localToGlobal.TransformAxis( localNormal ); return globalNormal; } // ******************************************************************** // ComputeSafety // // It assumes that it will be // i) called at the Point in the same volume as the EndPoint of the // ComputeStep. // ii) after (or at the end of) ComputeStep OR after the relocation. // ******************************************************************** // G4double G4ITNavigator::ComputeSafety( const G4ThreeVector &pGlobalpoint, const G4double pMaxLength, const G4bool keepState) { G4double newSafety = 0.0; #ifdef G4DEBUG_NAVIGATION G4int oldcoutPrec = G4cout.precision(8); if( fVerbose > 0 ) { G4cout << "*** G4ITNavigator::ComputeSafety: ***" << G4endl << " Called at point: " << pGlobalpoint << G4endl; G4VPhysicalVolume *motherPhysical = fHistory.GetTopVolume(); G4cout << " Volume = " << motherPhysical->GetName() << " - Maximum length = " << pMaxLength << G4endl; if( fVerbose >= 4 ) { G4cout << " ----- Upon entering Compute Safety:" << G4endl; PrintState(); } } #endif if (keepState) { SetSavedState(); } // fLastTriedStepComputation= true; -- this method is NOT computing the Step size G4double distEndpointSq = (pGlobalpoint-fStepEndPoint).mag2(); G4bool stayedOnEndpoint = distEndpointSq < kCarTolerance*kCarTolerance; G4bool endpointOnSurface = fEnteredDaughter || fExitedMother; if( !(endpointOnSurface && stayedOnEndpoint) ) { // Pseudo-relocate to this point (updates voxel information only) // LocateGlobalPointWithinVolume( pGlobalpoint ); // --->> Danger: Side effects on sub-navigator voxel information <<--- // Could be replaced again by 'granular' calls to sub-navigator // locates (similar side-effects, but faster. // Solutions: // 1) Re-locate (to where?) // 2) Insure that the methods using (G4ComputeStep?) // does a relocation (if information is disturbed only ?) #ifdef G4DEBUG_NAVIGATION if( fVerbose >= 2 ) { G4cout << " G4ITNavigator::ComputeSafety() relocates-in-volume to point: " << pGlobalpoint << G4endl; } #endif G4VPhysicalVolume *motherPhysical = fHistory.GetTopVolume(); G4LogicalVolume *motherLogical = motherPhysical->GetLogicalVolume(); G4SmartVoxelHeader* pVoxelHeader = motherLogical->GetVoxelHeader(); G4ThreeVector localPoint = ComputeLocalPoint(pGlobalpoint); if ( fHistory.GetTopVolumeType()!=kReplica ) { switch(CharacteriseDaughters(motherLogical)) { case kNormal: if ( pVoxelHeader ) { newSafety=fvoxelNav.ComputeSafety(localPoint,fHistory,pMaxLength); } else { newSafety=fnormalNav.ComputeSafety(localPoint,fHistory,pMaxLength); } break; case kParameterised: if( GetDaughtersRegularStructureId(motherLogical) != 1 ) { newSafety = fparamNav.ComputeSafety(localPoint,fHistory,pMaxLength); } else // Regular structure { newSafety = fregularNav.ComputeSafety(localPoint,fHistory,pMaxLength); } break; case kReplica: G4Exception("G4ITNavigator::ComputeSafety()", "NotApplicable", FatalException, "Not applicable for replicated volumes."); break; } } else { newSafety = freplicaNav.ComputeSafety(pGlobalpoint, localPoint, fHistory, pMaxLength); } } else // if( endpointOnSurface && stayedOnEndpoint ) { #ifdef G4DEBUG_NAVIGATION if( fVerbose >= 2 ) { G4cout << " G4ITNavigator::ComputeSafety() finds that point - " << pGlobalpoint << " - is on surface " << G4endl; if( fEnteredDaughter ) { G4cout << " entered new daughter volume"; } if( fExitedMother ) { G4cout << " and exited previous volume."; } G4cout << G4endl; G4cout << " EndPoint was = " << fStepEndPoint << G4endl; } #endif newSafety = 0.0; } // Remember last safety origin & value // fPreviousSftOrigin = pGlobalpoint; fPreviousSafety = newSafety; if (keepState) { RestoreSavedState(); } #ifdef G4DEBUG_NAVIGATION if( fVerbose > 1 ) { G4cout << " ---- Exiting ComputeSafety " << G4endl; if( fVerbose > 2 ) { PrintState(); } G4cout << " Returned value of Safety = " << newSafety << G4endl; } G4cout.precision(oldcoutPrec); #endif return newSafety; } // ******************************************************************** // CreateTouchableHistoryHandle // ******************************************************************** // G4TouchableHistoryHandle G4ITNavigator::CreateTouchableHistoryHandle() const { return G4TouchableHistoryHandle( CreateTouchableHistory() ); } // ******************************************************************** // PrintState // ******************************************************************** // void G4ITNavigator::PrintState() const { G4int oldcoutPrec = G4cout.precision(4); if( fVerbose == 4 ) { G4cout << "The current state of G4ITNavigator is: " << G4endl; G4cout << " ValidExitNormal= " << fValidExitNormal << G4endl << " ExitNormal = " << fExitNormal << G4endl << " Exiting = " << fExiting << G4endl << " Entering = " << fEntering << G4endl << " BlockedPhysicalVolume= " ; if (fBlockedPhysicalVolume==0) G4cout << "None"; else G4cout << fBlockedPhysicalVolume->GetName(); G4cout << G4endl << " BlockedReplicaNo = " << fBlockedReplicaNo << G4endl << " LastStepWasZero = " << fLastStepWasZero << G4endl << G4endl; } if( ( 1 < fVerbose) && (fVerbose < 4) ) { G4cout << std::setw(30) << " ExitNormal " << " " << std::setw( 5) << " Valid " << " " << std::setw( 9) << " Exiting " << " " << std::setw( 9) << " Entering" << " " << std::setw(15) << " Blocked:Volume " << " " << std::setw( 9) << " ReplicaNo" << " " << std::setw( 8) << " LastStepZero " << " " << G4endl; G4cout << "( " << std::setw(7) << fExitNormal.x() << ", " << std::setw(7) << fExitNormal.y() << ", " << std::setw(7) << fExitNormal.z() << " ) " << std::setw( 5) << fValidExitNormal << " " << std::setw( 9) << fExiting << " " << std::setw( 9) << fEntering << " "; if ( fBlockedPhysicalVolume==0 ) G4cout << std::setw(15) << "None"; else G4cout << std::setw(15)<< fBlockedPhysicalVolume->GetName(); G4cout << std::setw( 9) << fBlockedReplicaNo << " " << std::setw( 8) << fLastStepWasZero << " " << G4endl; } if( fVerbose > 2 ) { G4cout.precision(8); G4cout << " Current Localpoint = " << fLastLocatedPointLocal << G4endl; G4cout << " PreviousSftOrigin = " << fPreviousSftOrigin << G4endl; G4cout << " PreviousSafety = " << fPreviousSafety << G4endl; } G4cout.precision(oldcoutPrec); } // ******************************************************************** // ComputeStepLog // ******************************************************************** // void G4ITNavigator::ComputeStepLog(const G4ThreeVector& pGlobalpoint, G4double moveLenSq) const { // The following checks only make sense if the move is larger // than the tolerance. static const G4double fAccuracyForWarning = kCarTolerance, fAccuracyForException = 1000*kCarTolerance; G4ThreeVector OriginalGlobalpoint = fHistory.GetTopTransform().Inverse(). TransformPoint(fLastLocatedPointLocal); G4double shiftOriginSafSq = (fPreviousSftOrigin-pGlobalpoint).mag2(); // Check that the starting point of this step is // within the isotropic safety sphere of the last point // to a accuracy/precision given by fAccuracyForWarning. // If so give warning. // If it fails by more than fAccuracyForException exit with error. // if( shiftOriginSafSq >= sqr(fPreviousSafety) ) { G4double shiftOrigin = std::sqrt(shiftOriginSafSq); G4double diffShiftSaf = shiftOrigin - fPreviousSafety; if( diffShiftSaf > fAccuracyForWarning ) { G4int oldcoutPrec= G4cout.precision(8); G4int oldcerrPrec= G4cerr.precision(10); std::ostringstream message, suggestion; message << "Accuracy error or slightly inaccurate position shift." << G4endl << " The Step's starting point has moved " << std::sqrt(moveLenSq)/mm << " mm " << G4endl << " since the last call to a Locate method." << G4endl << " This has resulted in moving " << shiftOrigin/mm << " mm " << " from the last point at which the safety " << " was calculated " << G4endl << " which is more than the computed safety= " << fPreviousSafety/mm << " mm at that point." << G4endl << " This difference is " << diffShiftSaf/mm << " mm." << G4endl << " The tolerated accuracy is " << fAccuracyForException/mm << " mm."; suggestion << " "; static G4ThreadLocal G4int warnNow = 0; if( ((++warnNow % 100) == 1) ) { message << G4endl << " This problem can be due to either " << G4endl << " - a process that has proposed a displacement" << " larger than the current safety , or" << G4endl << " - inaccuracy in the computation of the safety"; suggestion << "We suggest that you " << G4endl << " - find i) what particle is being tracked, and " << " ii) through what part of your geometry " << G4endl << " for example by re-running this event with " << G4endl << " /tracking/verbose 1 " << G4endl << " - check which processes you declare for" << " this particle (and look at non-standard ones)" << G4endl << " - in case, create a detailed logfile" << " of this event using:" << G4endl << " /tracking/verbose 6 "; } G4Exception("G4ITNavigator::ComputeStep()", "GeomNav1002", JustWarning, message, G4String(suggestion.str())); G4cout.precision(oldcoutPrec); G4cerr.precision(oldcerrPrec); } #ifdef G4DEBUG_NAVIGATION else { G4cerr << "WARNING - G4ITNavigator::ComputeStep()" << G4endl << " The Step's starting point has moved " << std::sqrt(moveLenSq) << "," << G4endl << " which has taken it to the limit of" << " the current safety. " << G4endl; } #endif } G4double safetyPlus = fPreviousSafety + fAccuracyForException; if ( shiftOriginSafSq > sqr(safetyPlus) ) { std::ostringstream message; message << "May lead to a crash or unreliable results." << G4endl << " Position has shifted considerably without" << " notifying the navigator !" << G4endl << " Tolerated safety: " << safetyPlus << G4endl << " Computed shift : " << shiftOriginSafSq; G4Exception("G4ITNavigator::ComputeStep()", "GeomNav1002", JustWarning, message); } } // ******************************************************************** // Operator << // ******************************************************************** // std::ostream& operator << (std::ostream &os,const G4ITNavigator &n) { os << "Current History: " << G4endl << n.fHistory; return os; }