source: trunk/source/geometry/management/src/G4SmartVoxelHeader.cc @ 1228

Last change on this file since 1228 was 1228, checked in by garnier, 14 years ago

update geant4.9.3 tag

File size: 44.2 KB
Line 
1//
2// ********************************************************************
3// * License and Disclaimer                                           *
4// *                                                                  *
5// * The  Geant4 software  is  copyright of the Copyright Holders  of *
6// * the Geant4 Collaboration.  It is provided  under  the terms  and *
7// * conditions of the Geant4 Software License,  included in the file *
8// * LICENSE and available at  http://cern.ch/geant4/license .  These *
9// * include a list of copyright holders.                             *
10// *                                                                  *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work  make  any representation or  warranty, express or implied, *
14// * regarding  this  software system or assume any liability for its *
15// * use.  Please see the license in the file  LICENSE  and URL above *
16// * for the full disclaimer and the limitation of liability.         *
17// *                                                                  *
18// * This  code  implementation is the result of  the  scientific and *
19// * technical work of the GEANT4 collaboration.                      *
20// * By using,  copying,  modifying or  distributing the software (or *
21// * any work based  on the software)  you  agree  to acknowledge its *
22// * use  in  resulting  scientific  publications,  and indicate your *
23// * acceptance of all terms of the Geant4 Software license.          *
24// ********************************************************************
25//
26//
27// $Id: G4SmartVoxelHeader.cc,v 1.34 2009/10/30 14:05:47 gcosmo Exp $
28// GEANT4 tag $Name: geant4-09-03 $
29//
30//
31// class G4SmartVoxelHeader
32//
33// Implementation
34//
35// Define G4GEOMETRY_VOXELDEBUG for debugging information on G4cout
36//
37// History:
38// 29.04.02 Use 3D voxelisation for non consuming replication - G.C.
39// 18.04.01 Migrated to STL vector - G.C.
40// 12.02.99 Introduction of new quality/smartless: max for (slices/candid) S.G.
41// 11.02.99 Voxels at lower levels are now built for collapsed slices      S.G.
42// 21.07.95 Full implementation, supporting non divided physical volumes
43// 14.07.95 Initial version - stubb definitions only
44// --------------------------------------------------------------------
45
46#include "G4SmartVoxelHeader.hh"
47
48#include "G4ios.hh"
49
50#include "G4LogicalVolume.hh"
51#include "G4VPhysicalVolume.hh"
52#include "G4VoxelLimits.hh"
53
54#include "voxeldefs.hh"
55#include "G4AffineTransform.hh"
56#include "G4VSolid.hh"
57#include "G4VPVParameterisation.hh"
58
59// ***************************************************************************
60// Constructor for topmost header, to begin voxel construction at a
61// given logical volume.
62// Constructs target List of volumes, calls "Build and refine" constructor.
63// Assumes all daughters represent single volumes (ie. no divisions
64// or parametric)
65// ***************************************************************************
66//
67G4SmartVoxelHeader::G4SmartVoxelHeader(G4LogicalVolume* pVolume,
68                                       G4int pSlice)
69  : fminEquivalent(pSlice),
70    fmaxEquivalent(pSlice),
71    fparamAxis(kUndefined)
72{
73  G4int nDaughters = pVolume->GetNoDaughters();
74  G4VoxelLimits limits;   // Create `unlimited' limits object
75
76  // Determine whether daughter is replicated
77  //
78  if ((nDaughters!=1) || (!pVolume->GetDaughter(0)->IsReplicated()))
79  {
80    // Daughter not replicated => conventional voxel Build
81    // where each daughters extents are computed
82    //
83    BuildVoxels(pVolume);
84  }
85  else
86  {
87    // Single replicated daughter
88    //
89    BuildReplicaVoxels(pVolume);
90  }
91}
92
93// ***************************************************************************
94// Protected constructor:
95// builds and refines voxels between specified limits, considering only
96// the physical volumes numbered `pCandidates'. `pSlice' is used to set max
97// and min equivalent slice nos for the header - they apply to the level
98// of the header, not its nodes.
99// ***************************************************************************
100//
101G4SmartVoxelHeader::G4SmartVoxelHeader(G4LogicalVolume* pVolume,
102                                 const G4VoxelLimits& pLimits,
103                                 const G4VolumeNosVector* pCandidates,
104                                       G4int pSlice)
105  : fminEquivalent(pSlice),
106    fmaxEquivalent(pSlice),
107    fparamAxis(kUndefined)
108{
109#ifdef G4GEOMETRY_VOXELDEBUG
110  G4cout << "**** G4SmartVoxelHeader::G4SmartVoxelHeader" << G4endl
111         << "     Limits " << pLimits << G4endl
112         << "     Candidate #s = " ;
113  for (size_t i=0;i<pCandidates->size();i++)
114  {
115    G4cout << (*pCandidates)[i] << " ";
116  }
117  G4cout << G4endl;
118#endif   
119
120  BuildVoxelsWithinLimits(pVolume,pLimits,pCandidates);
121}
122
123// ***************************************************************************
124// Destructor:
125// deletes all proxies and underlying objects.
126// ***************************************************************************
127//
128G4SmartVoxelHeader::~G4SmartVoxelHeader()
129{
130  // Manually destroy underlying nodes/headers
131  // Delete collected headers and nodes once only
132  //
133  G4int node, proxy, maxNode=fslices.size();
134  G4SmartVoxelProxy *lastProxy=0;
135  G4SmartVoxelNode *dyingNode, *lastNode=0;
136  G4SmartVoxelHeader *dyingHeader, *lastHeader=0;
137
138  for (node=0; node<maxNode; node++)
139  {
140    if (fslices[node]->IsHeader())
141    {
142      dyingHeader = fslices[node]->GetHeader();
143      if (lastHeader!=dyingHeader)
144      {
145        lastHeader = dyingHeader;
146        lastNode = 0;
147        delete dyingHeader;
148      }
149    }
150      else
151    {
152      dyingNode = fslices[node]->GetNode();
153      if (dyingNode!=lastNode)
154      {
155        lastNode=dyingNode;
156        lastHeader=0;
157        delete dyingNode;
158      }
159    }
160  }
161  // Delete proxies
162  //
163  for (proxy=0; proxy<maxNode; proxy++)
164  {
165    if (fslices[proxy]!=lastProxy)
166    {
167      lastProxy = fslices[proxy];
168      delete lastProxy;
169    }
170  }
171  // Don't need to clear slices
172  // fslices.clear();
173}
174
175// ***************************************************************************
176// Equality operator: returns true if contents are equivalent.
177// Implies a deep search through contained nodes/header.
178// Compares headers' axes,sizes,extents. Returns false if different.
179// For each contained proxy, determines whether node/header, compares and
180// returns if different. Compares and returns if proxied nodes/headers
181// are different.
182// ***************************************************************************
183//
184G4bool G4SmartVoxelHeader::operator == (const G4SmartVoxelHeader& pHead) const
185{
186  if ( (GetAxis()      == pHead.GetAxis())
187    && (GetNoSlices()  == pHead.GetNoSlices())
188    && (GetMinExtent() == pHead.GetMinExtent())
189    && (GetMaxExtent() == pHead.GetMaxExtent()) )
190  {
191    G4int node, maxNode;
192    G4SmartVoxelProxy *leftProxy, *rightProxy;
193    G4SmartVoxelHeader *leftHeader, *rightHeader;
194    G4SmartVoxelNode *leftNode, *rightNode;
195
196    maxNode=GetNoSlices();
197    for (node=0; node<maxNode; node++)
198    {
199      leftProxy  = GetSlice(node);
200      rightProxy = pHead.GetSlice(node);
201      if (leftProxy->IsHeader())
202      {
203        if (rightProxy->IsNode())
204        {
205          return false;
206        }
207        else
208        {
209          leftHeader  = leftProxy->GetHeader();
210          rightHeader = rightProxy->GetHeader();
211          if (!(*leftHeader==*rightHeader))
212          {
213            return false;
214          }
215        }
216      }
217      else
218      {
219        if (rightProxy->IsHeader())
220        {
221          return false;
222        }
223        else
224        {
225          leftNode  = leftProxy->GetNode();
226          rightNode = rightProxy->GetNode();
227          if (!(*leftNode==*rightNode))
228          {
229            return false;
230          }
231        }
232      }
233    }
234    return true;
235  }
236  else
237  {
238    return false;
239  }
240}
241
242// ***************************************************************************
243// Builds voxels for daughters specified volume, in NON-REPLICATED case
244// o Create List of target volume nos (all daughters; 0->noDaughters-1)
245// o BuildWithinLimits does Build & also determines mother dimensions.
246// ***************************************************************************
247//
248void G4SmartVoxelHeader::BuildVoxels(G4LogicalVolume* pVolume)
249{
250  G4VoxelLimits limits;   // Create `unlimited' limits object
251  G4int nDaughters = pVolume->GetNoDaughters();
252
253  G4VolumeNosVector targetList;
254  targetList.reserve(nDaughters);
255  for (G4int i=0; i<nDaughters; i++)
256  {
257    targetList.push_back(i);
258  }
259  BuildVoxelsWithinLimits(pVolume, limits, &targetList);
260}
261
262// ***************************************************************************
263// Builds voxels for specified volume containing a single replicated volume.
264// If axis is not specified (i.e. "kUndefined"), 3D voxelisation is applied,
265// and the best axis is determined according to heuristics as for placements.
266// ***************************************************************************
267//
268void G4SmartVoxelHeader::BuildReplicaVoxels(G4LogicalVolume* pVolume)
269{
270  G4VPhysicalVolume *pDaughter=0;
271
272  // Replication data
273  //
274  EAxis axis;
275  G4int nReplicas;
276  G4double width,offset;
277  G4bool consuming;
278
279  // Consistency check: pVolume should contain single replicated volume
280  //
281  if ( (pVolume->GetNoDaughters()==1)
282    && (pVolume->GetDaughter(0)->IsReplicated()==true) )
283  {
284    // Obtain replication data
285    //
286    pDaughter=pVolume->GetDaughter(0);
287    pDaughter->GetReplicationData(axis,nReplicas,width,offset,consuming);
288    fparamAxis = axis;
289    if ( consuming==false )
290    {
291      G4VoxelLimits limits;   // Create `unlimited' limits object
292      G4VolumeNosVector targetList;
293      targetList.reserve(nReplicas);
294      for (G4int i=0; i<nReplicas; i++)
295      {
296        targetList.push_back(i);
297      }
298      if (axis != kUndefined)
299      {
300        // Apply voxelisation along the specified axis only
301
302        G4ProxyVector* pSlices=BuildNodes(pVolume,limits,&targetList,axis);
303        faxis = axis;
304        fslices = *pSlices;
305        delete pSlices;
306
307        // Calculate and set min and max extents given our axis
308        //
309        const G4AffineTransform origin;
310        pVolume->GetSolid()->CalculateExtent(faxis, limits, origin,
311                                             fminExtent, fmaxExtent);
312        // Calculate equivalent nos
313        //
314        BuildEquivalentSliceNos();
315        CollectEquivalentNodes();   // Collect common nodes
316      }
317      else
318      {
319        // Build voxels similarly as for normal placements considering
320        // all three cartesian axes.
321
322        BuildVoxelsWithinLimits(pVolume, limits, &targetList);
323      }
324    }
325    else
326    {
327      // Replication is consuming -> Build voxels directly
328      //
329      // o Cartesian axes - range is -width*nREplicas/2 to +width*nREplicas/2
330      //                    nReplicas replications result
331      // o Radial axis (rho) = range is 0 to width*nReplicas
332      //                    nReplicas replications result
333      // o Phi axi       - range is offset to offset+width*nReplicas radians
334      //
335      // Equivalent slices no computation & collection not required - all
336      // slices are different
337      //
338      switch (axis)
339      {
340        case kXAxis:
341        case kYAxis:
342        case kZAxis:
343          fminExtent = -width*nReplicas*0.5;
344          fmaxExtent =  width*nReplicas*0.5;
345          break;
346        case kRho:
347          fminExtent = offset;
348          fmaxExtent = width*nReplicas+offset;
349          break;
350        case kPhi:
351          fminExtent = offset;
352          fmaxExtent = offset+width*nReplicas;
353          break;
354        default:
355          G4cerr << "ERROR - G4SmartVoxelHeader::BuildReplicaVoxels()"
356                 << G4endl
357                 << "        Illegal axis !" << G4endl;
358          G4Exception("G4SmartVoxelHeader::BuildReplicaVoxels()", "FatalError",
359                      FatalException, "Illegal axis.");
360          break;
361      } 
362      faxis = axis;   // Set axis
363      BuildConsumedNodes(nReplicas);
364      if ( (axis==kXAxis) || (axis==kYAxis) || (axis==kZAxis) )
365      {
366        // Sanity check on extent
367        //
368        G4double emin = kInfinity, emax = -kInfinity;
369        G4VoxelLimits limits;
370        G4AffineTransform origin;
371        pVolume->GetSolid()->CalculateExtent(axis, limits, origin, emin, emax);
372        if ( (std::fabs((emin-fminExtent)/fminExtent) +
373              std::fabs((emax-fmaxExtent)/fmaxExtent)) > 0.05)
374        {
375          G4cerr << "ERROR - G4SmartVoxelHeader::BuildReplicaVoxels()"
376                 << G4endl
377                 << "        Replicated geometry, logical volume: "
378                 << pVolume->GetName() << G4endl;
379          G4Exception("G4SmartVoxelHeader::BuildReplicaVoxels", "FatalError",
380                      FatalException, "Sanity check: wrong solid extent.");
381        }
382      }
383    }
384  }
385  else
386  {
387    G4cerr << "ERROR - G4SmartVoxelHeader::BuildReplicaVoxels()"
388           << G4endl
389           << "        There must be a single replicated volume !" << G4endl;
390    G4Exception("G4SmartVoxelHeader::BuildReplicaVoxels", "InvalidSetup",
391                FatalException, "Only one replicated daughter is allowed !");
392  }
393}
394
395// ***************************************************************************
396// Builds `consumed nodes': nReplicas nodes each containing one replication,
397// numbered in sequence 0->nReplicas-1
398// o Modifies fslices `in place'
399// o faxis,fminExtent,fmaxExtent NOT modified.
400// ***************************************************************************
401//
402void G4SmartVoxelHeader::BuildConsumedNodes(G4int nReplicas)
403{
404  G4int nNode, nVol;
405  G4SmartVoxelNode *pNode;
406  G4SmartVoxelProxy *pProxyNode;
407
408  // Create and fill nodes in temporary G4NodeVector (on stack)
409  //
410  G4NodeVector nodeList;
411  nodeList.reserve(nReplicas);
412  for (nNode=0; nNode<nReplicas; nNode++)
413  {
414    pNode=new G4SmartVoxelNode(nNode);
415    if (!pNode)
416    {
417      G4cerr << "ERROR - G4SmartVoxelHeader::BuildConsumedNodes()" << G4endl
418             << "        Node allocation failed." << G4endl;
419      G4Exception("G4SmartVoxelHeader::BuildConsumedNodes()", "FatalError",
420                  FatalException, "Node allocation error.");
421    }
422    nodeList.push_back(pNode);
423  }
424  for (nVol=0; nVol<nReplicas; nVol++)
425  {
426    nodeList[nVol]->Insert(nVol);   // Insert replication of number
427  }                                 // identical to voxel number
428
429  // Create & fill proxy List `in place' by modifying instance data fslices
430  //
431  fslices.clear();
432  for (nNode=0; nNode<nReplicas; nNode++)
433  {
434    pProxyNode = new G4SmartVoxelProxy(nodeList[nNode]);
435    if (!pProxyNode)
436    {
437      G4cerr << "ERROR - G4SmartVoxelHeader::BuildConsumedNodes()" << G4endl
438             << "        Proxy node allocation failed." << G4endl;
439      G4Exception("G4SmartVoxelHeader::BuildConsumedNodes()", "FatalError",
440                  FatalException, "Proxy node allocation error.");
441    }
442    fslices.push_back(pProxyNode);
443  }
444}
445
446// ***************************************************************************
447// Builds and refines voxels between specified limits, considering only
448// the physical volumes numbered `pCandidates'.
449// o Chooses axis
450// o Determines min and max extents (of mother solid) within limits.
451// ***************************************************************************
452//
453void
454G4SmartVoxelHeader::BuildVoxelsWithinLimits(G4LogicalVolume* pVolume,
455                                            G4VoxelLimits pLimits,
456                                      const G4VolumeNosVector* pCandidates)
457{
458  // Choose best axis for slicing by:
459  // 1. Trying all unlimited cartesian axes
460  // 2. Select axis which gives greatest no slices
461
462  G4ProxyVector *pGoodSlices=0, *pTestSlices, *tmpSlices;
463  G4double goodSliceScore=kInfinity, testSliceScore;
464  EAxis goodSliceAxis = kXAxis;
465  EAxis testAxis      = kXAxis;
466  G4int node, maxNode, iaxis;
467  G4VoxelLimits noLimits;
468
469  // Try all non-limited cartesian axes
470  //
471  for (iaxis=0; iaxis<3; iaxis++)
472  {
473    switch(iaxis)
474    {
475      case 0:
476        testAxis = kXAxis;
477        break;
478      case 1:
479        testAxis = kYAxis;
480        break;
481      case 2:
482        testAxis = kZAxis;
483        break;
484    }
485    if (!pLimits.IsLimited(testAxis))
486    {
487      pTestSlices = BuildNodes(pVolume,pLimits,pCandidates,testAxis);
488      testSliceScore = CalculateQuality(pTestSlices);
489      if ( (!pGoodSlices) || (testSliceScore<goodSliceScore) )
490      {
491        goodSliceAxis  = testAxis;
492        goodSliceScore = testSliceScore;
493        tmpSlices      = pGoodSlices;
494        pGoodSlices    = pTestSlices;
495        pTestSlices    = tmpSlices;
496      }
497      if (pTestSlices)
498      {
499        // Destroy pTestSlices and all its contents
500        //
501        maxNode=pTestSlices->size();
502        for (node=0; node<maxNode; node++)
503        {
504          delete (*pTestSlices)[node]->GetNode();
505        }
506        G4SmartVoxelProxy* tmpProx;
507        while (pTestSlices->size()>0)
508        {
509          tmpProx = pTestSlices->back();
510          pTestSlices->pop_back();
511          for (G4ProxyVector::iterator i=pTestSlices->begin();
512                                       i!=pTestSlices->end(); i++)
513          {
514            if (*i==tmpProx)
515            {
516              pTestSlices->erase(i); i--;
517            }
518          }
519          if ( tmpProx ) { delete tmpProx; }
520        } 
521        delete pTestSlices;
522      }
523    }
524  }
525  // Check for error case.. when limits already 3d,
526  // so cannot select a new axis
527  //
528  if (!pGoodSlices)
529  {
530    G4cerr << "ERROR - G4SmartVoxelHeader::BuildVoxelsWithinLimits()"
531           << G4endl
532           << "        Illegal limits: only 3 dimensions allowed." << G4endl;
533    G4Exception("G4SmartVoxelHeader::BuildVoxelsWithinLimits()",
534                "InvalidSetup", FatalException,
535                "Cannot select more than 3 axis for optimisation.");
536  }
537
538  //
539  // We have selected pGoodSlices, with a score testSliceScore
540  //
541
542  // Store chosen axis, slice ptr
543  //
544  fslices=*pGoodSlices; // Set slice information, copy ptrs in collection
545  delete pGoodSlices;   // Destroy slices vector, but not contained
546                        // proxies or nodes
547  faxis=goodSliceAxis;
548
549#ifdef G4GEOMETRY_VOXELDEBUG
550  G4cout << G4endl << "     Selected axis = " << faxis << G4endl;
551  for (size_t islice=0; islice<fslices.size(); islice++)
552  {
553    G4cout << "     Node #" << islice << " = {";
554    for (G4int j=0; j<fslices[islice]->GetNode()->GetNoContained(); j++)
555    {
556      G4cout << " " << fslices[islice]->GetNode()->GetVolume(j);
557    }
558    G4cout << " }" << G4endl;
559  }
560  G4cout << G4endl;
561#endif
562
563  // Calculate and set min and max extents given our axis
564  //
565  G4VSolid* outerSolid = pVolume->GetSolid();
566  const G4AffineTransform origin;
567  if(!outerSolid->CalculateExtent(faxis,pLimits,origin,fminExtent,fmaxExtent))
568  {
569    outerSolid->CalculateExtent(faxis,noLimits,origin,fminExtent,fmaxExtent);
570  }
571
572  // Calculate equivalent nos
573  //
574  BuildEquivalentSliceNos();
575  CollectEquivalentNodes();     // Collect common nodes
576  RefineNodes(pVolume,pLimits); // Refine nodes creating headers
577
578  // No common headers can exist because collapsed by construction
579}
580
581// ***************************************************************************
582// Calculates and stores the minimum and maximum equivalent neighbour
583// values for all slices at our level.
584//
585// Precondition: all slices are nodes.
586// For each potential start of a group of equivalent nodes:
587// o searches forwards in fslices to find group end
588// o loops from start to end setting start and end slices.
589// ***************************************************************************
590//
591void G4SmartVoxelHeader::BuildEquivalentSliceNos()
592{
593  G4int sliceNo, minNo, maxNo, equivNo;
594  G4int maxNode = fslices.size();
595  G4SmartVoxelNode *startNode, *sampleNode;
596  for (sliceNo=0; sliceNo<maxNode; sliceNo++)
597  {
598    minNo = sliceNo;
599
600    // Get first node (see preconditions - will throw exception if a header)
601    //
602    startNode = fslices[minNo]->GetNode();
603
604    // Find max equivalent
605    //
606    for (equivNo=minNo+1; equivNo<maxNode; equivNo++)
607    {
608      sampleNode = fslices[equivNo]->GetNode();
609      if (!((*startNode) == (*sampleNode))) { break; }
610    }
611    maxNo = equivNo-1;
612    if (maxNo != minNo)
613    {
614      // Set min and max nos
615      //
616      for (equivNo=minNo; equivNo<=maxNo; equivNo++)
617      {
618        sampleNode = fslices[equivNo]->GetNode();
619        sampleNode->SetMinEquivalentSliceNo(minNo);
620        sampleNode->SetMaxEquivalentSliceNo(maxNo);
621      }
622      // Advance outer loop to end of equivalent group
623      //
624      sliceNo = maxNo;
625    }
626  }
627}
628
629// ***************************************************************************
630// Collects common nodes at our level, deleting all but one to save
631// memory, and adjusting stored slice pointers appropriately.
632//
633// Preconditions:
634// o the slices have not previously be "collected"
635// o all of the slices are nodes.
636// ***************************************************************************
637//
638void G4SmartVoxelHeader::CollectEquivalentNodes()
639{
640  G4int sliceNo, maxNo, equivNo;
641  G4int maxNode=fslices.size();
642  G4SmartVoxelNode *equivNode;
643  G4SmartVoxelProxy *equivProxy;
644
645  for (sliceNo=0; sliceNo<maxNode; sliceNo++)
646  {
647    equivProxy=fslices[sliceNo];
648
649    // Assumption (see preconditions): all slices are nodes
650    //
651    equivNode = equivProxy->GetNode();
652    maxNo = equivNode->GetMaxEquivalentSliceNo();
653    if (maxNo != sliceNo)
654    {
655#ifdef G4GEOMETRY_VOXELDEBUG
656      G4cout << "**** G4SmartVoxelHeader::CollectEquivalentNodes" << G4endl
657             << "     Collecting Nodes = " 
658             << sliceNo << " - " << maxNo << G4endl;
659#endif
660      // Do collection between sliceNo and maxNo inclusive
661      //
662      for (equivNo=sliceNo+1; equivNo<=maxNo; equivNo++)
663      {
664        delete fslices[equivNo]->GetNode();
665        delete fslices[equivNo];
666        fslices[equivNo] = equivProxy;
667      }
668      sliceNo = maxNo;
669    }
670  }
671}
672
673// ***************************************************************************
674// Collects common headers at our level, deleting all but one to save
675// memory, and adjusting stored slice pointers appropriately.
676//
677// Preconditions:
678// o if a header forms part of a range of equivalent slices
679//   (ie. GetMaxEquivalentSliceNo()>GetMinEquivalentSliceNo()),
680//   it is assumed that all slices in the range are headers.
681// o this will be true if a constant Expression is used to evaluate
682//   when to refine nodes.
683// ***************************************************************************
684//
685void G4SmartVoxelHeader::CollectEquivalentHeaders()
686{
687  G4int sliceNo, maxNo, equivNo;
688  G4int maxNode = fslices.size();
689  G4SmartVoxelHeader *equivHeader, *sampleHeader;
690  G4SmartVoxelProxy *equivProxy;
691
692  for (sliceNo=0; sliceNo<maxNode; sliceNo++)
693  {
694    equivProxy = fslices[sliceNo];
695    if (equivProxy->IsHeader())
696    {
697      equivHeader = equivProxy->GetHeader();
698      maxNo = equivHeader->GetMaxEquivalentSliceNo();
699      if (maxNo != sliceNo)
700      {
701        // Attempt collection between sliceNo and maxNo inclusive:
702        // look for common headers. All slices between sliceNo and maxNo
703        // are guaranteed to be headers but may not have equal contents
704        //
705#ifdef G4GEOMETRY_VOXELDEBUG
706        G4cout << "**** G4SmartVoxelHeader::CollectEquivalentHeaders" << G4endl
707               << "     Collecting Headers =";
708#endif
709        for (equivNo=sliceNo+1; equivNo<=maxNo; equivNo++)
710        {
711          sampleHeader = fslices[equivNo]->GetHeader();
712          if ( (*sampleHeader) == (*equivHeader) )
713          {
714#ifdef G4GEOMETRY_VOXELDEBUG
715            G4cout << " " << equivNo;
716#endif
717            // Delete sampleHeader + proxy and replace with equivHeader/Proxy
718            //
719            delete sampleHeader;
720            delete fslices[equivNo];
721            fslices[equivNo] = equivProxy;
722          }
723          else
724          {
725            // Not equal. Set this header to be
726            // the current header for comparisons
727            //
728            equivProxy  = fslices[equivNo];
729            equivHeader = equivProxy->GetHeader();
730          }
731
732        }
733#ifdef G4GEOMETRY_VOXELDEBUG
734        G4cout << G4endl;
735#endif
736        // Skip past examined slices
737        //
738        sliceNo = maxNo;
739      }
740    }
741  }
742}
743
744// ***************************************************************************
745// Builds the nodes corresponding to slices between the specified limits
746// and along the specified axis, using candidate volume no.s in the vector
747// pCandidates. If the `daughters' are replicated volumes (ie. the logical
748// volume has a single replicated/parameterised volume for a daughter)
749// the candidate no.s are interpreted as PARAMETERISED volume no.s &
750// PARAMETERISATIONs are applied to compute transformations & solid
751// dimensions appropriately. The volume must be parameterised - ie. has a
752// parameterisation object & non-consuming) - in this case.
753//
754// Returns pointer to built node "structure" (guaranteed non NULL) consisting
755// of G4SmartVoxelNodeProxies referring to G4SmartVoxelNodes.
756// ***************************************************************************
757//
758G4ProxyVector* G4SmartVoxelHeader::BuildNodes(G4LogicalVolume* pVolume,
759                                              G4VoxelLimits pLimits,
760                                        const G4VolumeNosVector* pCandidates,
761                                              EAxis pAxis)
762{
763  G4double motherMinExtent, motherMaxExtent, targetMinExtent, targetMaxExtent;
764  G4VPhysicalVolume *pDaughter=0;
765  G4VPVParameterisation *pParam=0;
766  G4VSolid *targetSolid;
767  G4AffineTransform targetTransform;
768  G4bool replicated;
769  G4int nCandidates = pCandidates->size();
770  G4int nVol, nNode, targetVolNo;
771  G4VoxelLimits noLimits;
772   
773#ifdef G4GEOMETRY_VOXELDEBUG
774  G4cout << "**** G4SmartVoxelHeader::BuildNodes" << G4endl
775         << "     Limits = " << pLimits << G4endl
776         << "       Axis = " << pAxis << G4endl
777         << " Candidates = " << nCandidates << G4endl;
778#endif
779
780  // Compute extent of logical volume's solid along this axis
781  // NOTE: results stored locally and not preserved/reused
782  //
783  G4VSolid* outerSolid = pVolume->GetSolid();
784  const G4AffineTransform origin;
785  if( !outerSolid->CalculateExtent(pAxis, pLimits, origin,
786                                   motherMinExtent, motherMaxExtent) )
787  {
788    outerSolid->CalculateExtent(pAxis, noLimits, origin,
789                                motherMinExtent, motherMaxExtent);
790  }
791  G4VolumeExtentVector minExtents(nCandidates,0.);
792  G4VolumeExtentVector maxExtents(nCandidates,0.);
793
794  if ( (pVolume->GetNoDaughters()==1)
795    && (pVolume->GetDaughter(0)->IsReplicated()==true) )
796  {
797    // Replication data not required: only parameterisation object
798    // and volume no. List used
799    //
800    pDaughter = pVolume->GetDaughter(0);
801    pParam = pDaughter->GetParameterisation();
802    if (!pParam)
803    {
804      G4cerr << "PANIC! - G4SmartVoxelHeader::BuildNodes()" << G4endl
805             << "         Replicated volume with no parameterisation object !"
806             << G4endl;
807      G4Exception("G4SmartVoxelHeader::BuildNodes()", "InvalidSetup",
808                  FatalException, "Missing parameterisation.");
809    }
810
811    // Setup daughter's transformations
812    //
813    targetTransform = G4AffineTransform(pDaughter->GetRotation(),
814                                        pDaughter->GetTranslation());
815    replicated = true;
816  }
817    else
818  {
819    replicated = false;
820  }
821   
822  // Compute extents
823  //
824  for (nVol=0; nVol<nCandidates; nVol++)
825  {
826    targetVolNo=(*pCandidates)[nVol];
827    if (replicated == false)
828    {
829      pDaughter=pVolume->GetDaughter(targetVolNo);
830
831      // Setup daughter's transformations
832      //
833      targetTransform = G4AffineTransform(pDaughter->GetRotation(),
834                                          pDaughter->GetTranslation());
835      // Get underlying (and setup) solid
836      //
837      targetSolid = pDaughter->GetLogicalVolume()->GetSolid();
838    }
839    else
840    {
841      // Find  solid
842      //
843      targetSolid = pParam->ComputeSolid(targetVolNo,pDaughter);
844
845      // Setup solid
846      //
847      targetSolid->ComputeDimensions(pParam,targetVolNo,pDaughter);
848
849      // Setup transform
850      //
851      pParam->ComputeTransformation(targetVolNo,pDaughter);
852      targetTransform = G4AffineTransform(pDaughter->GetRotation(),
853                                          pDaughter->GetTranslation());
854    }
855    // Calculate extents
856    //
857    if(!targetSolid->CalculateExtent(pAxis, pLimits, targetTransform,
858                                     targetMinExtent, targetMaxExtent))
859    {
860      targetSolid->CalculateExtent(pAxis, noLimits, targetTransform,
861                                   targetMinExtent,targetMaxExtent);
862    }
863    minExtents[nVol] = targetMinExtent;
864    maxExtents[nVol] = targetMaxExtent;
865
866    // Check not entirely outside mother when processing toplevel nodes
867    //
868    if ( (!pLimits.IsLimited()) && ((targetMaxExtent<=motherMinExtent)
869                                  ||(targetMinExtent>=motherMaxExtent)) )
870    {
871      G4cerr << "PANIC! - G4SmartVoxelHeader::BuildNodes()" << G4endl
872             << "         Daughter physical volume "
873             << pDaughter->GetName() << G4endl
874             << "         is entirely outside mother logical volume "
875             << pVolume->GetName() << " !!" << G4endl;
876      G4Exception("G4SmartVoxelHeader::BuildNodes()", "InvalidSetup",
877                  FatalException, "Overlapping daughter with mother volume.");
878    }
879
880#ifdef G4GEOMETRY_VOXELDEBUG
881    // Check for straddling volumes when debugging.
882    // If a volume is >kStraddlePercent percent over the mother
883    // boundary, print a warning.
884    //
885    if (!pLimits.IsLimited())
886    {
887      G4double width;
888      G4int kStraddlePercent=5;
889      width = maxExtents[nVol]-minExtents[nVol];
890      if ( (((motherMinExtent-minExtents[nVol])*100/width) > kStraddlePercent)
891         ||(((maxExtents[nVol]-motherMaxExtent)*100/width) > kStraddlePercent) )
892      {
893        G4cout << "**** G4SmartVoxelHeader::BuildNodes" << G4endl
894               << "     WARNING : Daughter # " << nVol
895               << " name = " << pDaughter->GetName() << G4endl
896               << "     Crosses mother boundary of logical volume, name = " 
897               << pVolume->GetName() << G4endl
898               << "     by more than " << kStraddlePercent
899               << "%" << G4endl;
900      }
901    }
902#endif
903  }
904
905  // Extents of all daughters known
906
907  // Calculate minimum slice width, only including volumes inside the limits
908  //
909  G4double minWidth = kInfinity;
910  G4double currentWidth;
911  for (nVol=0; nVol<nCandidates; nVol++)
912  {
913    // currentWidth should -always- be a positive value. Inaccurate computed extent
914    // from the solid or situations of malformed geometries (overlaps) may lead to
915    // negative values and therefore unpredictable crashes !
916    //
917    currentWidth = std::abs(maxExtents[nVol]-minExtents[nVol]);
918    if ( (currentWidth<minWidth)
919      && (maxExtents[nVol]>=pLimits.GetMinExtent(pAxis))
920      && (minExtents[nVol]<=pLimits.GetMaxExtent(pAxis)) )
921    {
922      minWidth = currentWidth;
923    }
924  }
925
926  // No. of Nodes formula - nearest integer to
927  // mother width/half min daughter width +1
928  //
929  G4double noNodesExactD = ((motherMaxExtent-motherMinExtent)*2.0/minWidth)+1.0;
930
931  // Compare with "smartless quality", i.e. the average number of slices
932  // used per contained volume.
933  //
934  G4double smartlessComputed = noNodesExactD / nCandidates;
935  G4double smartlessUser = pVolume->GetSmartless();
936  G4double smartless = (smartlessComputed <= smartlessUser)
937                       ? smartlessComputed : smartlessUser;
938  G4double noNodesSmart = smartless*nCandidates;
939  G4int    noNodesExactI = G4int(noNodesSmart);
940  G4int    noNodes = ((noNodesSmart-noNodesExactI)>=0.5)
941                     ? noNodesExactI+1 : noNodesExactI;
942  if( noNodes == 0 ) { noNodes=1; }
943
944#ifdef G4GEOMETRY_VOXELDEBUG
945  G4cout << "     Smartless computed = " << smartlessComputed << G4endl
946         << "     Smartless volume = " << smartlessUser
947         << " => # Smartless = " << smartless << G4endl;
948  G4cout << "     Min width = " << minWidth
949         << " => # Nodes = " << noNodes << G4endl;
950#endif
951
952  if (noNodes>kMaxVoxelNodes)
953  {
954    noNodes=kMaxVoxelNodes;
955#ifdef G4GEOMETRY_VOXELDEBUG
956    G4cout << "     Nodes Clipped to = " << kMaxVoxelNodes << G4endl;
957#endif   
958  }
959  G4double nodeWidth = (motherMaxExtent-motherMinExtent)/noNodes;
960
961// Create G4VoxelNodes. Will Add proxies before setting fslices
962//
963  G4NodeVector* nodeList = new G4NodeVector();
964  nodeList->reserve(noNodes);
965  if (!nodeList)
966  {
967    G4cerr << "ERROR - G4SmartVoxelHeader::BuildNodes()" << G4endl
968           << "        NodeList allocation failed." << G4endl;
969    G4Exception("G4SmartVoxelHeader::BuildNodes()", "FatalError",
970                FatalException, "NodeList allocation error.");
971  }
972  for (nNode=0; nNode<noNodes; nNode++)
973  {
974    G4SmartVoxelNode *pNode;
975    pNode = new G4SmartVoxelNode(nNode);
976    if (!pNode)
977    {
978      G4cerr << "ERROR - G4SmartVoxelHeader::BuildNodes()" << G4endl
979             << "        Node allocation failed." << G4endl;
980      G4Exception("G4SmartVoxelHeader::BuildNodes()", "FatalError",
981                  FatalException, "Node allocation error.");
982    }
983    nodeList->push_back(pNode);
984  }
985
986  // All nodes created (empty)
987
988  // Fill nodes: Step through extent lists
989  //
990  for (nVol=0; nVol<nCandidates; nVol++)
991  {
992    G4int nodeNo, minContainingNode, maxContainingNode;
993    minContainingNode = G4int((minExtents[nVol]-motherMinExtent)/nodeWidth);
994    maxContainingNode = G4int((maxExtents[nVol]-motherMinExtent)/nodeWidth);
995
996    // Only add nodes that are inside the limits of the axis
997    //
998    if ( (maxContainingNode>=0) && (minContainingNode<noNodes) )
999    {
1000      // If max extent is on max boundary => maxContainingNode=noNodes:
1001      // should be one less as nodeList has noNodes entries
1002      //
1003      if (maxContainingNode>=noNodes)
1004      {
1005        maxContainingNode = noNodes-1;
1006      }
1007      //
1008      // Protection against protruding volumes
1009      //
1010      if (minContainingNode<0)
1011      {
1012        minContainingNode=0;
1013      }
1014      for (nodeNo=minContainingNode; nodeNo<=maxContainingNode; nodeNo++)
1015      {
1016        (*nodeList)[nodeNo]->Insert((*pCandidates)[nVol]);
1017      }
1018    }
1019  }
1020
1021  // All nodes filled
1022
1023  // Create proxy List : caller has deletion responsibility
1024  // (but we must delete nodeList *itself* - not the contents)
1025  //
1026  G4ProxyVector* proxyList = new G4ProxyVector();
1027  proxyList->reserve(noNodes);
1028  if (!proxyList)
1029  {
1030    G4cerr << "ERROR - G4SmartVoxelHeader::BuildNodes()" << G4endl
1031           << "        Proxy list allocation failed." << G4endl;
1032    G4Exception("G4SmartVoxelHeader::BuildNodes()", "FatalError",
1033                FatalException, "Proxy list allocation error.");
1034  }
1035  //
1036  // Fill proxy List
1037  //
1038  for (nNode=0; nNode<noNodes; nNode++)
1039  {
1040    // Get rid of possible excess capacity in the internal node vector
1041    //
1042    ((*nodeList)[nNode])->Shrink();
1043    G4SmartVoxelProxy* pProxyNode = new G4SmartVoxelProxy((*nodeList)[nNode]);
1044    if (!pProxyNode)
1045    {
1046      G4cerr << "ERROR - G4SmartVoxelHeader::BuildNodes()" << G4endl
1047             << "        Proxy node allocation failed." << G4endl;
1048      G4Exception("G4SmartVoxelHeader::BuildNodes()", "FatalError",
1049                  FatalException, "Proxy node allocation failed.");
1050    }
1051    proxyList->push_back(pProxyNode);
1052  }
1053  delete nodeList;
1054  return proxyList;
1055}
1056
1057// ***************************************************************************
1058// Calculate a "quality value" for the specified vector of voxels.
1059// The value returned should be >0 and such that the smaller the number
1060// the higher the quality of the slice.
1061//
1062// Preconditions: pSlice must consist of G4SmartVoxelNodeProxies only
1063// Process:
1064// o Examine each node in turn, summing:
1065//      no. of non-empty nodes
1066//      no. of volumes in each node
1067// o Calculate Quality=sigma(volumes in nod)/(no. of non-empty nodes)
1068//      if all nodes empty, return kInfinity
1069// o Call G4Exception on finding a G4SmartVoxelHeaderProxy
1070// ***************************************************************************
1071//
1072G4double G4SmartVoxelHeader::CalculateQuality(G4ProxyVector *pSlice)
1073{
1074  G4double quality;
1075  G4int nNodes = pSlice->size();
1076  G4int noContained, maxContained=0, sumContained=0, sumNonEmptyNodes=0;
1077  G4SmartVoxelNode *node;
1078
1079  for (G4int i=0; i<nNodes; i++)
1080  {
1081    if ((*pSlice)[i]->IsNode())
1082    {
1083      // Definitely a node. Add info to running totals
1084      //
1085      node = (*pSlice)[i]->GetNode();
1086      noContained = node->GetNoContained();
1087      if (noContained)
1088      {
1089        sumNonEmptyNodes++;
1090        sumContained += noContained;
1091        //
1092        // Calc maxContained for statistics
1093        //
1094        if (noContained>maxContained)
1095        {
1096          maxContained = noContained;
1097        }
1098      }
1099    }
1100    else
1101    {
1102      G4cout << "ERROR - G4SmartVoxelHeader::CalculateQuality()" << G4endl
1103             << "        Not defined for sliced volumes." << G4endl;
1104      G4Exception("G4SmartVoxelHeader::CalculateQuality()", "NotApplicable",
1105                  FatalException, "Not applicable to replicated volumes.");
1106    }
1107  }
1108
1109  // Calculate quality with protection against no non-empty nodes
1110  //
1111  if (sumNonEmptyNodes)
1112  {
1113    quality = sumContained/sumNonEmptyNodes;
1114  }
1115  else
1116  {
1117    quality = kInfinity;
1118  }
1119
1120#ifdef G4GEOMETRY_VOXELDEBUG
1121  G4cout << "**** G4SmartVoxelHeader::CalculateQuality" << G4endl
1122         << "     Quality = " << quality << G4endl
1123         << "     Nodes = " << nNodes
1124         << " of which " << sumNonEmptyNodes << " non empty" << G4endl
1125         << "     Max Contained = " << maxContained << G4endl;
1126#endif
1127
1128  return quality;
1129}
1130
1131// ***************************************************************************
1132// Examined each contained node, refines (creates a replacement additional
1133// dimension of voxels) when there is more than one voxel in the slice.
1134// Does not refine further if already limited in two dimensions (=> this
1135// is the third level of limits)
1136//
1137// Preconditions: slices (nodes) have been built.
1138// ***************************************************************************
1139//
1140void G4SmartVoxelHeader::RefineNodes(G4LogicalVolume* pVolume,
1141                                     G4VoxelLimits pLimits)
1142{
1143  G4int refinedDepth=0, minVolumes;
1144  G4int maxNode = fslices.size();
1145
1146  if (pLimits.IsXLimited()) 
1147  {
1148    refinedDepth++;
1149  }
1150  if (pLimits.IsYLimited()) 
1151  {
1152    refinedDepth++;
1153  }
1154  if (pLimits.IsZLimited()) 
1155  {
1156    refinedDepth++;
1157  }
1158
1159  // Calculate minimum number of volumes necessary to refine
1160  //
1161  switch (refinedDepth)
1162  {
1163    case 0:
1164      minVolumes=kMinVoxelVolumesLevel2;
1165      break;
1166    case 1:
1167      minVolumes=kMinVoxelVolumesLevel3;
1168      break;
1169    default:
1170      minVolumes=10000;   // catch refinedDepth=3 and errors
1171      break;
1172  }
1173
1174  if (refinedDepth<2)
1175  {
1176    G4int targetNo, noContainedDaughters, minNo, maxNo, replaceNo, i;
1177    G4double sliceWidth = (fmaxExtent-fminExtent)/maxNode;
1178    G4VoxelLimits newLimits;
1179    G4SmartVoxelNode* targetNode;
1180    G4SmartVoxelProxy* targetNodeProxy;
1181    G4SmartVoxelHeader* replaceHeader;
1182    G4SmartVoxelProxy* replaceHeaderProxy;
1183    G4VolumeNosVector* targetList;
1184    G4SmartVoxelProxy* lastProxy;
1185     
1186    for (targetNo=0; targetNo<maxNode; targetNo++)
1187    {
1188      // Assume all slices are nodes (see preconditions)
1189      //
1190      targetNodeProxy = fslices[targetNo];
1191      targetNode = targetNodeProxy->GetNode();
1192
1193      if (targetNode->GetNoContained() >= minVolumes)
1194      {
1195        noContainedDaughters = targetNode->GetNoContained();
1196        targetList = new G4VolumeNosVector();
1197        targetList->reserve(noContainedDaughters);
1198        if (!targetList)
1199        {
1200          G4cerr << "ERROR - G4SmartVoxelHeader::RefineNodes()" << G4endl
1201                 << "        Target volume node list allocation failed."
1202                 << G4endl;
1203          G4Exception("G4SmartVoxelHeader::RefineNodes()",
1204                      "FatalError", FatalException,
1205                      "Target volume node list allocation error.");
1206        }
1207        for (i=0; i<noContainedDaughters; i++)
1208        {
1209          targetList->push_back(targetNode->GetVolume(i));
1210        }
1211        minNo = targetNode->GetMinEquivalentSliceNo();
1212        maxNo = targetNode->GetMaxEquivalentSliceNo();
1213
1214#ifdef G4GEOMETRY_VOXELDEBUG
1215        G4cout << "**** G4SmartVoxelHeader::RefineNodes" << G4endl
1216               << "     Refining nodes " << minNo
1217               << " - " << maxNo << " inclusive" << G4endl;
1218#endif
1219        // Delete node proxies at start of collected sets of nodes/headers
1220        //
1221        lastProxy=0;
1222        for (replaceNo=minNo; replaceNo<=maxNo; replaceNo++)
1223        {
1224          if (lastProxy != fslices[replaceNo])
1225          {
1226            lastProxy=fslices[replaceNo];
1227            delete lastProxy;
1228          }
1229        }
1230        // Delete node to be replaced
1231        //
1232        delete targetNode;
1233
1234        // Create new headers + proxies and replace in fslices
1235        //
1236        newLimits = pLimits;
1237        newLimits.AddLimit(faxis,fminExtent+sliceWidth*minNo,
1238                           fminExtent+sliceWidth*(maxNo+1));
1239        replaceHeader = new G4SmartVoxelHeader(pVolume,newLimits,
1240                                               targetList,replaceNo);
1241        if (!replaceHeader)
1242        {
1243          G4cerr << "ERROR - G4SmartVoxelHeader::RefineNodes()" << G4endl
1244                 << "        Refined VoxelHeader allocation failed." << G4endl;
1245          G4Exception("G4SmartVoxelHeader::RefineNodes()", "FatalError",
1246                      FatalException, "Refined VoxelHeader allocation error.");
1247        }
1248        replaceHeader->SetMinEquivalentSliceNo(minNo);
1249        replaceHeader->SetMaxEquivalentSliceNo(maxNo);
1250        replaceHeaderProxy = new G4SmartVoxelProxy(replaceHeader);
1251        if (!replaceHeader)
1252        {
1253          G4cerr << "ERROR - G4SmartVoxelHeader::RefineNodes()" << G4endl
1254                 << "        Refined VoxelProxy allocation failed." << G4endl;
1255          G4Exception("G4SmartVoxelHeader::RefineNodes()", "FatalError",
1256                      FatalException, "Refined VoxelProxy allocation error.");
1257        }
1258        for (replaceNo=minNo; replaceNo<=maxNo; replaceNo++)
1259        {
1260          fslices[replaceNo] = replaceHeaderProxy;
1261        }
1262        // Finished replacing current `equivalent' group
1263        //
1264        delete targetList;
1265        targetNo=maxNo;
1266      }
1267    }
1268  }
1269}
1270
1271// ***************************************************************************
1272// Returns true if all slices have equal contents.
1273// Preconditions: all equal slices have been collected.
1274// Procedure:
1275// o checks all slice proxy pointers are equal
1276// o returns true if only one slice or all slice proxies pointers equal.
1277// ***************************************************************************
1278//
1279G4bool G4SmartVoxelHeader::AllSlicesEqual() const
1280{
1281  G4int noSlices = fslices.size();
1282  G4SmartVoxelProxy* refProxy;
1283
1284  if (noSlices>1)
1285  {
1286    refProxy=fslices[0];
1287    for (G4int i=1; i<noSlices; i++)
1288    {
1289      if (refProxy!=fslices[i])
1290      {
1291        return false;
1292      }
1293    }
1294  }
1295  return true;
1296}
1297
1298// ***************************************************************************
1299// Streaming operator for debugging.
1300// ***************************************************************************
1301//
1302std::ostream& operator << (std::ostream& s, const G4SmartVoxelHeader& h)
1303{
1304  s << "Axis = " << G4int(h.faxis) << G4endl;
1305  G4SmartVoxelProxy *collectNode=0, *collectHead=0;
1306  G4int collectNodeNo=0;
1307  G4int collectHeadNo=0;
1308  size_t i, j;
1309  G4bool haveHeaders=false;
1310
1311  for (i=0; i<h.fslices.size(); i++)
1312  {
1313    s << "Slice #" << i << " = ";
1314    if (h.fslices[i]->IsNode())
1315    {
1316      if (h.fslices[i]!=collectNode)
1317      {
1318        s << "{";
1319        for (G4int j=0; j<h.fslices[i]->GetNode()->GetNoContained(); j++)
1320        {
1321          s << " " << h.fslices[i]->GetNode()->GetVolume(j);
1322        }
1323        s << " }" << G4endl;
1324        collectNode = h.fslices[i];
1325        collectNodeNo = i;
1326      }
1327      else
1328      {
1329        s << "As slice #" << collectNodeNo << G4endl;
1330      }
1331    }
1332    else
1333    {
1334      haveHeaders=true;
1335      if (h.fslices[i] != collectHead)
1336      {
1337        s << "Header" << G4endl;
1338        collectHead = h.fslices[i];
1339        collectHeadNo = i;
1340      }
1341      else
1342      {
1343        s << "As slice #" << collectHeadNo << G4endl;
1344      }
1345    }
1346  }
1347
1348  if (haveHeaders)
1349  {
1350    collectHead=0;
1351    for (j=0; j<h.fslices.size(); j++)
1352    {
1353      if (h.fslices[j]->IsHeader())
1354      {
1355        s << "Header at Slice #" << j << " = ";
1356        if (h.fslices[j] != collectHead)
1357        {
1358          s << G4endl
1359            << (*(h.fslices[j]->GetHeader()));
1360          collectHead = h.fslices[j];
1361          collectHeadNo = j;
1362        }
1363        else
1364        {
1365          s << "As slice #" << collectHeadNo << G4endl;
1366        }
1367      }
1368    }
1369  }
1370  return s;
1371}
Note: See TracBrowser for help on using the repository browser.