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

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

update geant4-09-04-beta-cand-01 interfaces-V09-03-09 vis-V09-03-08

File size: 44.3 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.35 2010/02/09 16:50:25 gcosmo Exp $
28// GEANT4 tag $Name: geant4-09-04-beta-cand-01 $
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= kInfinity, motherMaxExtent= -kInfinity,
764           targetMinExtent= kInfinity, targetMaxExtent= -kInfinity;
765  G4VPhysicalVolume *pDaughter=0;
766  G4VPVParameterisation *pParam=0;
767  G4VSolid *targetSolid;
768  G4AffineTransform targetTransform;
769  G4bool replicated;
770  G4int nCandidates = pCandidates->size();
771  G4int nVol, nNode, targetVolNo;
772  G4VoxelLimits noLimits;
773   
774#ifdef G4GEOMETRY_VOXELDEBUG
775  G4cout << "**** G4SmartVoxelHeader::BuildNodes" << G4endl
776         << "     Limits = " << pLimits << G4endl
777         << "       Axis = " << pAxis << G4endl
778         << " Candidates = " << nCandidates << G4endl;
779#endif
780
781  // Compute extent of logical volume's solid along this axis
782  // NOTE: results stored locally and not preserved/reused
783  //
784  G4VSolid* outerSolid = pVolume->GetSolid();
785  const G4AffineTransform origin;
786  if( !outerSolid->CalculateExtent(pAxis, pLimits, origin,
787                                   motherMinExtent, motherMaxExtent) )
788  {
789    outerSolid->CalculateExtent(pAxis, noLimits, origin,
790                                motherMinExtent, motherMaxExtent);
791  }
792  G4VolumeExtentVector minExtents(nCandidates,0.);
793  G4VolumeExtentVector maxExtents(nCandidates,0.);
794
795  if ( (pVolume->GetNoDaughters()==1)
796    && (pVolume->GetDaughter(0)->IsReplicated()==true) )
797  {
798    // Replication data not required: only parameterisation object
799    // and volume no. List used
800    //
801    pDaughter = pVolume->GetDaughter(0);
802    pParam = pDaughter->GetParameterisation();
803    if (!pParam)
804    {
805      G4cerr << "PANIC! - G4SmartVoxelHeader::BuildNodes()" << G4endl
806             << "         Replicated volume with no parameterisation object !"
807             << G4endl;
808      G4Exception("G4SmartVoxelHeader::BuildNodes()", "InvalidSetup",
809                  FatalException, "Missing parameterisation.");
810    }
811
812    // Setup daughter's transformations
813    //
814    targetTransform = G4AffineTransform(pDaughter->GetRotation(),
815                                        pDaughter->GetTranslation());
816    replicated = true;
817  }
818    else
819  {
820    replicated = false;
821  }
822   
823  // Compute extents
824  //
825  for (nVol=0; nVol<nCandidates; nVol++)
826  {
827    targetVolNo=(*pCandidates)[nVol];
828    if (replicated == false)
829    {
830      pDaughter=pVolume->GetDaughter(targetVolNo);
831
832      // Setup daughter's transformations
833      //
834      targetTransform = G4AffineTransform(pDaughter->GetRotation(),
835                                          pDaughter->GetTranslation());
836      // Get underlying (and setup) solid
837      //
838      targetSolid = pDaughter->GetLogicalVolume()->GetSolid();
839    }
840    else
841    {
842      // Find  solid
843      //
844      targetSolid = pParam->ComputeSolid(targetVolNo,pDaughter);
845
846      // Setup solid
847      //
848      targetSolid->ComputeDimensions(pParam,targetVolNo,pDaughter);
849
850      // Setup transform
851      //
852      pParam->ComputeTransformation(targetVolNo,pDaughter);
853      targetTransform = G4AffineTransform(pDaughter->GetRotation(),
854                                          pDaughter->GetTranslation());
855    }
856    // Calculate extents
857    //
858    if(!targetSolid->CalculateExtent(pAxis, pLimits, targetTransform,
859                                     targetMinExtent, targetMaxExtent))
860    {
861      targetSolid->CalculateExtent(pAxis, noLimits, targetTransform,
862                                   targetMinExtent,targetMaxExtent);
863    }
864    minExtents[nVol] = targetMinExtent;
865    maxExtents[nVol] = targetMaxExtent;
866
867    // Check not entirely outside mother when processing toplevel nodes
868    //
869    if ( (!pLimits.IsLimited()) && ((targetMaxExtent<=motherMinExtent)
870                                  ||(targetMinExtent>=motherMaxExtent)) )
871    {
872      G4cerr << "PANIC! - G4SmartVoxelHeader::BuildNodes()" << G4endl
873             << "         Daughter physical volume "
874             << pDaughter->GetName() << G4endl
875             << "         is entirely outside mother logical volume "
876             << pVolume->GetName() << " !!" << G4endl;
877      G4Exception("G4SmartVoxelHeader::BuildNodes()", "InvalidSetup",
878                  FatalException, "Overlapping daughter with mother volume.");
879    }
880
881#ifdef G4GEOMETRY_VOXELDEBUG
882    // Check for straddling volumes when debugging.
883    // If a volume is >kStraddlePercent percent over the mother
884    // boundary, print a warning.
885    //
886    if (!pLimits.IsLimited())
887    {
888      G4double width;
889      G4int kStraddlePercent=5;
890      width = maxExtents[nVol]-minExtents[nVol];
891      if ( (((motherMinExtent-minExtents[nVol])*100/width) > kStraddlePercent)
892         ||(((maxExtents[nVol]-motherMaxExtent)*100/width) > kStraddlePercent) )
893      {
894        G4cout << "**** G4SmartVoxelHeader::BuildNodes" << G4endl
895               << "     WARNING : Daughter # " << nVol
896               << " name = " << pDaughter->GetName() << G4endl
897               << "     Crosses mother boundary of logical volume, name = " 
898               << pVolume->GetName() << G4endl
899               << "     by more than " << kStraddlePercent
900               << "%" << G4endl;
901      }
902    }
903#endif
904  }
905
906  // Extents of all daughters known
907
908  // Calculate minimum slice width, only including volumes inside the limits
909  //
910  G4double minWidth = kInfinity;
911  G4double currentWidth;
912  for (nVol=0; nVol<nCandidates; nVol++)
913  {
914    // currentWidth should -always- be a positive value. Inaccurate computed extent
915    // from the solid or situations of malformed geometries (overlaps) may lead to
916    // negative values and therefore unpredictable crashes !
917    //
918    currentWidth = std::abs(maxExtents[nVol]-minExtents[nVol]);
919    if ( (currentWidth<minWidth)
920      && (maxExtents[nVol]>=pLimits.GetMinExtent(pAxis))
921      && (minExtents[nVol]<=pLimits.GetMaxExtent(pAxis)) )
922    {
923      minWidth = currentWidth;
924    }
925  }
926
927  // No. of Nodes formula - nearest integer to
928  // mother width/half min daughter width +1
929  //
930  G4double noNodesExactD = ((motherMaxExtent-motherMinExtent)*2.0/minWidth)+1.0;
931
932  // Compare with "smartless quality", i.e. the average number of slices
933  // used per contained volume.
934  //
935  G4double smartlessComputed = noNodesExactD / nCandidates;
936  G4double smartlessUser = pVolume->GetSmartless();
937  G4double smartless = (smartlessComputed <= smartlessUser)
938                       ? smartlessComputed : smartlessUser;
939  G4double noNodesSmart = smartless*nCandidates;
940  G4int    noNodesExactI = G4int(noNodesSmart);
941  G4int    noNodes = ((noNodesSmart-noNodesExactI)>=0.5)
942                     ? noNodesExactI+1 : noNodesExactI;
943  if( noNodes == 0 ) { noNodes=1; }
944
945#ifdef G4GEOMETRY_VOXELDEBUG
946  G4cout << "     Smartless computed = " << smartlessComputed << G4endl
947         << "     Smartless volume = " << smartlessUser
948         << " => # Smartless = " << smartless << G4endl;
949  G4cout << "     Min width = " << minWidth
950         << " => # Nodes = " << noNodes << G4endl;
951#endif
952
953  if (noNodes>kMaxVoxelNodes)
954  {
955    noNodes=kMaxVoxelNodes;
956#ifdef G4GEOMETRY_VOXELDEBUG
957    G4cout << "     Nodes Clipped to = " << kMaxVoxelNodes << G4endl;
958#endif   
959  }
960  G4double nodeWidth = (motherMaxExtent-motherMinExtent)/noNodes;
961
962// Create G4VoxelNodes. Will Add proxies before setting fslices
963//
964  G4NodeVector* nodeList = new G4NodeVector();
965  nodeList->reserve(noNodes);
966  if (!nodeList)
967  {
968    G4cerr << "ERROR - G4SmartVoxelHeader::BuildNodes()" << G4endl
969           << "        NodeList allocation failed." << G4endl;
970    G4Exception("G4SmartVoxelHeader::BuildNodes()", "FatalError",
971                FatalException, "NodeList allocation error.");
972  }
973  for (nNode=0; nNode<noNodes; nNode++)
974  {
975    G4SmartVoxelNode *pNode;
976    pNode = new G4SmartVoxelNode(nNode);
977    if (!pNode)
978    {
979      G4cerr << "ERROR - G4SmartVoxelHeader::BuildNodes()" << G4endl
980             << "        Node allocation failed." << G4endl;
981      G4Exception("G4SmartVoxelHeader::BuildNodes()", "FatalError",
982                  FatalException, "Node allocation error.");
983    }
984    nodeList->push_back(pNode);
985  }
986
987  // All nodes created (empty)
988
989  // Fill nodes: Step through extent lists
990  //
991  for (nVol=0; nVol<nCandidates; nVol++)
992  {
993    G4int nodeNo, minContainingNode, maxContainingNode;
994    minContainingNode = G4int((minExtents[nVol]-motherMinExtent)/nodeWidth);
995    maxContainingNode = G4int((maxExtents[nVol]-motherMinExtent)/nodeWidth);
996
997    // Only add nodes that are inside the limits of the axis
998    //
999    if ( (maxContainingNode>=0) && (minContainingNode<noNodes) )
1000    {
1001      // If max extent is on max boundary => maxContainingNode=noNodes:
1002      // should be one less as nodeList has noNodes entries
1003      //
1004      if (maxContainingNode>=noNodes)
1005      {
1006        maxContainingNode = noNodes-1;
1007      }
1008      //
1009      // Protection against protruding volumes
1010      //
1011      if (minContainingNode<0)
1012      {
1013        minContainingNode=0;
1014      }
1015      for (nodeNo=minContainingNode; nodeNo<=maxContainingNode; nodeNo++)
1016      {
1017        (*nodeList)[nodeNo]->Insert((*pCandidates)[nVol]);
1018      }
1019    }
1020  }
1021
1022  // All nodes filled
1023
1024  // Create proxy List : caller has deletion responsibility
1025  // (but we must delete nodeList *itself* - not the contents)
1026  //
1027  G4ProxyVector* proxyList = new G4ProxyVector();
1028  proxyList->reserve(noNodes);
1029  if (!proxyList)
1030  {
1031    G4cerr << "ERROR - G4SmartVoxelHeader::BuildNodes()" << G4endl
1032           << "        Proxy list allocation failed." << G4endl;
1033    G4Exception("G4SmartVoxelHeader::BuildNodes()", "FatalError",
1034                FatalException, "Proxy list allocation error.");
1035  }
1036  //
1037  // Fill proxy List
1038  //
1039  for (nNode=0; nNode<noNodes; nNode++)
1040  {
1041    // Get rid of possible excess capacity in the internal node vector
1042    //
1043    ((*nodeList)[nNode])->Shrink();
1044    G4SmartVoxelProxy* pProxyNode = new G4SmartVoxelProxy((*nodeList)[nNode]);
1045    if (!pProxyNode)
1046    {
1047      G4cerr << "ERROR - G4SmartVoxelHeader::BuildNodes()" << G4endl
1048             << "        Proxy node allocation failed." << G4endl;
1049      G4Exception("G4SmartVoxelHeader::BuildNodes()", "FatalError",
1050                  FatalException, "Proxy node allocation failed.");
1051    }
1052    proxyList->push_back(pProxyNode);
1053  }
1054  delete nodeList;
1055  return proxyList;
1056}
1057
1058// ***************************************************************************
1059// Calculate a "quality value" for the specified vector of voxels.
1060// The value returned should be >0 and such that the smaller the number
1061// the higher the quality of the slice.
1062//
1063// Preconditions: pSlice must consist of G4SmartVoxelNodeProxies only
1064// Process:
1065// o Examine each node in turn, summing:
1066//      no. of non-empty nodes
1067//      no. of volumes in each node
1068// o Calculate Quality=sigma(volumes in nod)/(no. of non-empty nodes)
1069//      if all nodes empty, return kInfinity
1070// o Call G4Exception on finding a G4SmartVoxelHeaderProxy
1071// ***************************************************************************
1072//
1073G4double G4SmartVoxelHeader::CalculateQuality(G4ProxyVector *pSlice)
1074{
1075  G4double quality;
1076  G4int nNodes = pSlice->size();
1077  G4int noContained, maxContained=0, sumContained=0, sumNonEmptyNodes=0;
1078  G4SmartVoxelNode *node;
1079
1080  for (G4int i=0; i<nNodes; i++)
1081  {
1082    if ((*pSlice)[i]->IsNode())
1083    {
1084      // Definitely a node. Add info to running totals
1085      //
1086      node = (*pSlice)[i]->GetNode();
1087      noContained = node->GetNoContained();
1088      if (noContained)
1089      {
1090        sumNonEmptyNodes++;
1091        sumContained += noContained;
1092        //
1093        // Calc maxContained for statistics
1094        //
1095        if (noContained>maxContained)
1096        {
1097          maxContained = noContained;
1098        }
1099      }
1100    }
1101    else
1102    {
1103      G4cout << "ERROR - G4SmartVoxelHeader::CalculateQuality()" << G4endl
1104             << "        Not defined for sliced volumes." << G4endl;
1105      G4Exception("G4SmartVoxelHeader::CalculateQuality()", "NotApplicable",
1106                  FatalException, "Not applicable to replicated volumes.");
1107    }
1108  }
1109
1110  // Calculate quality with protection against no non-empty nodes
1111  //
1112  if (sumNonEmptyNodes)
1113  {
1114    quality = sumContained/sumNonEmptyNodes;
1115  }
1116  else
1117  {
1118    quality = kInfinity;
1119  }
1120
1121#ifdef G4GEOMETRY_VOXELDEBUG
1122  G4cout << "**** G4SmartVoxelHeader::CalculateQuality" << G4endl
1123         << "     Quality = " << quality << G4endl
1124         << "     Nodes = " << nNodes
1125         << " of which " << sumNonEmptyNodes << " non empty" << G4endl
1126         << "     Max Contained = " << maxContained << G4endl;
1127#endif
1128
1129  return quality;
1130}
1131
1132// ***************************************************************************
1133// Examined each contained node, refines (creates a replacement additional
1134// dimension of voxels) when there is more than one voxel in the slice.
1135// Does not refine further if already limited in two dimensions (=> this
1136// is the third level of limits)
1137//
1138// Preconditions: slices (nodes) have been built.
1139// ***************************************************************************
1140//
1141void G4SmartVoxelHeader::RefineNodes(G4LogicalVolume* pVolume,
1142                                     G4VoxelLimits pLimits)
1143{
1144  G4int refinedDepth=0, minVolumes;
1145  G4int maxNode = fslices.size();
1146
1147  if (pLimits.IsXLimited()) 
1148  {
1149    refinedDepth++;
1150  }
1151  if (pLimits.IsYLimited()) 
1152  {
1153    refinedDepth++;
1154  }
1155  if (pLimits.IsZLimited()) 
1156  {
1157    refinedDepth++;
1158  }
1159
1160  // Calculate minimum number of volumes necessary to refine
1161  //
1162  switch (refinedDepth)
1163  {
1164    case 0:
1165      minVolumes=kMinVoxelVolumesLevel2;
1166      break;
1167    case 1:
1168      minVolumes=kMinVoxelVolumesLevel3;
1169      break;
1170    default:
1171      minVolumes=10000;   // catch refinedDepth=3 and errors
1172      break;
1173  }
1174
1175  if (refinedDepth<2)
1176  {
1177    G4int targetNo, noContainedDaughters, minNo, maxNo, replaceNo, i;
1178    G4double sliceWidth = (fmaxExtent-fminExtent)/maxNode;
1179    G4VoxelLimits newLimits;
1180    G4SmartVoxelNode* targetNode;
1181    G4SmartVoxelProxy* targetNodeProxy;
1182    G4SmartVoxelHeader* replaceHeader;
1183    G4SmartVoxelProxy* replaceHeaderProxy;
1184    G4VolumeNosVector* targetList;
1185    G4SmartVoxelProxy* lastProxy;
1186     
1187    for (targetNo=0; targetNo<maxNode; targetNo++)
1188    {
1189      // Assume all slices are nodes (see preconditions)
1190      //
1191      targetNodeProxy = fslices[targetNo];
1192      targetNode = targetNodeProxy->GetNode();
1193
1194      if (targetNode->GetNoContained() >= minVolumes)
1195      {
1196        noContainedDaughters = targetNode->GetNoContained();
1197        targetList = new G4VolumeNosVector();
1198        targetList->reserve(noContainedDaughters);
1199        if (!targetList)
1200        {
1201          G4cerr << "ERROR - G4SmartVoxelHeader::RefineNodes()" << G4endl
1202                 << "        Target volume node list allocation failed."
1203                 << G4endl;
1204          G4Exception("G4SmartVoxelHeader::RefineNodes()",
1205                      "FatalError", FatalException,
1206                      "Target volume node list allocation error.");
1207        }
1208        for (i=0; i<noContainedDaughters; i++)
1209        {
1210          targetList->push_back(targetNode->GetVolume(i));
1211        }
1212        minNo = targetNode->GetMinEquivalentSliceNo();
1213        maxNo = targetNode->GetMaxEquivalentSliceNo();
1214
1215#ifdef G4GEOMETRY_VOXELDEBUG
1216        G4cout << "**** G4SmartVoxelHeader::RefineNodes" << G4endl
1217               << "     Refining nodes " << minNo
1218               << " - " << maxNo << " inclusive" << G4endl;
1219#endif
1220        // Delete node proxies at start of collected sets of nodes/headers
1221        //
1222        lastProxy=0;
1223        for (replaceNo=minNo; replaceNo<=maxNo; replaceNo++)
1224        {
1225          if (lastProxy != fslices[replaceNo])
1226          {
1227            lastProxy=fslices[replaceNo];
1228            delete lastProxy;
1229          }
1230        }
1231        // Delete node to be replaced
1232        //
1233        delete targetNode;
1234
1235        // Create new headers + proxies and replace in fslices
1236        //
1237        newLimits = pLimits;
1238        newLimits.AddLimit(faxis,fminExtent+sliceWidth*minNo,
1239                           fminExtent+sliceWidth*(maxNo+1));
1240        replaceHeader = new G4SmartVoxelHeader(pVolume,newLimits,
1241                                               targetList,replaceNo);
1242        if (!replaceHeader)
1243        {
1244          G4cerr << "ERROR - G4SmartVoxelHeader::RefineNodes()" << G4endl
1245                 << "        Refined VoxelHeader allocation failed." << G4endl;
1246          G4Exception("G4SmartVoxelHeader::RefineNodes()", "FatalError",
1247                      FatalException, "Refined VoxelHeader allocation error.");
1248        }
1249        replaceHeader->SetMinEquivalentSliceNo(minNo);
1250        replaceHeader->SetMaxEquivalentSliceNo(maxNo);
1251        replaceHeaderProxy = new G4SmartVoxelProxy(replaceHeader);
1252        if (!replaceHeader)
1253        {
1254          G4cerr << "ERROR - G4SmartVoxelHeader::RefineNodes()" << G4endl
1255                 << "        Refined VoxelProxy allocation failed." << G4endl;
1256          G4Exception("G4SmartVoxelHeader::RefineNodes()", "FatalError",
1257                      FatalException, "Refined VoxelProxy allocation error.");
1258        }
1259        for (replaceNo=minNo; replaceNo<=maxNo; replaceNo++)
1260        {
1261          fslices[replaceNo] = replaceHeaderProxy;
1262        }
1263        // Finished replacing current `equivalent' group
1264        //
1265        delete targetList;
1266        targetNo=maxNo;
1267      }
1268    }
1269  }
1270}
1271
1272// ***************************************************************************
1273// Returns true if all slices have equal contents.
1274// Preconditions: all equal slices have been collected.
1275// Procedure:
1276// o checks all slice proxy pointers are equal
1277// o returns true if only one slice or all slice proxies pointers equal.
1278// ***************************************************************************
1279//
1280G4bool G4SmartVoxelHeader::AllSlicesEqual() const
1281{
1282  G4int noSlices = fslices.size();
1283  G4SmartVoxelProxy* refProxy;
1284
1285  if (noSlices>1)
1286  {
1287    refProxy=fslices[0];
1288    for (G4int i=1; i<noSlices; i++)
1289    {
1290      if (refProxy!=fslices[i])
1291      {
1292        return false;
1293      }
1294    }
1295  }
1296  return true;
1297}
1298
1299// ***************************************************************************
1300// Streaming operator for debugging.
1301// ***************************************************************************
1302//
1303std::ostream& operator << (std::ostream& s, const G4SmartVoxelHeader& h)
1304{
1305  s << "Axis = " << G4int(h.faxis) << G4endl;
1306  G4SmartVoxelProxy *collectNode=0, *collectHead=0;
1307  G4int collectNodeNo=0;
1308  G4int collectHeadNo=0;
1309  size_t i, j;
1310  G4bool haveHeaders=false;
1311
1312  for (i=0; i<h.fslices.size(); i++)
1313  {
1314    s << "Slice #" << i << " = ";
1315    if (h.fslices[i]->IsNode())
1316    {
1317      if (h.fslices[i]!=collectNode)
1318      {
1319        s << "{";
1320        for (G4int j=0; j<h.fslices[i]->GetNode()->GetNoContained(); j++)
1321        {
1322          s << " " << h.fslices[i]->GetNode()->GetVolume(j);
1323        }
1324        s << " }" << G4endl;
1325        collectNode = h.fslices[i];
1326        collectNodeNo = i;
1327      }
1328      else
1329      {
1330        s << "As slice #" << collectNodeNo << G4endl;
1331      }
1332    }
1333    else
1334    {
1335      haveHeaders=true;
1336      if (h.fslices[i] != collectHead)
1337      {
1338        s << "Header" << G4endl;
1339        collectHead = h.fslices[i];
1340        collectHeadNo = i;
1341      }
1342      else
1343      {
1344        s << "As slice #" << collectHeadNo << G4endl;
1345      }
1346    }
1347  }
1348
1349  if (haveHeaders)
1350  {
1351    collectHead=0;
1352    for (j=0; j<h.fslices.size(); j++)
1353    {
1354      if (h.fslices[j]->IsHeader())
1355      {
1356        s << "Header at Slice #" << j << " = ";
1357        if (h.fslices[j] != collectHead)
1358        {
1359          s << G4endl
1360            << (*(h.fslices[j]->GetHeader()));
1361          collectHead = h.fslices[j];
1362          collectHeadNo = j;
1363        }
1364        else
1365        {
1366          s << "As slice #" << collectHeadNo << G4endl;
1367        }
1368      }
1369    }
1370  }
1371  return s;
1372}
Note: See TracBrowser for help on using the repository browser.