Files
geant4/source/geometry/navigation/src/G4Navigator.cc
T
2022-07-01 10:44:02 +02:00

2229 lines
81 KiB
C++

//
// ********************************************************************
// * 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. *
// ********************************************************************
//
// class G4Navigator Implementation
//
// Original author: Paul Kent, July 95/96
// Responsible 1996-present: John Apostolakis, Gabriele Cosmo
// Additional revisions by: Pedro Arce, Vladimir Grichine
// --------------------------------------------------------------------
#include <iomanip>
#include "G4Navigator.hh"
#include "G4ios.hh"
#include "G4SystemOfUnits.hh"
#include "G4GeometryTolerance.hh"
#include "G4VPhysicalVolume.hh"
#include "G4VoxelSafety.hh"
// Constant determining how precise normals should be (how close to unit
// vectors). If exceeded, warnings will be issued.
// Can be CLHEP::perMillion (its old default) for geometry checking.
//
static const G4double kToleranceNormalCheck = CLHEP::perThousand;
// ********************************************************************
// Constructor
// ********************************************************************
//
G4Navigator::G4Navigator()
{
ResetStackAndState();
// Initialises also all
// - exit / entry flags
// - flags & variables for exit normals
// - zero step counters
// - blocked volume
if( fVerbose > 2 )
{
G4cout << " G4Navigator parameters: Action Threshold (No Zero Steps) = "
<< fActionThreshold_NoZeroSteps
<< " Abandon Threshold (No Zero Steps) = "
<< fAbandonThreshold_NoZeroSteps << G4endl;
}
kCarTolerance = G4GeometryTolerance::GetInstance()->GetSurfaceTolerance();
fMinStep = 0.05*kCarTolerance;
fSqTol = sqr(kCarTolerance);
fregularNav.SetNormalNavigation( &fnormalNav );
fStepEndPoint = G4ThreeVector( kInfinity, kInfinity, kInfinity );
fLastStepEndPointLocal = G4ThreeVector( kInfinity, kInfinity, kInfinity );
fpVoxelSafety = new G4VoxelSafety();
#ifdef ALTERNATIVE_VOXEL_NAV
fpvoxelNav = new G4VoxelNavigation();
#endif
}
// ********************************************************************
// Destructor
// ********************************************************************
//
G4Navigator::~G4Navigator()
{
delete fpVoxelSafety;
delete fpExternalNav;
#ifdef ALTERNATIVE_VOXEL_NAV
delete fpvoxelNav;
#endif
}
// ********************************************************************
// ResetHierarchyAndLocate
// ********************************************************************
//
G4VPhysicalVolume*
G4Navigator::ResetHierarchyAndLocate(const G4ThreeVector& p,
const G4ThreeVector& direction,
const G4TouchableHistory& h)
{
ResetState();
fHistory = *h.GetHistory();
SetupHierarchy();
fLastTriedStepComputation = false; // Redundant, but best
return LocateGlobalPointAndSetup(p, &direction, true, false);
}
// ********************************************************************
// LocateGlobalPointAndSetup
//
// Locate the point in the hierarchy return 0 if outside
// The direction is required
// - if on an edge shared by more than two surfaces
// (to resolve likely looping in tracking)
// - at initial location of a particle
// (to resolve potential ambiguity at boundary)
//
// Flags on exit: (comments to be completed)
// fEntering - True if entering `daughter' volume (or replica)
// whether daughter of last mother directly
// or daughter of that volume's ancestor.
// fExiting - True if exited 'mother' volume
// (always ? - how about if going back down ? - tbc)
// ********************************************************************
//
G4VPhysicalVolume*
G4Navigator::LocateGlobalPointAndSetup( const G4ThreeVector& globalPoint,
const G4ThreeVector* pGlobalDirection,
const G4bool relativeSearch,
const G4bool ignoreDirection )
{
G4bool notKnownContained = true, noResult;
G4VPhysicalVolume *targetPhysical;
G4LogicalVolume *targetLogical;
G4VSolid *targetSolid = 0;
G4ThreeVector localPoint, globalDirection;
EInside insideCode;
G4bool considerDirection = pGlobalDirection && ((!ignoreDirection) || fLocatedOnEdge);
fLastTriedStepComputation = false;
fChangedGrandMotherRefFrame = false; // For local exit normal
if( considerDirection )
{
globalDirection=*pGlobalDirection;
}
#ifdef G4VERBOSE
if( fVerbose > 2 )
{
G4int oldcoutPrec = G4cout.precision(8);
G4cout << "*** G4Navigator::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
G4int noLevelsExited = 0;
G4int noLevelsEntered = 0;
if ( !relativeSearch )
{
ResetStackAndState();
}
else
{
if ( fWasLimitedByGeometry )
{
fWasLimitedByGeometry = false;
fEnteredDaughter = fEntering; // Remember
fExitedMother = fExiting; // Remember
if ( fExiting )
{
++noLevelsExited; // count this first level entered too
if ( fHistory.GetDepth() )
{
fBlockedPhysicalVolume = fHistory.GetTopVolume();
fBlockedReplicaNo = fHistory.GetTopReplicaNo();
fHistory.BackLevel();
}
else
{
fLastLocatedPointLocal = localPoint;
fLocatedOutsideWorld = true;
fBlockedPhysicalVolume = 0; // to be sure
fBlockedReplicaNo = -1;
fEntering = false; // No longer
fEnteredDaughter = false;
fExitedMother = true; // ??
return nullptr; // 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
//
// assert( fBlockedPhysicalVolume!=0 );
// Expect to be on edge => on surface
//
if ( fLocatedOnEdge && (VolumeType(fBlockedPhysicalVolume)!=kReplica ))
{
fExiting = false;
// Consider effect on Exit Normal !?
}
}
else
if ( fEntering )
{
// assert( fBlockedPhysicalVolume!=0 );
++noLevelsEntered; // count the first level entered too
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;
case kExternal:
G4Exception("G4Navigator::LocateGlobalPointAndSetup()",
"GeomNav0001", FatalException,
"Extra levels not applicable for external volumes.");
break;
}
fEntering = false;
fBlockedPhysicalVolume = nullptr;
localPoint = fHistory.GetTopTransform().TransformPoint(globalPoint);
notKnownContained = false;
}
}
else
{
fBlockedPhysicalVolume = nullptr;
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) // Loop checking, 07.10.2016, J.Apostolakis
{
EVolume topVolumeType = fHistory.GetTopVolumeType();
if (topVolumeType!=kReplica && topVolumeType!=kExternal)
{
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 << "*** G4Navigator::LocateGlobalPointAndSetup(): ***" << G4endl
<< " Invoked Inside() for solid: " << targetSolid->GetName()
<< ". Solid replied: " << solidResponse << G4endl
<< " For local point p: " << localPoint << G4endl;
}
#endif
}
else
{
if( topVolumeType == kReplica )
{
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
}
else
{
targetSolid = fHistory.GetTopVolume()->GetLogicalVolume()->GetSolid();
localPoint = fHistory.GetTopTransform().TransformPoint(globalPoint);
G4ThreeVector localDirection =
fHistory.GetTopTransform().TransformAxis(globalDirection);
insideCode = fpExternalNav->Inside(targetSolid, localPoint, localDirection);
}
}
// Point is inside current volume, break out of the loop
if ( insideCode == kInside )
break;
// Point is outside current volume, move up a level in the hierarchy
if ( insideCode == kOutside )
{
++noLevelsExited;
// Exiting world volume
if ( fHistory.GetDepth() == 0 )
{
fLocatedOutsideWorld = true;
fLastLocatedPointLocal = localPoint;
return nullptr;
}
fBlockedPhysicalVolume = fHistory.GetTopVolume();
fBlockedReplicaNo = fHistory.GetTopReplicaNo();
fHistory.BackLevel();
fExiting = false;
if( noLevelsExited > 1 )
{
// The first transformation was done by the sub-navigator
//
if(const auto *mRot = fBlockedPhysicalVolume->GetRotation())
{
fGrandMotherExitNormal *= (*mRot).inverse();
fChangedGrandMotherRefFrame = true;
}
}
continue;
}
// Point is on the surface of a volume
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);
// Make sure localPoint in correct reference frame
// ( Was it already correct ? How ? )
//
localPoint= fHistory.GetTopTransform().TransformPoint(globalPoint);
if ( fHistory.GetTopVolumeType() != kReplica )
{
G4ThreeVector normal = targetSolid->SurfaceNormal(localPoint);
directionExiting = normal.dot(localDirection) > 0.0;
isExiting = isExiting || directionExiting;
}
}
// Point is on a surface, but no longer exiting, break out of the loop
if ( !isExiting )
break;
++noLevelsExited;
// Point is on the outer surface, leaving world volume
if ( fHistory.GetDepth() == 0 )
{
fLocatedOutsideWorld = true;
fLastLocatedPointLocal = localPoint;
return nullptr;
}
// Point is still on a surface, but exited a volume not necessarily convex
fValidExitNormal = false;
fBlockedPhysicalVolume = fHistory.GetTopVolume();
fBlockedReplicaNo = fHistory.GetTopReplicaNo();
fHistory.BackLevel();
if( noLevelsExited > 1 )
{
// The first transformation was done by the sub-navigator
//
const G4RotationMatrix* mRot =
fBlockedPhysicalVolume->GetRotation();
if( mRot )
{
fGrandMotherExitNormal *= (*mRot).inverse();
fChangedGrandMotherRefFrame = true;
}
}
} // 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 = GetVoxelNavigator().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;
case kExternal:
noResult = fpExternalNav->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 )
{
++noLevelsEntered;
// Entering a daughter after ascending
//
// The blocked volume is no longer valid - it was for another level
//
fBlockedPhysicalVolume = nullptr;
fBlockedReplicaNo = -1;
// fEntering should be false -- else blockedVolume is assumed good.
// fEnteredDaughter is used for ExitNormal
//
fEntering = false;
fEnteredDaughter = true;
if( fExitedMother )
{
G4VPhysicalVolume* enteredPhysical = fHistory.GetTopVolume();
const G4RotationMatrix* mRot = enteredPhysical->GetRotation();
if( mRot )
{
// Go deeper, i.e. move 'down' in the hierarchy
// Apply direct rotation, not inverse
//
fGrandMotherExitNormal *= (*mRot);
fChangedGrandMotherRefFrame= true;
}
}
#ifdef G4DEBUG_NAVIGATION
if( fVerbose > 2 )
{
G4VPhysicalVolume* enteredPhysical = fHistory.GetTopVolume();
G4cout << "*** G4Navigator::LocateGlobalPointAndSetup() ***" << G4endl;
G4cout << " Entering volume: " << enteredPhysical->GetName()
<< G4endl;
}
#endif
}
} while (noResult); // Loop checking, 07.10.2016, J.Apostolakis
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();
if( fVerbose >= 5 )
{
G4cout << "Upon exiting LocateGlobalPointAndSetup():" << G4endl;
G4cout << " History = " << G4endl << fHistory << G4endl << G4endl;
}
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
G4Navigator::LocateGlobalPointWithinVolume(const G4ThreeVector& pGlobalpoint)
{
#ifdef G4DEBUG_NAVIGATION
assert( !fWasLimitedByGeometry );
// Check: Either step was not limited by a boundary or
// else the full step is no longer being taken
#endif
fLastLocatedPointLocal = ComputeLocalPoint(pGlobalpoint);
fLastTriedStepComputation = false;
fChangedGrandMotherRefFrame = false; // Frame for Exit Normal
// 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();
switch( CharacteriseDaughters(motherLogical) )
{
case kNormal:
if ( pVoxelHeader )
{
GetVoxelNavigator().VoxelLocate( pVoxelHeader, fLastLocatedPointLocal );
}
break;
case kParameterised:
if( GetDaughtersRegularStructureId(motherLogical) != 1 )
{
// Resets state & returns voxel node
//
fparamNav.ParamVoxelLocate( pVoxelHeader, fLastLocatedPointLocal );
}
break;
case kReplica:
// Nothing to do
break;
case kExternal:
fpExternalNav->RelocateWithinVolume( motherPhysical,
fLastLocatedPointLocal );
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 = nullptr;
fBlockedReplicaNo = -1;
fEntering = false;
fEnteredDaughter = false; // Boundary not encountered, did not enter
fExiting = false;
fExitedMother = false; // Boundary not encountered, did not exit
}
// ********************************************************************
// SetSavedState
//
// Save the state, in case this is a parasitic call
// Save fValidExitNormal, fExitNormal, fExiting, fEntering,
// fBlockedPhysicalVolume, fBlockedReplicaNo, fLastStepWasZero;
// ********************************************************************
//
void G4Navigator::SetSavedState()
{
// Note: the state of dependent objects is not currently saved.
// ( This means that the full state is changed by calls between
// SetSavedState() and RestoreSavedState();
fSaveState.sExitNormal = fExitNormal;
fSaveState.sValidExitNormal = fValidExitNormal;
fSaveState.sExiting = fExiting;
fSaveState.sEntering = fEntering;
fSaveState.spBlockedPhysicalVolume = fBlockedPhysicalVolume;
fSaveState.sBlockedReplicaNo = fBlockedReplicaNo;
fSaveState.sLastStepWasZero = fLastStepWasZero;
fSaveState.sLocatedOutsideWorld = fLocatedOutsideWorld;
fSaveState.sLastLocatedPointLocal = fLastLocatedPointLocal;
fSaveState.sEnteredDaughter = fEnteredDaughter;
fSaveState.sExitedMother = fExitedMother;
fSaveState.sWasLimitedByGeometry = fWasLimitedByGeometry;
// Even the safety sphere - if you want to change it do it explicitly!
//
fSaveState.sPreviousSftOrigin = fPreviousSftOrigin;
fSaveState.sPreviousSafety = fPreviousSafety;
}
// ********************************************************************
// RestoreSavedState
//
// Restore the state (in Compute Step), in case this is a parasitic call
// ********************************************************************
//
void G4Navigator::RestoreSavedState()
{
fExitNormal = fSaveState.sExitNormal;
fValidExitNormal = fSaveState.sValidExitNormal;
fExiting = fSaveState.sExiting;
fEntering = fSaveState.sEntering;
fBlockedPhysicalVolume = fSaveState.spBlockedPhysicalVolume;
fBlockedReplicaNo = fSaveState.sBlockedReplicaNo;
fLastStepWasZero = fSaveState.sLastStepWasZero;
fLocatedOutsideWorld = fSaveState.sLocatedOutsideWorld;
fLastLocatedPointLocal = fSaveState.sLastLocatedPointLocal;
fEnteredDaughter = fSaveState.sEnteredDaughter;
fExitedMother = fSaveState.sExitedMother;
fWasLimitedByGeometry = fSaveState.sWasLimitedByGeometry;
// The 'expected' behaviour is to restore these too (fix 2014.05.26)
fPreviousSftOrigin = fSaveState.sPreviousSftOrigin;
fPreviousSafety = fSaveState.sPreviousSafety;
}
// ********************************************************************
// ComputeStep
//
// Computes the next geometric Step: intersections with current
// mother and `daughter' volumes.
//
// NOTE:
//
// Flags on entry:
// --------------
// fValidExitNormal - Normal of exited volume is valid (convex, not a
// coincident boundary)
// fExitNormal - Surface normal of exited volume
// fExiting - True if have exited solid
//
// fBlockedPhysicalVolume - Ptr to exited volume (or 0)
// fBlockedReplicaNo - Replication no of exited volume
// fLastStepWasZero - True if last Step size was almost zero.
//
// Flags on exit:
// -------------
// fValidExitNormal - True if surface normal of exited volume is valid
// fExitNormal - Surface normal of exited volume rotated to mothers
// reference system
// fExiting - True if exiting mother
// fEntering - True if entering `daughter' volume (or replica)
// fBlockedPhysicalVolume - Ptr to candidate (entered) volume
// fBlockedReplicaNo - Replication no of candidate (entered) volume
// fLastStepWasZero - True if this Step size was almost zero.
// ********************************************************************
//
G4double G4Navigator::ComputeStep( const G4ThreeVector& pGlobalpoint,
const G4ThreeVector& pDirection,
const G4double pCurrentProposedStepLength,
G4double& pNewSafety)
{
#ifdef G4DEBUG_NAVIGATION
static G4ThreadLocal G4int sNavCScalls = 0;
++sNavCScalls;
#endif
G4ThreeVector localDirection = ComputeLocalAxis(pDirection);
G4double Step = kInfinity;
G4VPhysicalVolume *motherPhysical = fHistory.GetTopVolume();
G4LogicalVolume *motherLogical = motherPhysical->GetLogicalVolume();
// All state relating to exiting normals must be reset
//
fExitNormalGlobalFrame = G4ThreeVector( 0., 0., 0.);
// Reset value - to erase its memory
fChangedGrandMotherRefFrame = false;
// Reset - used for local exit normal
fGrandMotherExitNormal = G4ThreeVector( 0., 0., 0.);
fCalculatedExitNormal = false;
// Reset for new step
#ifdef G4VERBOSE
if( fVerbose > 0 )
{
G4cout << "*** G4Navigator::ComputeStep: ***" << G4endl;
G4cout << " Volume = " << motherPhysical->GetName()
<< " - Proposed step length = " << pCurrentProposedStepLength
<< G4endl;
#ifdef G4DEBUG_NAVIGATION
if( fVerbose >= 2 )
{
G4cout << " Called with the arguments: " << G4endl
<< " Globalpoint = " << std::setw(25) << pGlobalpoint << G4endl
<< " Direction = " << std::setw(25) << pDirection << G4endl;
if( fVerbose >= 4 )
{
G4cout << " ---- Upon entering : State" << 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 >= fSqTol )
{
#ifdef G4VERBOSE
ComputeStepLog(pGlobalpoint, moveLenSq);
#endif
// Relocate the point within the same volume
//
LocateGlobalPointWithinVolume( pGlobalpoint );
}
}
if ( fHistory.GetTopVolumeType()!=kReplica )
{
switch( CharacteriseDaughters(motherLogical) )
{
case kNormal:
if ( motherLogical->GetVoxelHeader() )
{
Step = GetVoxelNavigator().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 );
//
// 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("G4Navigator::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("G4Navigator::ComputeStep()", "GeomNav0001",
FatalException, "Not applicable for replicated volumes.");
break;
case kExternal:
Step = fpExternalNav->ComputeStep(fLastLocatedPointLocal,
localDirection,
pCurrentProposedStepLength,
pNewSafety,
fHistory,
fValidExitNormal,
fExitNormal,
fExiting,
fEntering,
&fBlockedPhysicalVolume,
fBlockedReplicaNo);
break;
}
}
else
{
// In the case of a replica, it must handle the exiting
// edge/corner problem by itself
//
fExiting = fExitedMother;
Step = freplicaNav.ComputeStep(pGlobalpoint,
pDirection,
fLastLocatedPointLocal,
localDirection,
pCurrentProposedStepLength,
pNewSafety,
fHistory,
fValidExitNormal,
fCalculatedExitNormal,
fExitNormal,
fExiting,
fEntering,
&fBlockedPhysicalVolume,
fBlockedReplicaNo);
}
// 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<fMinStep);
if (fPushed) { fPushed = fLastStepWasZero; }
// Handle large number of consecutive zero steps
//
if ( fLastStepWasZero )
{
++fNumberZeroSteps;
G4bool act = fNumberZeroSteps >= fActionThreshold_NoZeroSteps;
G4bool actAndReport = false;
G4bool abandon = fNumberZeroSteps >= fAbandonThreshold_NoZeroSteps;
G4bool inform = false;
#ifdef G4VERBOSE
actAndReport = act && (!fPushed) && fWarnPush;
#endif
#ifdef G4DEBUG_NAVIGATION
inform = fNumberZeroSteps > 1;
#endif
if ( act || inform )
{
if( act && !abandon )
{
// Act to recover this stuck track. Pushing it along original direction
//
Step += 100*kCarTolerance;
fPushed = true;
}
if( actAndReport || abandon || inform )
{
std::ostringstream message;
message.precision(16);
message << "Stuck Track: potential geometry or navigation problem."
<< G4endl;
message << " Track stuck, not moving for "
<< fNumberZeroSteps << " steps." << G4endl
<< " Current phys volume: '" << motherPhysical->GetName()
<< "'" << G4endl
<< " - at position : " << pGlobalpoint << G4endl
<< " in direction: " << pDirection << G4endl
<< " (local position: " << newLocalPoint << ")" << G4endl
<< " (local direction: " << localDirection << ")." << G4endl
<< " Previous phys volume: '"
<< ( fLastMotherPhys ? fLastMotherPhys->GetName() : "" )
<< "'" << G4endl << G4endl;
if( actAndReport || abandon )
{
message << " Likely geometry overlap - else navigation problem !"
<< G4endl;
}
if( abandon ) // i.e. fNumberZeroSteps >= fAbandonThreshold_NoZeroSteps
{
// Must kill this stuck track
#ifdef G4VERBOSE
if ( fWarnPush ) { CheckOverlapsIterative(motherPhysical); }
#endif
message << " Track *abandoned* due to excessive number of Zero steps."
<< " Event aborted. " << G4endl << G4endl;
G4Exception("G4Navigator::ComputeStep()", "GeomNav0003",
EventMustBeAborted, message);
}
else
{
#ifdef G4VERBOSE
if ( actAndReport ) // (!fPushed => !wasPushed) && (fWarnPush))
{
message << " *** Trying to get *unstuck* using a push"
<< " - expanding step to " << Step << " (mm) ..."
<< " Potential overlap in geometry !" << G4endl;
G4Exception("G4Navigator::ComputeStep()", "GeomNav1002",
JustWarning, message);
}
#endif
#ifdef G4DEBUG_NAVIGATION
else
{
if( fNumberZeroSteps > 1 )
{
message << ", nav-comp-step calls # " << sNavCScalls
<< ", Step= " << Step << G4endl;
G4cout << message.str();
}
}
#endif
} // end of else if ( abandon )
} // end of if( actAndReport || abandon || inform )
} // end of if ( act || inform )
}
else
{
if (!fPushed) { fNumberZeroSteps = 0; }
}
fLastMotherPhys = motherPhysical;
fEnteredDaughter = fEntering; // I expect to enter a volume in this Step
fExitedMother = fExiting;
fStepEndPoint = pGlobalpoint
+ std::min(Step,pCurrentProposedStepLength) * 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 || fCalculatedExitNormal )
{
// 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;
if ( fHistory.GetTopVolumeType() != kReplica )
{
// Find normal in the 'mother' coordinate system
//
G4ThreeVector exitNormalMotherFrame=
motherLogical->GetSolid()->SurfaceNormal(finalLocalPoint);
// Transform it to the 'grand-mother' coordinate system
//
const G4RotationMatrix* mRot = motherPhysical->GetRotation();
if( mRot )
{
fChangedGrandMotherRefFrame = true;
fGrandMotherExitNormal = (*mRot).inverse() * exitNormalMotherFrame;
}
else
{
fGrandMotherExitNormal = exitNormalMotherFrame;
}
// Do not set fValidExitNormal -- this signifies
// that the solid is convex!
}
else
{
fCalculatedExitNormal = false;
//
// Nothing can be done at this stage currently - to solve this
// Replica Navigation must have calculated the normal for this case
// already.
// Cases: mother is not convex, and exit is at previous replica level
#ifdef G4DEBUG_NAVIGATION
G4ExceptionDescription desc;
desc << "Problem in ComputeStep: Replica Navigation did not provide"
<< " valid exit Normal. " << G4endl;
desc << " Do not know how calculate it in this case." << G4endl;
desc << " Location = " << finalLocalPoint << G4endl;
desc << " Volume name = " << motherPhysical->GetName()
<< " copy/replica No = " << motherPhysical->GetCopyNo() << G4endl;
G4Exception("G4Navigator::ComputeStep()", "GeomNav0003",
JustWarning, desc, "Normal not available for exiting.");
#endif
}
}
if ( fHistory.GetTopVolumeType() != kReplica )
fCalculatedExitNormal = true;
// Now transform it to the global reference frame !!
//
if( fValidExitNormal || fCalculatedExitNormal )
{
G4int depth = fHistory.GetDepth();
if( depth > 0 )
{
fExitNormalGlobalFrame = fHistory.GetTransform(depth-1)
.InverseTransformAxis( fGrandMotherExitNormal );
}
else
{
fExitNormalGlobalFrame = fGrandMotherExitNormal;
}
}
else
{
fExitNormalGlobalFrame = G4ThreeVector( 0., 0., 0.);
}
}
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;
if( fVerbose > 5 ) { G4cout << G4endl; }
if( Step == kInfinity )
{
G4cout << " Requested step= " << pCurrentProposedStepLength ;
if( fVerbose > 5) { G4cout << G4endl; }
}
G4cout << " Safety = " << pNewSafety << G4endl;
}
#endif
fLastTriedStepComputation = true;
return Step;
}
// ********************************************************************
// CheckNextStep
//
// Compute the step without altering the navigator state
// ********************************************************************
//
G4double G4Navigator::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 );
// It is a parasitic call, so attempt to restore the key parts of the state
//
RestoreSavedState();
// NOTE: the state of the current subnavigator is NOT restored.
// ***> TODO: restore subnavigator state
// if( last_located) Need Position of last location
// if( last_computed step) Need Endposition of last step
return step;
}
// ********************************************************************
// ResetState
//
// Resets stack and minimum of navigator state `machine'
// ********************************************************************
//
void G4Navigator::ResetState()
{
fWasLimitedByGeometry = false;
fEntering = false;
fExiting = false;
fLocatedOnEdge = false;
fLastStepWasZero = false;
fEnteredDaughter = false;
fExitedMother = false;
fPushed = false;
fValidExitNormal = false;
fChangedGrandMotherRefFrame = false;
fCalculatedExitNormal = false;
fExitNormal = G4ThreeVector(0,0,0);
fGrandMotherExitNormal = G4ThreeVector(0,0,0);
fExitNormalGlobalFrame = G4ThreeVector(0,0,0);
fPreviousSftOrigin = G4ThreeVector(0,0,0);
fPreviousSafety = 0.0;
fNumberZeroSteps = 0;
fBlockedPhysicalVolume = nullptr;
fBlockedReplicaNo = -1;
fLastLocatedPointLocal = G4ThreeVector( kInfinity, -kInfinity, 0.0 );
fLocatedOutsideWorld = false;
fLastMotherPhys = nullptr;
}
// ********************************************************************
// SetupHierarchy
//
// Renavigates & resets hierarchy described by current history
// o Reset volumes
// o Recompute transforms and/or solids of replicated/parameterised volumes
// ********************************************************************
//
void G4Navigator::SetupHierarchy()
{
const G4int depth = fHistory.GetDepth();
for ( auto i = 1; i <= depth; ++i )
{
switch ( fHistory.GetVolumeType(i) )
{
case kNormal:
case kExternal:
break;
case kReplica:
freplicaNav.ComputeTransformation(fHistory.GetReplicaNo(i), fHistory.GetVolume(i));
break;
case kParameterised:
G4VPhysicalVolume* current = fHistory.GetVolume(i);
G4int replicaNo = fHistory.GetReplicaNo(i);
G4VPVParameterisation* pParam = current->GetParameterisation();
G4VSolid* pSolid = pParam->ComputeSolid(replicaNo, current);
// Set up dimensions & transform in solid/physical volume
//
pSolid->ComputeDimensions(pParam, replicaNo, current);
pParam->ComputeTransformation(replicaNo, current);
G4TouchableHistory* pTouchable = nullptr;
if( pParam->IsNested() )
{
pTouchable= new G4TouchableHistory( fHistory );
pTouchable->MoveUpHistory(); // Move up to the parent level
// Adequate only if Nested at the Branch level (last)
// To extend to other cases:
// pTouchable->MoveUpHistory(cdepth-i-1);
// Move to the parent level of *Current* level
// Could replace this line and constructor with a revised
// c-tor for History(levels to drop)
}
// Set up the correct solid and material in Logical Volume
//
G4LogicalVolume* pLogical = current->GetLogicalVolume();
pLogical->SetSolid( pSolid );
pLogical->UpdateMaterial( pParam ->
ComputeMaterial(replicaNo, current, pTouchable) );
delete pTouchable;
break;
}
}
}
// ********************************************************************
// GetLocalExitNormal
//
// Obtains the Normal vector to a surface (in local coordinates)
// pointing out of previous volume and into current volume
// ********************************************************************
//
G4ThreeVector G4Navigator::GetLocalExitNormal( G4bool* valid )
{
G4ThreeVector ExitNormal(0.,0.,0.);
G4VSolid* currentSolid = nullptr;
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.
// The following code should also work in case of Replica
{
// 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
//
// First flip ( ExitNormal = -nextSolidExitNormal; )
// and then rotate the the normal to the frame of the mother (current volume)
ExitNormal = MotherToDaughterTransform
.InverseTransformAxis( -nextSolidExitNormal );
fCalculatedExitNormal = true;
}
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("G4Navigator::GetLocalExitNormal()", "GeomNav1001",
JustWarning, message);
}
#endif
}
*valid = onSurface; // was =true;
}
}
}
else if ( fExiting )
{
ExitNormal = fGrandMotherExitNormal;
*valid = true;
fCalculatedExitNormal = true; // Should be true already
}
else // i.e. ( fBlockedPhysicalVolume == 0 )
{
*valid = false;
G4Exception("G4Navigator::GetLocalExitNormal()",
"GeomNav0003", JustWarning,
"Incorrect call to GetLocalSurfaceNormal." );
}
}
else // ( ! fLastTriedStepComputation ) i.e. last call was to Locate
{
if ( EnteredDaughterVolume() )
{
G4VSolid* daughterSolid = fHistory.GetTopVolume()->GetLogicalVolume()
->GetSolid();
ExitNormal = -(daughterSolid->SurfaceNormal(fLastLocatedPointLocal));
if( std::fabs(ExitNormal.mag2()-1.0 ) > kToleranceNormalCheck )
{
G4ExceptionDescription desc;
desc << " Parameters of solid: " << *daughterSolid
<< " Point for surface = " << fLastLocatedPointLocal << std::endl;
G4Exception("G4Navigator::GetLocalExitNormal()",
"GeomNav0003", FatalException, desc,
"Surface Normal returned by Solid is not a Unit Vector." );
}
fCalculatedExitNormal = true;
*valid = true;
}
else
{
if( fExitedMother )
{
ExitNormal = fGrandMotherExitNormal;
*valid = true;
fCalculatedExitNormal = true;
}
else // We are not at a boundary. ExitNormal remains (0,0,0)
{
*valid = false;
fCalculatedExitNormal = false;
G4ExceptionDescription message;
message << "Function called when *NOT* at a Boundary." << G4endl;
message << "Exit Normal not calculated." << G4endl;
G4Exception("G4Navigator::GetLocalExitNormal()",
"GeomNav0003", JustWarning, message);
}
}
}
return ExitNormal;
}
// ********************************************************************
// GetMotherToDaughterTransform
//
// Obtains the mother to daughter affine transformation
// ********************************************************************
//
G4AffineTransform
G4Navigator::GetMotherToDaughterTransform( G4VPhysicalVolume* pEnteringPhysVol,
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("G4Navigator::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;
case kExternal:
// Expect that nothing is needed to prepare the transformation.
// It is stored already in the physical volume (placement)
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
G4Navigator::GetLocalExitNormalAndCheck(
#ifdef G4DEBUG_NAVIGATION
const G4ThreeVector& ExpectedBoundaryPointGlobal,
#else
const G4ThreeVector&,
#endif
G4bool* pValid)
{
#ifdef G4DEBUG_NAVIGATION
// Check Current point against expected 'local' value
//
if ( fLastTriedStepComputation )
{
G4ThreeVector ExpectedBoundaryPointLocal;
const G4AffineTransform& GlobalToLocal = GetGlobalToLocalTransform();
ExpectedBoundaryPointLocal =
GlobalToLocal.TransformPoint( ExpectedBoundaryPointGlobal );
// Add here: Comparison against expected position,
// i.e. the endpoint of ComputeStep
}
#endif
return GetLocalExitNormal( pValid );
}
// ********************************************************************
// GetGlobalExitNormal
//
// Obtains the Normal vector to a surface (in global coordinates)
// pointing out of previous volume and into current volume
// ********************************************************************
//
G4ThreeVector
G4Navigator::GetGlobalExitNormal(const G4ThreeVector& IntersectPointGlobal,
G4bool* pNormalCalculated)
{
G4bool validNormal;
G4ThreeVector localNormal, globalNormal;
G4bool usingStored = fCalculatedExitNormal && (
( fLastTriedStepComputation && fExiting ) // Just calculated it
|| // No locate in between
( !fLastTriedStepComputation
&& (IntersectPointGlobal-fStepEndPoint).mag2() < 10.0*fSqTol ) );
// Calculated it 'just' before & then called locate
// but it did not move position
if( usingStored )
{
// This was computed in last call to ComputeStep
// and only if it arrived at boundary
//
globalNormal = fExitNormalGlobalFrame;
G4double normMag2 = globalNormal.mag2();
if( std::fabs ( normMag2 - 1.0 ) < perThousand ) // was perMillion
{
*pNormalCalculated = true; // ComputeStep always computes it if Exiting
// (fExiting==true)
}
else
{
G4ExceptionDescription message;
message.precision(10);
message << " WARNING> Expected normal-global-frame to be valid, "
<< " i.e. a unit vector!" << G4endl
<< " - but |normal| = " << std::sqrt(normMag2)
<< " - and |normal|^2 = " << normMag2 << G4endl
<< " which differs from 1.0 by " << normMag2 - 1.0 << G4endl
<< " n = " << fExitNormalGlobalFrame << G4endl
<< " Global point: " << IntersectPointGlobal << G4endl
<< " Volume: " << fHistory.GetTopVolume()->GetName() << G4endl;
#ifdef G4VERBOSE
G4LogicalVolume* candLog = fHistory.GetTopVolume()->GetLogicalVolume();
if ( candLog )
{
message << " Solid: " << candLog->GetSolid()->GetName()
<< ", Type: " << candLog->GetSolid()->GetEntityType() << G4endl
<< *candLog->GetSolid() << G4endl;
}
#endif
message << "============================================================"
<< G4endl;
G4int oldVerbose = fVerbose;
fVerbose = 4;
message << " State of Navigator: " << G4endl;
message << *this << G4endl;
fVerbose = oldVerbose;
message << "============================================================"
<< G4endl;
G4Exception("G4Navigator::GetGlobalExitNormal()",
"GeomNav0003",JustWarning, message,
"Value obtained from stored global-normal is not a unit vector.");
// (Re)Compute it now -- as either it was not computed, or it is wrong.
//
localNormal = GetLocalExitNormalAndCheck(IntersectPointGlobal,
&validNormal);
*pNormalCalculated = fCalculatedExitNormal;
globalNormal = fHistory.GetTopTransform()
.InverseTransformAxis(localNormal);
}
}
else
{
localNormal = GetLocalExitNormalAndCheck(IntersectPointGlobal,&validNormal);
*pNormalCalculated = fCalculatedExitNormal;
#ifdef G4DEBUG_NAVIGATION
usingStored = false;
if( (!validNormal) && !fCalculatedExitNormal )
{
G4ExceptionDescription edN;
edN << " Calculated = " << fCalculatedExitNormal << G4endl;
edN << " Entering= " << fEntering << G4endl;
G4int oldVerbose = this->GetVerboseLevel();
this->SetVerboseLevel(4);
edN << " State of Navigator: " << G4endl;
edN << *this << G4endl;
this->SetVerboseLevel( oldVerbose );
G4Exception("G4Navigator::GetGlobalExitNormal()",
"GeomNav0003", JustWarning, edN,
"LocalExitNormalAndCheck() did not calculate Normal.");
}
#endif
G4double localMag2 = localNormal.mag2();
if( validNormal && (std::fabs(localMag2-1.0)) > kToleranceNormalCheck )
{
G4ExceptionDescription edN;
edN.precision(10);
edN << "G4Navigator::GetGlobalExitNormal: "
<< " Using Local Normal - from call to GetLocalExitNormalAndCheck. "
<< G4endl
<< " Local Exit Normal : " << " || = " << std::sqrt(localMag2)
<< " vec = " << localNormal << G4endl
<< " Global Exit Normal : " << " || = " << globalNormal.mag()
<< " vec = " << globalNormal << G4endl
<< " Global point: " << IntersectPointGlobal << G4endl;
edN << " Calculated It = " << fCalculatedExitNormal << G4endl
<< " Volume: " << fHistory.GetTopVolume()->GetName() << G4endl;
#ifdef G4VERBOSE
G4LogicalVolume* candLog = fHistory.GetTopVolume()->GetLogicalVolume();
if ( candLog )
{
edN << " Solid: " << candLog->GetSolid()->GetName()
<< ", Type: " << candLog->GetSolid()->GetEntityType() << G4endl
<< *candLog->GetSolid();
}
#endif
G4Exception("G4Navigator::GetGlobalExitNormal()",
"GeomNav0003",JustWarning, edN,
"Value obtained from new local *solid* is incorrect.");
localNormal = localNormal.unit(); // Should we correct it ??
}
globalNormal = fHistory.GetTopTransform()
.InverseTransformAxis(localNormal);
}
#ifdef G4DEBUG_NAVIGATION
if( usingStored )
{
G4ThreeVector globalNormAgn;
localNormal = GetLocalExitNormalAndCheck(IntersectPointGlobal,&validNormal);
globalNormAgn = fHistory.GetTopTransform()
.InverseTransformAxis(localNormal);
// Check the value computed against fExitNormalGlobalFrame
G4ThreeVector diffNorm = globalNormAgn - fExitNormalGlobalFrame;
if( diffNorm.mag2() > kToleranceNormalCheck )
{
G4ExceptionDescription edDfn;
edDfn << "Found difference in normals in case of exiting mother "
<< "- when Get is called after ComputingStep " << G4endl;
edDfn << " Magnitude of diff = " << diffNorm.mag() << G4endl;
edDfn << " Normal stored (Global) = " << fExitNormalGlobalFrame
<< G4endl;
edDfn << " Global Computed from Local = " << globalNormAgn << G4endl;
G4Exception("G4Navigator::GetGlobalExitNormal()", "GeomNav0003",
JustWarning, edDfn);
}
}
#endif
// Synchronise stored global exit normal as possibly re-computed here
//
fExitNormalGlobalFrame = globalNormal;
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 G4Navigator::ComputeSafety( const G4ThreeVector& pGlobalpoint,
const G4double pMaxLength,
const G4bool keepState)
{
#ifdef G4DEBUG_NAVIGATION
G4int oldcoutPrec = G4cout.precision(8);
if( fVerbose > 0 )
{
G4cout << "*** G4Navigator::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
G4double distEndpointSq = (pGlobalpoint-fStepEndPoint).mag2();
G4bool stayedOnEndpoint = distEndpointSq < sqr(kCarTolerance);
G4bool endpointOnSurface = fEnteredDaughter || fExitedMother;
if( endpointOnSurface && stayedOnEndpoint )
{
#ifdef G4DEBUG_NAVIGATION
if( fVerbose >= 2 )
{
G4cout << " G4Navigator::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;
G4cout << " ---- Exiting ComputeSafety " << G4endl;
PrintState();
G4cout << " Returned value of Safety is zero " << G4endl;
G4cout.precision(oldcoutPrec);
}
#endif
return 0.0;
}
G4double newSafety = 0.0;
if (keepState) { SetSavedState(); }
// 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 << " G4Navigator::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 = fpVoxelSafety->ComputeSafety(localPoint,
*motherPhysical, pMaxLength);
// = VoxelNav().ComputeSafety(localPoint,fHistory,pMaxLength); // - Old method
}
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("G4Navigator::ComputeSafety()", "GeomNav0001",
FatalException, "Not applicable for replicated volumes.");
break;
case kExternal:
newSafety = fpExternalNav->ComputeSafety(localPoint, fHistory,
pMaxLength);
break;
}
}
else
{
newSafety = freplicaNav.ComputeSafety(pGlobalpoint, localPoint,
fHistory, pMaxLength);
}
if (keepState)
{
RestoreSavedState();
// This now overwrites the values of the Safety 'sphere' (correction)
}
// Remember last safety origin & value
//
// We overwrite the Safety 'sphere' - keeping old behaviour
fPreviousSftOrigin = pGlobalpoint;
fPreviousSafety = newSafety;
#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 G4Navigator::CreateTouchableHistoryHandle() const
{
return G4TouchableHistoryHandle( CreateTouchableHistory() );
}
// ********************************************************************
// PrintState
// ********************************************************************
//
void G4Navigator::PrintState() const
{
G4int oldcoutPrec = G4cout.precision(4);
if( fVerbose >= 4 )
{
G4cout << "The current state of G4Navigator 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 << G4endl; // Make sure to line up
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 == nullptr )
{ 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 G4Navigator::ComputeStepLog(const G4ThreeVector& pGlobalpoint,
G4double moveLenSq) const
{
// The following checks only make sense if the move is larger
// than the tolerance.
const G4double fAccuracyForWarning = kCarTolerance,
fAccuracyForException = 1000*kCarTolerance;
G4ThreeVector OriginalGlobalpoint = fHistory.GetTopTransform().
InverseTransformPoint(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("G4Navigator::ComputeStep()",
"GeomNav1002", JustWarning,
message, G4String(suggestion.str()));
G4cout.precision(oldcoutPrec);
G4cerr.precision(oldcerrPrec);
}
#ifdef G4DEBUG_NAVIGATION
else
{
G4cerr << "WARNING - G4Navigator::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("G4Navigator::ComputeStep()", "GeomNav1002",
JustWarning, message);
}
}
// ********************************************************************
// CheckOverlapsIterative
// ********************************************************************
//
G4bool G4Navigator::CheckOverlapsIterative(G4VPhysicalVolume* vol)
{
// Check and report overlaps
//
G4bool foundOverlap = false;
G4int nPoints = 300000, ntrials = 9, numOverlaps = 5;
G4double trialLength = 1.0 * CLHEP::centimeter;
while ( ntrials-- > 0 && !foundOverlap )
{
if ( fVerbose > 1 )
{
G4cout << " ** Running overlap checks in volume "
<< vol->GetName()
<< " with length = " << trialLength << G4endl;
}
foundOverlap = vol->CheckOverlaps(nPoints, trialLength,
fVerbose, numOverlaps);
trialLength *= 0.1;
if ( trialLength <= 1.0e-5 ) { numOverlaps= 1;}
}
return foundOverlap;
}
// ********************************************************************
// Operator <<
// ********************************************************************
//
std::ostream& operator << (std::ostream &os,const G4Navigator &n)
{
// Old version did only the following:
// os << "Current History: " << G4endl << n.fHistory;
// Old behaviour is recovered for fVerbose = 0
// Adapted from G4Navigator::PrintState() const
G4int oldcoutPrec = os.precision(4);
if( n.fVerbose >= 4 )
{
os << "The current state of G4Navigator is: " << G4endl;
os << " ValidExitNormal= " << n.fValidExitNormal << G4endl
<< " ExitNormal = " << n.fExitNormal << G4endl
<< " Exiting = " << n.fExiting << G4endl
<< " Entering = " << n.fEntering << G4endl
<< " BlockedPhysicalVolume= " ;
if (n.fBlockedPhysicalVolume==0)
os << "None";
else
os << n.fBlockedPhysicalVolume->GetName();
os << G4endl
<< " BlockedReplicaNo = " << n.fBlockedReplicaNo << G4endl
<< " LastStepWasZero = " << n.fLastStepWasZero << G4endl
<< G4endl;
}
if( ( 1 < n.fVerbose) && (n.fVerbose < 4) )
{
os << G4endl; // Make sure to line up
os << 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;
os << "( " << std::setw(7) << n.fExitNormal.x()
<< ", " << std::setw(7) << n.fExitNormal.y()
<< ", " << std::setw(7) << n.fExitNormal.z() << " ) "
<< std::setw( 5) << n.fValidExitNormal << " "
<< std::setw( 9) << n.fExiting << " "
<< std::setw( 9) << n.fEntering << " ";
if ( n.fBlockedPhysicalVolume==0 )
{ os << std::setw(15) << "None"; }
else
{ os << std::setw(15)<< n.fBlockedPhysicalVolume->GetName(); }
os << std::setw( 9) << n.fBlockedReplicaNo << " "
<< std::setw( 8) << n.fLastStepWasZero << " "
<< G4endl;
}
if( n.fVerbose > 2 )
{
os.precision(8);
os << " Current Localpoint = " << n.fLastLocatedPointLocal << G4endl;
os << " PreviousSftOrigin = " << n.fPreviousSftOrigin << G4endl;
os << " PreviousSafety = " << n.fPreviousSafety << G4endl;
}
if( n.fVerbose > 3 || n.fVerbose == 0 )
{
os << "Current History: " << G4endl << n.fHistory;
}
os.precision(oldcoutPrec);
return os;
}
#ifdef ALTERNATIVE_VOXEL_NAV
// ********************************************************************
// SetVoxelNavigation -- alternative navigator for Voxel geom
// ********************************************************************
//
void G4Navigator::SetVoxelNavigation(G4VoxelNavigation* voxelNav)
{
delete fpvoxelNav;
fpvoxelNav = voxelNav;
}
#endif
// ********************************************************************
// InformLastStep: Derived navigators can inform of its step
// - used to update fLastStepWasZero
// ********************************************************************
void G4Navigator::InformLastStep(G4double lastStep, G4bool entersDaughtVol, G4bool exitsMotherVol )
{
G4bool zeroStep = ( lastStep == 0.0 );
fLocatedOnEdge = fLastStepWasZero && zeroStep;
fLastStepWasZero = zeroStep;
fExiting = exitsMotherVol;
fEntering = entersDaughtVol;
}