Files
geant4/source/geometry/management/src/G4SmartVoxelHeader.cc
T
2016-06-08 16:03:00 +02:00

1190 lines
33 KiB
C++

// This code implementation is the intellectual property of
// the GEANT4 collaboration.
//
// By copying, distributing or modifying the Program (or any work
// based on the Program) you indicate your acceptance of this statement,
// and all its terms.
//
// $Id: G4SmartVoxelHeader.cc,v 1.7 2000/11/20 17:31:34 gcosmo Exp $
// GEANT4 tag $Name: geant4-03-01 $
//
//
// class G4SmartVoxelHeader
//
// Implementation
//
// Define G4GEOMETRY_VOXELDEBUG for debugging information on G4cout
//
// History:
// 12.02.99 Introduction of new quality/smartless: max for (slices/candid) S.G.
// 11.02.99 Voxels at lower levels are now built for collapsed slices S.G.
// 21.07.95 Full implementation, supporting non divided physical volumes
// 14.07.95 Initial version - stubb definitions only
#include "G4SmartVoxelHeader.hh"
#include "G4LogicalVolume.hh"
#include "G4VPhysicalVolume.hh"
#include "G4VoxelLimits.hh"
#include "G4AffineTransform.hh"
#include "G4VSolid.hh"
#include "G4VPVParameterisation.hh"
// Constructor for topmost header, to begin voxel construction at a
// given logical volume
//
// Process:
//
// Constructs target List of volumes, calls "Build and refine" constructor.
// Assumes all daughters represent single volumes (ie. no divisions
// or parametric)
G4SmartVoxelHeader::G4SmartVoxelHeader(G4LogicalVolume* pVolume,
const G4int pSlice) :
fminEquivalent(pSlice),
fmaxEquivalent(pSlice)
{
G4int nDaughters;
G4VoxelLimits limits; // Create `unlimited' limits object
nDaughters=pVolume->GetNoDaughters();
// Determine whether daughter is replicated
if (nDaughters!=1||!pVolume->GetDaughter(0)->IsReplicated())
{
// Daughter not replicated => conventional voxel Build
// where each daughters extents are computed
BuildVoxels(pVolume);
}
else
{
// Single replicated daughter
BuildReplicaVoxels(pVolume);
}
}
G4SmartVoxelHeader::G4SmartVoxelHeader(G4LogicalVolume* pVolume,
const G4VoxelLimits& pLimits,
const G4VolumeNosVector* pCandidates,
const G4int pSlice) :
fminEquivalent(pSlice),
fmaxEquivalent(pSlice)
{
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << "**** G4SmartVoxelHeader::G4SmartVoxelHeader" << G4endl
<< " Limits " << pLimits << G4endl
<< " Candidate #s = " ;
for (G4int i=0;i<pCandidates->entries();i++)
{
G4cout << pCandidates->at(i) << " ";
}
G4cout << G4endl;
#endif
BuildVoxelsWithinLimits(pVolume,pLimits,pCandidates);
}
// Return true if contents are equivalent. Implies
// a deep search through contained nodes/header
//
// Process:
//
// Compare headers' axes,sizes,extents. Return false if different
// For each contained proxy, determine whether node/header, compare and return
// if different. Compare and return if proxied nodes/headers are different
G4bool G4SmartVoxelHeader::operator == (const G4SmartVoxelHeader& pHead) const
{
if (GetAxis()==pHead.GetAxis()
&&GetNoSlices()==pHead.GetNoSlices()
&&GetMinExtent()==pHead.GetMinExtent()
&&GetMaxExtent()==pHead.GetMaxExtent())
{
G4int node,maxNode;
G4SmartVoxelProxy *leftProxy,*rightProxy;
G4SmartVoxelHeader *leftHeader,*rightHeader;
G4SmartVoxelNode *leftNode,*rightNode;
maxNode=GetNoSlices();
for (node=0;node<maxNode;node++)
{
leftProxy=GetSlice(node);
rightProxy=pHead.GetSlice(node);
if (leftProxy->IsHeader())
{
if (rightProxy->IsNode())
{
return false;
}
else
{
leftHeader=leftProxy->GetHeader();
rightHeader=rightProxy->GetHeader();
if (!(*leftHeader==*rightHeader))
{
return false;
}
}
}
else
{
if (rightProxy->IsHeader())
{
return false;
}
else
{
leftNode=leftProxy->GetNode();
rightNode=rightProxy->GetNode();
if (!(*leftNode==*rightNode))
{
return false;
}
}
}
}
return true;
}
else
{
return false;
}
}
// Build voxels for daughters specified volume, in NON-REPLICATED case
// o Create List of target volume nos (all daughters; 0->noDaughters-1)
// o BuildWithinLimits does Build & also determines mother dimensions
void G4SmartVoxelHeader::BuildVoxels(G4LogicalVolume* pVolume)
{
G4VoxelLimits limits; // Create `unlimited' limits object
G4int nDaughters=pVolume->GetNoDaughters();
G4VolumeNosVector targetList(nDaughters);
for (G4int i=0;i<nDaughters;i++)
{
targetList.insert(i);
}
BuildVoxelsWithinLimits(pVolume,limits,&targetList);
}
// Build voxels for specified volume containing a single
// replicated volume.
void G4SmartVoxelHeader::BuildReplicaVoxels(G4LogicalVolume* pVolume)
{
G4VPhysicalVolume *pDaughter;
// Replication data
EAxis axis;
G4int nReplicas;
G4double width,offset;
G4bool consuming;
// Consistency check: pVolume should contain single replicated volume
if (pVolume->GetNoDaughters()==1&&
pVolume->GetDaughter(0)->IsReplicated()==true)
{
// Obtain replication data
pDaughter=pVolume->GetDaughter(0);
pDaughter->GetReplicationData(axis,nReplicas,width,offset,consuming);
if (consuming==false)
{
G4VoxelLimits limits; // Create `unlimited' limits object
G4VolumeNosVector targetList(nReplicas);
for (G4int i=0;i<nReplicas;i++)
{
targetList.insert(i);
}
G4ProxyVector* pSlices=BuildNodes(pVolume,limits,&targetList,axis);
faxis=axis;
fslices=*pSlices;
delete pSlices;
// Calculate and set min and max extents given our axis
const G4AffineTransform origin;
pVolume->GetSolid()->CalculateExtent(faxis,limits,
origin,
fminExtent,fmaxExtent);
// Calculate equivalent nos
BuildEquivalentSliceNos();
CollectEquivalentNodes(); // Collect common nodes
}
else
{
// Replication is consuming -> Build voxels directly
//
// o Cartesian axes - range is -width*nREplicas/2 to +width*nREplicas/2
// nReplicas replications result
// o Radial axis (rho) = range is 0 to width*nReplicas
// nReplicas replications result
// o Phi axi - range is offset to offset+width*nReplicas radians
//
// Equivalent slices no computation & collection not required - all
// slices are different
switch (axis)
{
case kXAxis:
case kYAxis:
case kZAxis:
fminExtent=-width*nReplicas*0.5;
fmaxExtent=width*nReplicas*0.5;
break;
case kRho:
fminExtent=offset;
fmaxExtent=width*nReplicas+offset;
break;
case kPhi:
fminExtent=offset;
fmaxExtent=offset+width*nReplicas;
break;
default:
G4Exception("G4SmartVoxelHeader::BuildReplicaVoxels logical error - illegal axis");
break;
}
faxis=axis; // Set axis
BuildConsumedNodes(nReplicas);
if (axis==kXAxis||axis==kYAxis||axis==kZAxis)
{
// Sanity check on extent
G4double min,max;
G4VoxelLimits limits;
G4AffineTransform origin;
pVolume->GetSolid()->CalculateExtent(axis,limits,
origin,
min,max);
if (fabs((min-fminExtent)/fminExtent) +
fabs((max-fmaxExtent)/fmaxExtent)>0.05)
{
G4Exception(G4String("G4SmartVoxelHeader::BuildReplicaVoxels probable error in replicated geometry, logical volume ")+pVolume->GetName());
}
}
}
}
else
{
G4Exception("Illegal call of G4SmartVoxelHeader::BuildReplicaVoxels must have single replicated volume");
}
}
// Build `consumed nodes': nReplicas nodes each containing one
// replication, numbered in sequence 0->nReplicas-1
//
// o Modifies fslices `in place'
// o faxis,fminExtent,fmaxExtent NOT modified
void G4SmartVoxelHeader::BuildConsumedNodes(const G4int nReplicas)
{
G4int nNode,nVol;
G4SmartVoxelNode *pNode;
G4SmartVoxelProxy *pProxyNode;
// Create and fill nodes in temporary G4NodeVector (on stack)
G4NodeVector nodeList(nReplicas);
for (nNode=0;nNode<nReplicas;nNode++)
{
pNode=new G4SmartVoxelNode(nNode);
if (!pNode)
{
G4Exception("G4SmartVoxelHeader::BuildConsumedNodes Node allocation failed");
}
nodeList.insert(pNode);
}
for (nVol=0;nVol<nReplicas;nVol++)
{
nodeList(nVol)->Insert(nVol); // Insert replication of number
// identical to voxel number
}
// Create & fill proxy List `in place' by modifying instance data fslices
fslices.clear();
for (nNode=0;nNode<nReplicas;nNode++)
{
pProxyNode=new G4SmartVoxelProxy(nodeList(nNode));
if (!pProxyNode)
{
G4Exception("G4SmartVoxelHeader::BuildConsumedNodes Proxy Node allocation failed");
}
fslices.insert(pProxyNode);
}
}
// Build and refine voxels between specified limits, considering only
// the physical volumes numbered `pCandidates'.
// o Chooses axis
// o Determines min and max extents (of mother solid) within limits
void G4SmartVoxelHeader::BuildVoxelsWithinLimits(G4LogicalVolume* pVolume,
G4VoxelLimits pLimits,
const G4VolumeNosVector* pCandidates)
{
// Choose best axis for slicing by:
// 1. Trying all unlimited cartesian axes
// 2. Select axis which gives greatest no slices
G4ProxyVector *pGoodSlices=0,*pTestSlices,*tmpSlices;
G4double goodSliceScore=kInfinity,testSliceScore;
EAxis goodSliceAxis=kXAxis;
EAxis testAxis =kXAxis;
G4int node,maxNode,iaxis;
G4VoxelLimits noLimits;
// Try all non-limited cartesian axes
for (iaxis=0;iaxis<3;iaxis++)
{
switch(iaxis)
{
case 0:
testAxis=kXAxis;
break;
case 1:
testAxis=kYAxis;
break;
case 2:
testAxis=kZAxis;
break;
}
if (!pLimits.IsLimited(testAxis))
{
pTestSlices=BuildNodes(pVolume,pLimits,pCandidates,testAxis);
testSliceScore=CalculateQuality(pTestSlices);
if (!pGoodSlices||testSliceScore<goodSliceScore)
{
goodSliceAxis=testAxis;
goodSliceScore=testSliceScore;
tmpSlices=pGoodSlices;
pGoodSlices=pTestSlices;
pTestSlices=tmpSlices;
}
if (pTestSlices)
{
maxNode=pTestSlices->entries();
for (node=0;node<maxNode;node++)
{
delete pTestSlices->operator()(node)->GetNode();
}
pTestSlices->clearAndDestroy();
delete pTestSlices;
}
}
}
// Check for error case.. when limits already 3d, so cannot select
// a new axis
if (!pGoodSlices)
{
G4Exception("G4SmartVoxelHeader - Illegal limits: Already 3 dimensions of limits");
}
//
// We have selected pGoodSlices, with a score testSliceScore
//
// Store chosen axis, slice ptr
fslices=*pGoodSlices; // Set slice information, copy ptrs in
// collection
delete pGoodSlices; // Destroy slices vector, but not contained
// proxies or nodes
faxis=goodSliceAxis;
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << G4endl << " Selected axis = " << faxis << G4endl;
G4int islice;
for (islice=0;islice<fslices.entries();islice++)
{
G4int j;
G4cout << " Node #" << islice << " = {";
for (j=0;j<fslices(islice)->GetNode()->GetNoContained();j++)
{ G4cout << " " << fslices(islice)->GetNode()->GetVolume(j); }
G4cout << " }" << G4endl;
}
G4cout << G4endl;
#endif
// Calculate and set min and max extents given our axis
G4VSolid* outerSolid=pVolume->GetSolid();
const G4AffineTransform origin;
if(!outerSolid->CalculateExtent(faxis,pLimits,origin,fminExtent,fmaxExtent)){
outerSolid->CalculateExtent(faxis,noLimits,origin,fminExtent,fmaxExtent);
};
// Calculate equivalent nos
BuildEquivalentSliceNos();
CollectEquivalentNodes(); // Collect common nodes
RefineNodes(pVolume,pLimits); // Refine nodes creating headers
// No common headers can exist because collapsed by construction
}
// Destructor - Delete all proxies and underlying objects
G4SmartVoxelHeader::~G4SmartVoxelHeader()
{
// Manually destroy underlying nodes/headers
// Delete collected headers and nodes once only
G4int node,proxy,maxNode;
G4SmartVoxelProxy *lastProxy=0;
G4SmartVoxelNode *dyingNode,*lastNode=0;
G4SmartVoxelHeader *dyingHeader,*lastHeader=0;
maxNode=fslices.entries();
for (node=0;node<maxNode;node++)
{
if (fslices(node)->IsHeader())
{
dyingHeader=fslices(node)->GetHeader();
if (lastHeader!=dyingHeader)
{
lastHeader=dyingHeader;
lastNode=0;
delete dyingHeader;
}
}
else
{
dyingNode=fslices(node)->GetNode();
if (dyingNode!=lastNode)
{
lastNode=dyingNode;
lastHeader=0;
delete dyingNode;
}
}
}
// Delete proxies
for (proxy=0;proxy<maxNode;proxy++)
{
if (fslices(proxy)!=lastProxy)
{
lastProxy=fslices(proxy);
delete lastProxy;
}
}
// Don't need to clear slices
// fslices.clear();
}
// Calculate and Store the minimum and maximum equivalent neighbour
// values for all slices at our level.
//
// Precondition:
//
// All slices are nodes
//
// Process:
//
// For each potential start of a group of equivalent nodes
// Search forwards in fslices to find group end
// Loop from start to end setting start and end slices
void G4SmartVoxelHeader::BuildEquivalentSliceNos()
{
G4int sliceNo,minNo,maxNo,equivNo;
G4int maxNode=fslices.entries();
G4SmartVoxelNode *startNode,*sampleNode;
for (sliceNo=0;sliceNo<maxNode;sliceNo++)
{
minNo=sliceNo;
// Get first node (see preconditions - will throw exception if a header)
startNode=fslices(minNo)->GetNode();
// Find max equivalent
for (equivNo=minNo+1;equivNo<maxNode;equivNo++)
{
sampleNode=fslices(equivNo)->GetNode();
if (!(*startNode==*sampleNode))
{
break;
}
}
maxNo=equivNo-1;
if (maxNo!=minNo)
{
// Set min and max nos
for (equivNo=minNo;equivNo<=maxNo;equivNo++)
{
sampleNode=fslices(equivNo)->GetNode();
sampleNode->SetMinEquivalentSliceNo(minNo);
sampleNode->SetMaxEquivalentSliceNo(maxNo);
}
// Advance outer loop to end of equivalent group
sliceNo=maxNo;
}
}
}
// Collect common nodes at our level, deleting all but one to save
// memory, and adjusting stored slice ptrs appropriately.
//
// Preconditions:
//
// The slices have not previously be "collected"
//
// All of the slices are nodes
void G4SmartVoxelHeader::CollectEquivalentNodes()
{
G4int sliceNo,maxNo,equivNo;
G4int maxNode=fslices.entries();
G4SmartVoxelNode *equivNode;
G4SmartVoxelProxy *equivProxy;
for (sliceNo=0;sliceNo<maxNode;sliceNo++)
{
equivProxy=fslices(sliceNo);
// Asuumption: (see preconditions) all slices are nodes
equivNode=equivProxy->GetNode();
maxNo=equivNode->GetMaxEquivalentSliceNo();
if (maxNo!=sliceNo)
{
// Do collection between sliceNo and maxNo inclusive
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << "**** G4SmartVoxelHeader::CollectEquivalentNodes" << G4endl
<< " Collecting Nodes = "
<< sliceNo << " - " << maxNo << G4endl;
#endif
for (equivNo=sliceNo+1;equivNo<=maxNo;equivNo++)
{
delete fslices(equivNo)->GetNode();
delete fslices(equivNo);
fslices(equivNo)=equivProxy;
}
sliceNo=maxNo;
}
}
}
// Collect common headerss at our level, deleting all but one to save
// memory, and adjusting stored slice ptrs appropriately.
//
// Preconditions:
//
// If a header form part of a range of equivalent slices - ie.
// GetMaxEquivalentSliceNo()>GetMinEquivalentSliceNo(), it is assumed
// that all slices in the range are headers.
//
// This will be true if a constant Expression is used to evaluate
// when to refine nodes.
void G4SmartVoxelHeader::CollectEquivalentHeaders()
{
G4int sliceNo,maxNo,equivNo;
G4int maxNode=fslices.entries();
G4SmartVoxelHeader *equivHeader,*sampleHeader;
G4SmartVoxelProxy *equivProxy;
for (sliceNo=0;sliceNo<maxNode;sliceNo++)
{
equivProxy=fslices(sliceNo);
if (equivProxy->IsHeader())
{
equivHeader=equivProxy->GetHeader();
maxNo=equivHeader->GetMaxEquivalentSliceNo();
if (maxNo!=sliceNo)
{
// Attempt collection between sliceNo and maxNo inclusive: look for common
// headers. All slices between sliceNo and maxNo are guaranteed to be headers
// but may not have equal contents
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << "**** G4SmartVoxelHeader::CollectEquivalentHeaders" << G4endl
<< " Collecting Headers =";
#endif
for (equivNo=sliceNo+1;equivNo<=maxNo;equivNo++)
{
sampleHeader=fslices(equivNo)->GetHeader();
if (*sampleHeader==*equivHeader)
{
// Dlete sampleHeader + proxy and replace with equivHeader/Proxy
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << " " << equivNo;
#endif
delete sampleHeader;
delete fslices(equivNo);
fslices(equivNo)=equivProxy;
}
else
{
// Not equal. Set this header to be the current header for comparisons
equivProxy=fslices(equivNo);
equivHeader=equivProxy->GetHeader();
}
}
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << G4endl;
#endif
// Skip past examined slices
sliceNo=maxNo;
}
}
}
}
// Build the nodes corresponding to slices between the specified limits
// and along the specified axis, using candidate volume no.s in the vector
// pCandidates. If the `daughters' are replicated volumes (ie. the logical
// volume has a single replicated/parameterised volume for a daughter)
// the candidate no.s are interpreted as PARAMETERISED volume no.s &
// PARAMETERISATIONs are applied to compute transformations & solid
// dimensions appropriately. The volume must be parameterised - ie.
// has a parameterisation object & non-consuming) - in this case.
//
// Return ptr to built node "structure" (guaranteed non null)
// consisting of G4SmartVoxelNodeProxies refering to G4SmartVoxelNodes
G4ProxyVector* G4SmartVoxelHeader::BuildNodes(G4LogicalVolume* pVolume,
G4VoxelLimits pLimits,
const G4VolumeNosVector* pCandidates,
EAxis pAxis)
{
G4double motherMinExtent,motherMaxExtent,targetMinExtent,targetMaxExtent;
G4VPhysicalVolume *pDaughter=0;
G4VPVParameterisation *pParam=0;
G4VSolid *targetSolid;
G4AffineTransform targetTransform;
G4bool replicated;
G4int nCandidates;
G4int nVol,nNode,targetVolNo;
G4VoxelLimits noLimits;
nCandidates=pCandidates->entries();
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << "**** G4SmartVoxelHeader::BuildNodes" << G4endl
<< " Limits = " << pLimits << G4endl
<< " Axis = " << pAxis
<< " Candidates = " << nCandidates << G4endl;
#endif
// Compute extent of logical volume's solid along this axis
// NOTE: results stored locally and not preserved/reused
G4VSolid* outerSolid=pVolume->GetSolid();
const G4AffineTransform origin;
if(!outerSolid->CalculateExtent(pAxis,pLimits,origin,motherMinExtent,motherMaxExtent)){
outerSolid->CalculateExtent(pAxis,noLimits,origin,motherMinExtent,motherMaxExtent);
};
G4VolumeExtentVector minExtents(nCandidates);
G4VolumeExtentVector maxExtents(nCandidates);
if (pVolume->GetNoDaughters()==1 &&
pVolume->GetDaughter(0)->IsReplicated()==true)
{
// Replication data not required: only parameterisation object
// and volume no. List used
pDaughter=pVolume->GetDaughter(0);
pParam=pDaughter->GetParameterisation();
if (!pParam)
{
G4Exception("G4SmartVoxelHeader::BuildNodes - PANIC: Found replicated volume with no paramterisation object");
}
// Setup volume, preserving current mother link
pDaughter->Setup(pDaughter->GetMother());
// targetSolid=pDaughter->GetLogicalVolume()->GetSolid();
targetTransform=G4AffineTransform(pDaughter->GetRotation(),
pDaughter->GetTranslation());
replicated=true;
}
else
{
replicated=false;
}
// Compute extents
for (nVol=0;nVol<nCandidates;nVol++)
{
targetVolNo=pCandidates->operator()(nVol);
if (replicated==false)
{
pDaughter=pVolume->GetDaughter(targetVolNo);
// Setup volume, preserving current mother link
pDaughter->Setup(pDaughter->GetMother());
targetTransform=G4AffineTransform(pDaughter->GetRotation(),
pDaughter->GetTranslation());
// Get underlying (and setup) solid
targetSolid=pDaughter->GetLogicalVolume()->GetSolid();
}
else
{
// Find solid
targetSolid=pParam->ComputeSolid(targetVolNo,pDaughter);
// Setup solid
targetSolid->ComputeDimensions(pParam,targetVolNo,pDaughter);
// Setup transform
pParam->ComputeTransformation(targetVolNo,pDaughter);
targetTransform=G4AffineTransform(pDaughter->GetRotation(),
pDaughter->GetTranslation());
}
// Calc extents
if(!targetSolid->CalculateExtent(pAxis,pLimits,targetTransform,targetMinExtent,targetMaxExtent)){
targetSolid->CalculateExtent(pAxis,noLimits,targetTransform,targetMinExtent,targetMaxExtent);
};
minExtents(nVol)=targetMinExtent;
maxExtents(nVol)=targetMaxExtent;
// Check not entirely outside mother when processing toplevel nodes
if (!pLimits.IsLimited()&&((targetMaxExtent<=motherMinExtent)||(targetMinExtent>=motherMaxExtent)))
{
G4String error("G4SmartVoxelHeader::BuildNodes - PANIC! Daughter physical volume name = ");
error+=G4String(pDaughter->GetName());
error+=G4String(" is entirely outside mother logical volume name = ");
error+=G4String(pVolume->GetName());
G4Exception(error);
}
#ifdef G4GEOMETRY_VOXELDEBUG
// Check for straddling volumes when debugging
// If a volume is >kStraddlePercent percent over the mother boundary
// Print a warning
if (!pLimits.IsLimited())
{
G4double width;
const G4int kStraddlePercent=5;
width=maxExtents(nVol)-minExtents(nVol);
if (((motherMinExtent-minExtents(nVol))*100/width>kStraddlePercent)
||((maxExtents(nVol)-motherMaxExtent)*100/width>kStraddlePercent))
{
G4cout << "**** G4SmartVoxelHeader::BuildNodes" << G4endl
<< " WARNING : Daughter # " << nVol
<< " Name = " << pDaughter->GetName() << G4endl
<< " Crosses mother boundary of logical volume Name = "
<< pVolume->GetName() << G4endl
<< " by more than " << kStraddlePercent
<< "%" << G4endl;
}
}
#endif
}
// Extents of all daughters known
// Calculate minimum slice width, only including volumes inside the limits
G4double minWidth=kInfinity;
G4double currentWidth;
for (nVol=0;nVol<nCandidates;nVol++)
{
currentWidth=maxExtents(nVol)-minExtents(nVol);
if (currentWidth<minWidth&&
maxExtents(nVol)>=pLimits.GetMinExtent(pAxis)&&
minExtents(nVol)<=pLimits.GetMaxExtent(pAxis))
{
minWidth=currentWidth;
}
}
//
// No. of Nodes formula - nearest integer to
//
// mother width/half min daughter width +1
G4double noNodesExactD=((motherMaxExtent-motherMinExtent)*2.0/minWidth)+1.0;
// Compare with "smartless quality", i.e. the average number of slices used per
// contained volume:
G4double smartlessComputed = noNodesExactD / nCandidates;
G4double smartlessUser = pVolume->GetSmartless();
G4double smartless = (smartlessComputed <= smartlessUser) ?
smartlessComputed : smartlessUser ;
G4double noNodesSmart = smartless * nCandidates;
G4int noNodesExactI = G4int(noNodesSmart);
G4int noNodes = ((noNodesSmart-noNodesExactI)>=0.5) ?
noNodesExactI + 1 : noNodesExactI ;
//
//
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << " Min width = " << minWidth
<< " => # Nodes = " << noNodes << G4endl;
#endif
if (noNodes>kMaxVoxelNodes)
{
noNodes=kMaxVoxelNodes;
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << " Nodes Clipped to = " << kMaxVoxelNodes << G4endl;
#endif
}
G4double nodeWidth=(motherMaxExtent-motherMinExtent)/noNodes;
// Create G4VoxelNodes. Will Add proxies before setting fslices
G4NodeVector *nodeList;
nodeList= new G4NodeVector(noNodes);
if (!nodeList)
{
G4Exception("G4SmartVoxelHeader::BuildNodes NodeList allocation failed");
}
for (nNode=0;nNode<noNodes;nNode++)
{
G4SmartVoxelNode *pNode;
pNode=new G4SmartVoxelNode(nNode);
if (!pNode)
{
G4Exception("G4SmartVoxelHeader::BuildNodes Node allocation failed");
}
nodeList->insert(pNode);
}
// All nodes created (empty)
// Fill nodes: Step through extent lists
for (nVol=0;nVol<nCandidates;nVol++)
{
G4int nodeNo,minContainingNode,maxContainingNode;
minContainingNode=G4int ((minExtents(nVol)-motherMinExtent)/nodeWidth);
maxContainingNode=G4int ((maxExtents(nVol)-motherMinExtent)/nodeWidth);
// Only add nodes that are inside the limits of the axis
if (maxContainingNode>=0&&minContainingNode<noNodes)
{
// If max extent is on max boundary => maxContainingNode=noNodes: should be
// one less as nodeList has noNodes entries;
if (maxContainingNode>=noNodes)
{
maxContainingNode=noNodes-1;
}
// Protection against protruding volumes
if (minContainingNode<0)
{
minContainingNode=0;
}
for (nodeNo=minContainingNode;nodeNo<=maxContainingNode;nodeNo++)
{
nodeList->operator()(nodeNo)->Insert(pCandidates->operator()(nVol));
}
}
}
// All nodes filled
// Create proxy List : caller has deletion responsibility but we must delete
// nodeList *itself* - not the contents)
G4ProxyVector *proxyList;
proxyList=new G4ProxyVector(noNodes);
if (!proxyList)
{
G4Exception("G4SmartVoxelHeader::BuildNodes proxy List allocation failed");
}
// Fill proxy List
for (nNode=0;nNode<noNodes;nNode++)
{
G4SmartVoxelProxy *pProxyNode;
pProxyNode=new G4SmartVoxelProxy(nodeList->operator()(nNode));
if (!pProxyNode)
{
G4Exception("G4SmartVoxelHeader::BuildNodes Proxy Node allocation failed");
}
proxyList->insert(pProxyNode);
}
delete nodeList;
return proxyList;
}
// Calculate a "quality value" for the specified vector of voxels
// The value returned should be >0 and such that the smaller the
// number the higher the quality of the slice.
//
// pSlice must consist of G4SmartVoxelNodeProxies only
//
// Process:
//
// Examine each node in turn, summing:
// no. of non-empty nodes
// no. of volumes in each node
//
// Calculate Quality=sigma(volumes in nod)/(no. of non-empty nodes)
//
// if all nodes empty, return kInfinity
//
// Call G4Exception on finding a G4SmartVoxelHeaderProxy
G4double G4SmartVoxelHeader::CalculateQuality(G4ProxyVector *pSlice)
{
G4double quality;
G4int nNodes,sumContained=0,sumNonEmptyNodes=0;
G4int noContained,maxContained=0;
G4SmartVoxelNode *node;
nNodes=pSlice->entries();
for (G4int i=0;i<nNodes;i++)
{
if (pSlice->operator()(i)->IsNode())
{
// Definitely a node. Add info to running totals
node=pSlice->operator()(i)->GetNode();
noContained=node->GetNoContained();
if (noContained)
{
sumNonEmptyNodes++;
sumContained+=noContained;
// Calc maxContained for statistics
if (noContained>maxContained)
{
maxContained=noContained;
}
}
}
else
{
G4Exception("G4SmartVoxelHeader::CalculateQuality - Not defined for divided volumes");
}
}
// Calculate quality with protection against no non-empty nodes;
if (sumNonEmptyNodes)
{
quality=sumContained/sumNonEmptyNodes;
}
else
{
quality=kInfinity;
}
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << "**** G4SmartVoxelHeader::CalculateQuality" << G4endl
<< " Quality = " << quality << G4endl
<< " Nodes = " << nNodes
<< " of which " << sumNonEmptyNodes << " non empty" << G4endl
<< " Max Contained = " << maxContained << G4endl;
#endif
return quality;
}
// Examined each contained node, refine (create a replacement additional
// dimension of voxels) when there is more than one voxel in the slice.
//
// Do not refine further if already limited in two dimensions (=> this
// is the third level of limits)
//
// Preconditions:
//
// Slices (nodes) have been built
void G4SmartVoxelHeader::RefineNodes(G4LogicalVolume* pVolume,
G4VoxelLimits pLimits)
{
G4int refinedDepth=0,minVolumes;
G4int maxNode=fslices.entries();
if (pLimits.IsXLimited())
{
refinedDepth++;
}
if (pLimits.IsYLimited())
{
refinedDepth++;
}
if (pLimits.IsZLimited())
{
refinedDepth++;
}
// Calculate min no of volumes necessary to refine
switch (refinedDepth)
{
case 0:
minVolumes=kMinVoxelVolumesLevel2;
break;
case 1:
minVolumes=kMinVoxelVolumesLevel3;
break;
default:
minVolumes=10000; // catch refinedDepth=3 and errors
break;
}
if (refinedDepth<2)
{
G4int targetNo,noContainedDaughters,minNo,maxNo,replaceNo,i;
G4double sliceWidth=(fmaxExtent-fminExtent)/maxNode;
G4VoxelLimits newLimits;
G4SmartVoxelNode* targetNode;
G4SmartVoxelProxy* targetNodeProxy;
G4SmartVoxelHeader* replaceHeader;
G4SmartVoxelProxy* replaceHeaderProxy;
G4VolumeNosVector* targetList;
G4SmartVoxelProxy* lastProxy;
for (targetNo=0;targetNo<maxNode;targetNo++)
{
targetNodeProxy=fslices(targetNo);
// Assume all slices are nodes (see preconditions)
targetNode=targetNodeProxy->GetNode();
if (targetNode->GetNoContained()>=minVolumes)
{
noContainedDaughters=targetNode->GetNoContained();
targetList = new G4VolumeNosVector(noContainedDaughters);
if (!targetList)
{
G4Exception("G4SmartVoxelHeader::RefineNodes - Target volume no List new failed");
}
for (i=0;i<noContainedDaughters;i++)
{
targetList->insert(targetNode->GetVolume(i));
}
minNo=targetNode->GetMinEquivalentSliceNo();
maxNo=targetNode->GetMaxEquivalentSliceNo();
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << "**** G4SmartVoxelHeader::RefineNodes"
<< G4endl
<< " Refining nodes " << minNo
<< " - " << maxNo << " inclusive" << G4endl;
#endif
// Delete node proxies at start of collected sets of nodes/headers
lastProxy=0;
for (replaceNo=minNo;replaceNo<=maxNo;replaceNo++)
{
if (lastProxy!=fslices(replaceNo))
{
lastProxy=fslices(replaceNo);
delete lastProxy;
}
}
// Delete node to be replaced
delete targetNode;
// Create new headers + proxies and replace in fslices
newLimits=pLimits;
newLimits.AddLimit(faxis,fminExtent+sliceWidth*minNo,fminExtent+sliceWidth*(maxNo+1));
replaceHeader=new G4SmartVoxelHeader(pVolume,newLimits,targetList,replaceNo);
if (!replaceHeader)
{
G4Exception("G4SmartVoxelHeader::RefineNodes - Refined VoxelHeader new failed");
}
replaceHeader->SetMinEquivalentSliceNo(minNo);
replaceHeader->SetMaxEquivalentSliceNo(maxNo);
replaceHeaderProxy=new G4SmartVoxelProxy(replaceHeader);
if (!replaceHeader)
{
G4Exception("G4SmartVoxelHeader::RefineNodes - Refined VoxelProxy new failed");
}
for (replaceNo=minNo;replaceNo<=maxNo;replaceNo++)
{
fslices(replaceNo)=replaceHeaderProxy;
}
// Finished replacing current `equivalent' group
delete targetList;
targetNo=maxNo;
}
}
}
}
// Return true if all slices have equal contents
// Preconditions:
// All equal slices have been collected
// Procedure:
// Check all slice proxy pointers are equal
//
// Return true if only one slice or all slice proxies pointers equal
G4bool G4SmartVoxelHeader::AllSlicesEqual() const
{
G4int noSlices;
G4SmartVoxelProxy *refProxy;
noSlices=fslices.entries();
if (noSlices>1)
{
refProxy=fslices(0);
for (G4int i=1;i<noSlices;i++)
{
if (refProxy!=fslices(i))
{
return false;
}
}
}
return true;
}
// Output for debugging
G4std::ostream& operator << (G4std::ostream&s, const G4SmartVoxelHeader& h)
{
s << "Axis = " << h.faxis << G4endl;
G4SmartVoxelProxy *collectNode=0,*collectHead=0;
G4int collectNodeNo=0;
G4int collectHeadNo=0;
size_t i,j;
G4bool haveHeaders=false;
for (i=0;i<h.fslices.entries();i++)
{
s << "Slice #" << i << " = ";
if (h.fslices(i)->IsNode())
{
if (h.fslices(i)!=collectNode)
{
s << "{";
for (G4int j=0;j<h.fslices(i)->GetNode()->GetNoContained();j++)
{ s << " " << h.fslices(i)->GetNode()->GetVolume(j); }
s << " }" << G4endl;
collectNode=h.fslices(i);
collectNodeNo=i;
}
else
{
s << "As slice #" << collectNodeNo << G4endl;
}
}
else
{
haveHeaders=true;
if (h.fslices(i)!=collectHead)
{
s << "Header" << G4endl;
collectHead=h.fslices(i);
collectHeadNo=i;
}
else
{
s << "As slice #" << collectHeadNo << G4endl;
}
}
}
if (haveHeaders)
{
collectHead=0;
for (j=0;j<h.fslices.entries();j++)
{
if (h.fslices(j)->IsHeader())
{
s << "Header at Slice #" << j << " = ";
if (h.fslices(j)!=collectHead)
{
s << G4endl
<< (*(h.fslices(j)->GetHeader()));
collectHead=h.fslices(j);
collectHeadNo=j;
}
else
{
s << "As slice #" << collectHeadNo << G4endl;
}
}
}
}
return s;
}