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

1320 lines
41 KiB
C++

//
// ********************************************************************
// * DISCLAIMER *
// * *
// * The following disclaimer summarizes all the specific disclaimers *
// * of contributors to this software. The specific disclaimers,which *
// * govern, are listed with their locations in: *
// * http://cern.ch/geant4/license *
// * *
// * 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. *
// * *
// * 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.20 2002/05/17 17:59:47 gcosmo Exp $
// GEANT4 tag $Name: geant4-05-00 $
//
//
// class G4SmartVoxelHeader
//
// Implementation
//
// Define G4GEOMETRY_VOXELDEBUG for debugging information on G4cout
//
// History:
// 29.04.02 Use 3D voxelisation for non consuming replication - G.C.
// 18.04.01 Migrated to STL vector - G.C.
// 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.
// 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,
G4int pSlice)
: fminEquivalent(pSlice),
fmaxEquivalent(pSlice),
fparamAxis(kUndefined)
{
G4int nDaughters = pVolume->GetNoDaughters();
G4VoxelLimits limits; // Create `unlimited' limits object
// 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);
}
}
// ***************************************************************************
// Protected constructor:
// builds and refines voxels between specified limits, considering only
// the physical volumes numbered `pCandidates'. `pSlice' is used to set max
// and min equivalent slice nos for the header - they apply to the level
// of the header, not its nodes.
// ***************************************************************************
//
G4SmartVoxelHeader::G4SmartVoxelHeader(G4LogicalVolume* pVolume,
const G4VoxelLimits& pLimits,
const G4VolumeNosVector* pCandidates,
G4int pSlice)
: fminEquivalent(pSlice),
fmaxEquivalent(pSlice),
fparamAxis(kUndefined)
{
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << "**** G4SmartVoxelHeader::G4SmartVoxelHeader" << G4endl
<< " Limits " << pLimits << G4endl
<< " Candidate #s = " ;
for (size_t i=0;i<pCandidates->size();i++)
{
G4cout << (*pCandidates)[i] << " ";
}
G4cout << G4endl;
#endif
BuildVoxelsWithinLimits(pVolume,pLimits,pCandidates);
}
// ***************************************************************************
// Destructor:
// deletes all proxies and underlying objects.
// ***************************************************************************
//
G4SmartVoxelHeader::~G4SmartVoxelHeader()
{
// Manually destroy underlying nodes/headers
// Delete collected headers and nodes once only
//
G4int node, proxy, maxNode=fslices.size();
G4SmartVoxelProxy *lastProxy=0;
G4SmartVoxelNode *dyingNode, *lastNode=0;
G4SmartVoxelHeader *dyingHeader, *lastHeader=0;
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();
}
// ***************************************************************************
// Equality operator: returns true if contents are equivalent.
// Implies a deep search through contained nodes/header.
// Compares headers' axes,sizes,extents. Returns false if different.
// For each contained proxy, determines whether node/header, compares and
// returns if different. Compares and returns 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;
}
}
// ***************************************************************************
// Builds 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;
targetList.reserve(nDaughters);
for (G4int i=0; i<nDaughters; i++)
{
targetList.push_back(i);
}
BuildVoxelsWithinLimits(pVolume, limits, &targetList);
}
// ***************************************************************************
// Builds voxels for specified volume containing a single replicated volume.
// If axis is not specified (i.e. "kUndefined"), 3D voxelisation is applied,
// and the best axis is determined according to heuristics as for placements.
// ***************************************************************************
//
void G4SmartVoxelHeader::BuildReplicaVoxels(G4LogicalVolume* pVolume)
{
G4VPhysicalVolume *pDaughter=0;
// 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);
fparamAxis = axis;
if ( consuming==false )
{
G4VoxelLimits limits; // Create `unlimited' limits object
G4VolumeNosVector targetList;
targetList.reserve(nReplicas);
for (G4int i=0; i<nReplicas; i++)
{
targetList.push_back(i);
}
if (axis != kUndefined)
{
// Apply voxelisation along the specified axis only
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
{
// Build voxels similarly as for normal placements considering
// all three cartesian axes.
BuildVoxelsWithinLimits(pVolume, limits, &targetList);
}
}
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:
G4cout << "ERROR - Illegal axis !" << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildReplicaVoxels");
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)
{
G4cout << "ERROR - Replicated geometry, logical volume: "
<< pVolume->GetName() << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildReplicaVoxels");
}
}
}
}
else
{
G4cout << "ERROR - There must be a single replicated volume !" << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildReplicaVoxels");
}
}
// ***************************************************************************
// Builds `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(G4int nReplicas)
{
G4int nNode, nVol;
G4SmartVoxelNode *pNode;
G4SmartVoxelProxy *pProxyNode;
// Create and fill nodes in temporary G4NodeVector (on stack)
//
G4NodeVector nodeList;
nodeList.reserve(nReplicas);
for (nNode=0; nNode<nReplicas; nNode++)
{
pNode=new G4SmartVoxelNode(nNode);
if (!pNode)
{
G4cout << "ERROR - Node allocation failed." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildConsumedNodes");
}
nodeList.push_back(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)
{
G4cout << "ERROR - Proxy Node allocation failed." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildConsumedNodes");
}
fslices.push_back(pProxyNode);
}
}
// ***************************************************************************
// Builds and refines 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)
{
// Destroy pTestSlices and all its contents
//
maxNode=pTestSlices->size();
for (node=0; node<maxNode; node++)
{
delete (*pTestSlices)[node]->GetNode();
}
G4SmartVoxelProxy* tmpProx;
while (pTestSlices->size()>0)
{
tmpProx = pTestSlices->back();
pTestSlices->pop_back();
for (G4ProxyVector::iterator i=pTestSlices->begin();
i!=pTestSlices->end(); i++)
{
if (*i==tmpProx)
{
pTestSlices->erase(i); i--;
}
}
if ( tmpProx ) { delete tmpProx; }
}
delete pTestSlices;
}
}
}
// Check for error case.. when limits already 3d,
// so cannot select a new axis
//
if (!pGoodSlices)
{
G4cout << "ERROR - Illegal limits: already 3 dimensions of limits !" << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildVoxelsWithinLimits");
}
//
// 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;
for (size_t islice=0; islice<fslices.size(); islice++)
{
G4cout << " Node #" << islice << " = {";
for (G4int 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
}
// ***************************************************************************
// Calculates and stores the minimum and maximum equivalent neighbour
// values for all slices at our level.
//
// Precondition: all slices are nodes.
// For each potential start of a group of equivalent nodes:
// o searches forwards in fslices to find group end
// o loops from start to end setting start and end slices.
// ***************************************************************************
//
void G4SmartVoxelHeader::BuildEquivalentSliceNos()
{
G4int sliceNo, minNo, maxNo, equivNo;
G4int maxNode = fslices.size();
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;
}
}
}
// ***************************************************************************
// Collects common nodes at our level, deleting all but one to save
// memory, and adjusting stored slice pointers appropriately.
//
// Preconditions:
// o the slices have not previously be "collected"
// o all of the slices are nodes.
// ***************************************************************************
//
void G4SmartVoxelHeader::CollectEquivalentNodes()
{
G4int sliceNo, maxNo, equivNo;
G4int maxNode=fslices.size();
G4SmartVoxelNode *equivNode;
G4SmartVoxelProxy *equivProxy;
for (sliceNo=0; sliceNo<maxNode; sliceNo++)
{
equivProxy=fslices[sliceNo];
// Assumption (see preconditions): all slices are nodes
//
equivNode = equivProxy->GetNode();
maxNo = equivNode->GetMaxEquivalentSliceNo();
if (maxNo != sliceNo)
{
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << "**** G4SmartVoxelHeader::CollectEquivalentNodes" << G4endl
<< " Collecting Nodes = "
<< sliceNo << " - " << maxNo << G4endl;
#endif
// Do collection between sliceNo and maxNo inclusive
//
for (equivNo=sliceNo+1; equivNo<=maxNo; equivNo++)
{
delete fslices[equivNo]->GetNode();
delete fslices[equivNo];
fslices[equivNo] = equivProxy;
}
sliceNo = maxNo;
}
}
}
// ***************************************************************************
// Collects common headers at our level, deleting all but one to save
// memory, and adjusting stored slice pointers appropriately.
//
// Preconditions:
// o if a header forms part of a range of equivalent slices
// (ie. GetMaxEquivalentSliceNo()>GetMinEquivalentSliceNo()),
// it is assumed that all slices in the range are headers.
// o 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.size();
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) )
{
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << " " << equivNo;
#endif
// Delete sampleHeader + proxy and replace with equivHeader/Proxy
//
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;
}
}
}
}
// ***************************************************************************
// Builds 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.
//
// Returns pointer to built node "structure" (guaranteed non NULL) consisting
// of G4SmartVoxelNodeProxies referring 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 = pCandidates->size();
G4int nVol, nNode, targetVolNo;
G4VoxelLimits noLimits;
#ifdef G4GEOMETRY_VOXELDEBUG
G4cout << "**** G4SmartVoxelHeader::BuildNodes" << G4endl
<< " Limits = " << pLimits << G4endl
<< " Axis = " << pAxis << G4endl
<< " 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,0.);
G4VolumeExtentVector maxExtents(nCandidates,0.);
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)
{
G4cout << "PANIC! Replicated volume with no parameterisation object !"
<< G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildNodes");
}
// Setup volume, preserving current mother link
//
pDaughter->Setup(pDaughter->GetMother());
targetTransform = G4AffineTransform(pDaughter->GetRotation(),
pDaughter->GetTranslation());
replicated = true;
}
else
{
replicated = false;
}
// Compute extents
//
for (nVol=0; nVol<nCandidates; nVol++)
{
targetVolNo=(*pCandidates)[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());
}
// Calculate 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)) )
{
G4cout << "PANIC! Daughter physical volume "
<< pDaughter->GetName() << G4endl
<< "is entirely outside mother logical volume "
<< pVolume->GetName() << " !!" << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildNodes");
}
#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;
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;
if( noNodes == 0 ) { noNodes=1; }
#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 = new G4NodeVector();
nodeList->reserve(noNodes);
if (!nodeList)
{
G4cout << "ERROR - NodeList allocation failed." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildNodes");
}
for (nNode=0; nNode<noNodes; nNode++)
{
G4SmartVoxelNode *pNode;
pNode = new G4SmartVoxelNode(nNode);
if (!pNode)
{
G4cout << "ERROR - Node allocation failed." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildNodes");
}
nodeList->push_back(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)[nodeNo]->Insert((*pCandidates)[nVol]);
}
}
}
// All nodes filled
// Create proxy List : caller has deletion responsibility
// (but we must delete nodeList *itself* - not the contents)
//
G4ProxyVector* proxyList = new G4ProxyVector();
proxyList->reserve(noNodes);
if (!proxyList)
{
G4cout << "ERROR - Proxy List allocation failed." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildNodes");
}
//
// Fill proxy List
//
for (nNode=0; nNode<noNodes; nNode++)
{
G4SmartVoxelProxy* pProxyNode = new G4SmartVoxelProxy((*nodeList)[nNode]);
if (!pProxyNode)
{
G4cout << "ERROR - Proxy Node allocation failed." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::BuildNodes");
}
proxyList->push_back(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.
//
// Preconditions: pSlice must consist of G4SmartVoxelNodeProxies only
// Process:
// o Examine each node in turn, summing:
// no. of non-empty nodes
// no. of volumes in each node
// o Calculate Quality=sigma(volumes in nod)/(no. of non-empty nodes)
// if all nodes empty, return kInfinity
// o Call G4Exception on finding a G4SmartVoxelHeaderProxy
// ***************************************************************************
//
G4double G4SmartVoxelHeader::CalculateQuality(G4ProxyVector *pSlice)
{
G4double quality;
G4int nNodes = pSlice->size();
G4int noContained, maxContained=0, sumContained=0, sumNonEmptyNodes=0;
G4SmartVoxelNode *node;
for (G4int i=0; i<nNodes; i++)
{
if ((*pSlice)[i]->IsNode())
{
// Definitely a node. Add info to running totals
//
node = (*pSlice)[i]->GetNode();
noContained = node->GetNoContained();
if (noContained)
{
sumNonEmptyNodes++;
sumContained += noContained;
//
// Calc maxContained for statistics
//
if (noContained>maxContained)
{
maxContained = noContained;
}
}
}
else
{
G4cout << "ERROR - Not defined for sliced volumes." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::CalculateQuality");
}
}
// 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, refines (creates a replacement additional
// dimension of voxels) when there is more than one voxel in the slice.
// Does 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.size();
if (pLimits.IsXLimited())
{
refinedDepth++;
}
if (pLimits.IsYLimited())
{
refinedDepth++;
}
if (pLimits.IsZLimited())
{
refinedDepth++;
}
// Calculate minimum number 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++)
{
// Assume all slices are nodes (see preconditions)
//
targetNodeProxy = fslices[targetNo];
targetNode = targetNodeProxy->GetNode();
if (targetNode->GetNoContained() >= minVolumes)
{
noContainedDaughters = targetNode->GetNoContained();
targetList = new G4VolumeNosVector();
targetList->reserve(noContainedDaughters);
if (!targetList)
{
G4cout << "ERROR - Target volume no List new failed." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::RefineNodes");
}
for (i=0; i<noContainedDaughters; i++)
{
targetList->push_back(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)
{
G4cout << "ERROR - Refined VoxelHeader new failed." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::RefineNodes");
}
replaceHeader->SetMinEquivalentSliceNo(minNo);
replaceHeader->SetMaxEquivalentSliceNo(maxNo);
replaceHeaderProxy = new G4SmartVoxelProxy(replaceHeader);
if (!replaceHeader)
{
G4cout << "ERROR - Refined VoxelProxy new failed." << G4endl;
G4Exception("ERROR - G4SmartVoxelHeader::RefineNodes");
}
for (replaceNo=minNo; replaceNo<=maxNo; replaceNo++)
{
fslices[replaceNo] = replaceHeaderProxy;
}
// Finished replacing current `equivalent' group
//
delete targetList;
targetNo=maxNo;
}
}
}
}
// ***************************************************************************
// Returns true if all slices have equal contents.
// Preconditions: all equal slices have been collected.
// Procedure:
// o checks all slice proxy pointers are equal
// o returns true if only one slice or all slice proxies pointers equal.
// ***************************************************************************
//
G4bool G4SmartVoxelHeader::AllSlicesEqual() const
{
G4int noSlices = fslices.size();
G4SmartVoxelProxy* refProxy;
if (noSlices>1)
{
refProxy=fslices[0];
for (G4int i=1; i<noSlices; i++)
{
if (refProxy!=fslices[i])
{
return false;
}
}
}
return true;
}
// ***************************************************************************
// Streaming operator for debugging.
// ***************************************************************************
//
G4std::ostream& operator << (G4std::ostream& s, const G4SmartVoxelHeader& h)
{
s << "Axis = " << G4int(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.size(); 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.size(); 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;
}