Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/examples/advanced/fastAerosol/src/FastAerosol.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

Diff markup

Differences between /examples/advanced/fastAerosol/src/FastAerosol.cc (Version 11.3.0) and /examples/advanced/fastAerosol/src/FastAerosol.cc (Version 10.2.p1)


  1 //                                                  1 
  2 // *******************************************    
  3 // * License and Disclaimer                       
  4 // *                                              
  5 // * The  Geant4 software  is  copyright of th    
  6 // * the Geant4 Collaboration.  It is provided    
  7 // * conditions of the Geant4 Software License    
  8 // * LICENSE and available at  http://cern.ch/    
  9 // * include a list of copyright holders.         
 10 // *                                              
 11 // * Neither the authors of this software syst    
 12 // * institutes,nor the agencies providing fin    
 13 // * work  make  any representation or  warran    
 14 // * regarding  this  software system or assum    
 15 // * use.  Please see the license in the file     
 16 // * for the full disclaimer and the limitatio    
 17 // *                                              
 18 // * This  code  implementation is the result     
 19 // * technical work of the GEANT4 collaboratio    
 20 // * By using,  copying,  modifying or  distri    
 21 // * any work based  on the software)  you  ag    
 22 // * use  in  resulting  scientific  publicati    
 23 // * acceptance of all terms of the Geant4 Sof    
 24 // *******************************************    
 25 //                                                
 26                                                   
 27 // -------------------------------------------    
 28 // Implementation for FastAerosol class           
 29 // Author: A.Knaian (ara@nklabs.com), N.MacFad    
 30 // -------------------------------------------    
 31                                                   
 32 #include "FastAerosol.hh"                         
 33                                                   
 34 #include "G4SystemOfUnits.hh"                     
 35 #include "G4GeometryTolerance.hh"                 
 36                                                   
 37 #include <numeric>  // for summing vectors wit    
 38                                                   
 39 // multithreaded safety                           
 40 //#include <atomic>                               
 41 #include "G4AutoLock.hh"                          
 42 namespace                                         
 43 {                                                 
 44  G4Mutex gridMutex = G4MUTEX_INITIALIZER;         
 45  G4Mutex sphereMutex = G4MUTEX_INITIALIZER;       
 46 }                                                 
 47                                                   
 48 //////////////////////////////////////////////    
 49 //                                                
 50 // Constructor - check inputs                     
 51 //             - initialize grid                  
 52 //             - cache values                     
 53 //                                                
 54 FastAerosol::FastAerosol(const G4String& pName    
 55              G4double pR, G4double pMinD, G4do    
 56         G4double pdR,                             
 57               std::function<G4double (G4ThreeV    
 58   : fName(pName), fCloud(pCloud), fMinD(pMinD)    
 59 {                                                 
 60  kCarTolerance = G4GeometryTolerance::GetInsta    
 61                                                   
 62  // get and set bounding box dimensions           
 63  G4ThreeVector minBounding, maxBounding;          
 64  fCloud->BoundingLimits(minBounding, maxBoundi    
 65  G4ThreeVector halfSizeVec = 0.5*(maxBounding     
 66                                                   
 67  G4double pX = halfSizeVec[0];                    
 68  G4double pY = halfSizeVec[1];                    
 69  G4double pZ = halfSizeVec[2];                    
 70                                                   
 71  if (pX < 2*kCarTolerance ||                      
 72        pY < 2*kCarTolerance ||                    
 73    pZ < 2*kCarTolerance)  // limit to thicknes    
 74   {                                               
 75    std::ostringstream message;                    
 76    message << "Dimensions too small for cloud:    
 77             << GetName() << "!" << G4endl         
 78       << "     fDx, fDy, fDz = " << pX << ", "    
 79     G4Exception("FastAerosol::FastAerosol()",     
 80           FatalException, message);               
 81   }                                               
 82   fDx = pX;                                       
 83   fDy = pY;                                       
 84   fDz = pZ;                                       
 85                                                   
 86                                                   
 87   // check and set droplet radius                 
 88   if (pR < 0.0)                                   
 89   {                                               
 90     std::ostringstream message;                   
 91     message << "Invalid radius for cloud: " <<    
 92         << "        Radius must be positive."     
 93         << "        Inputs: pR = " << pR;         
 94     G4Exception("FastAerosol::FastAerosol()",     
 95           FatalErrorInArgument, message);         
 96   }                                               
 97   fR = pR;                                        
 98   fR2 = fR*fR;                                    
 99                                                   
100                                                   
101   // check and set droplet radius safety          
102   if (pdR < 0.0 || pdR > fR)                      
103   {                                               
104     std::ostringstream message;                   
105     message << "Invalid sphericality measure f    
106         << "        Radius uncertainty must be    
107         << "        Inputs: pdR = " << pdR        
108         << "        Inputs: pR = " << pR;         
109     G4Exception("FastAerosol::FastAerosol()",     
110           FatalErrorInArgument, message);         
111   }                                               
112   fdR = pdR;                                      
113                                                   
114                                                   
115   // check and set number density                 
116   if (pAvgNumDens <= 0)                           
117   {                                               
118     std::ostringstream message;                   
119     message << "Invalid average number density    
120         << "     pAvgNumDens = " << pAvgNumDen    
121     G4Exception("FastAerosol::FastAerosol()",     
122           FatalException, message);               
123   }                                               
124   fAvgNumDens = pAvgNumDens;                      
125                                                   
126                                                   
127   // Set collision limit for collsion between     
128   // no droplets will be placed closer than th    
129   fCollisionLimit2 = (2*fR + fMinD)*(2*fR + fM    
130                                                   
131   // set maximum number of droplet that we are    
132   fMaxDropCount = (G4int)floor(fAvgNumDens*(fC    
133                                                   
134   // initialize grid variables                    
135   InitializeGrid();                               
136                                                   
137                                                   
138   // begin cache of voxelized circles and sphe    
139   // see header for more details on these data    
140   G4AutoLock lockSphere(&sphereMutex);  // loc    
141                                                   
142   fCircleCollection.push_back({{0, 0}});  // t    
143   fSphereCollection.push_back({{{0}}}); // the    
144                                                   
145   fMaxCircleR = 0;                                
146   fMaxSphereR = 0;                                
147                                                   
148   MakeSphere(fPreSphereR);                        
149                                                   
150   lockSphere.unlock();          // unlock         
151                                                   
152   // vector search radius. In terms of voxel w    
153   // you need to search a larger area if fR is    
154   fVectorSearchRadius = (G4int)ceill(fR/fGridP    
155 }                                                 
156                                                   
157 //////////////////////////////////////////////    
158 //                                                
159 // Alternative Constructor                        
160 //                                                
161 // Same as standard constructor except with a     
162 //                                                
163 FastAerosol::FastAerosol(const G4String& pName    
164   FastAerosol(pName, pCloud, pR, pMinD, pNumDe    
165         [](G4ThreeVector) {return 1.0;})          
166 {}                                                
167                                                   
168 //////////////////////////////////////////////    
169 //                                                
170 // Alternative Constructor                        
171 //                                                
172 // Same as standard constructor except with a     
173 // assuming no uncertainty in sphericality        
174 //                                                
175 FastAerosol::FastAerosol(const G4String& pName    
176 {}                                                
177                                                   
178 //////////////////////////////////////////////    
179 //                                                
180 // Initialize grid                                
181 //                                                
182 // Sets grids to initial values and calculates    
183 // for each voxel                                 
184 //                                                
185 void FastAerosol::InitializeGrid()                
186 {                                                 
187  // set pitch so, on average, fDropletsPerVoxe    
188  fGridPitch = std::pow(fDropletsPerVoxel/fAvgN    
189                                                   
190  // if a voxel has center farther than this di    
191  // we know it is fully contained in the bulk     
192  fEdgeDistance = fGridPitch*std::sqrt(3.0)/2.0    
193                                                   
194  // set number of grid cells                      
195  fNx = (G4int)ceill(2*fDx / fGridPitch);          
196  fNy = (G4int)ceill(2*fDy / fGridPitch);          
197  fNz = (G4int)ceill(2*fDz / fGridPitch);          
198  fNumGridCells = (long) fNx*fNy*fNz;              
199  fNxy = fNx*fNy;                                  
200                                                   
201  // try... catch... because vectors can only b    
202  // thus you can't set grid too fine              
203  try                                              
204  {                                                
205    std::vector<G4ThreeVector> emptyVoxel{};       
206    fGrid.resize(fNumGridCells, emptyVoxel);       
207    fGridMean.resize(fNumGridCells, 0);            
208    fGridValid = new std::atomic<bool>[fNumGrid    
209   }                                               
210   catch ( const std::bad_alloc&)                  
211   {                                               
212    std::ostringstream message;                    
213    message << "Out of memory! Grid pitch too s    
214            << GetName() << "!" << G4endl          
215      << "        Asked for fNumGridCells = "      
216      << fNumGridCells << G4endl                   
217      << "        each with element of size "      
218      <<   sizeof(std::vector<G4ThreeVector>) <    
219      << "        each with element of size " <    
220      << G4endl                                    
221      << "        but the max size is " << fGri    
222    G4Exception("FastAerosol::FastAerosol()", "    
223     FatalErrorInArgument, message); }             
224                                                   
225  // initialize grid cache                         
226  G4ThreeVector voxelCenter;                       
227  G4bool valid;                                    
228                                                   
229  for (int i=0; i<fNumGridCells; i++)              
230  {                                                
231   voxelCenter = fGridPitch*(GetIndexCoord(i)+G    
232   voxelCenter -= G4ThreeVector(fDx,fDy,fDz);      
233                                                   
234  //shift all voxels so that aerosol center is     
235  // whether or not the grid is 'finished'         
236  // fGridValid[i] is true if we don't plan on     
237  // more droplets in the voxel                    
238  // false if otherwise                            
239  valid = (fCloud->Inside(voxelCenter) != kInsi    
240                                                   
241  if (valid) // will not populate the voxel        
242   {                                               
243    // have to store 'valid' in this way so tha    
244    fGridValid[i].store(true, std::memory_order    
245   }                                               
246   else  // might need to populate the voxel       
247   {                                               
248    fGridValid[i].store(false, std::memory_orde    
249                                                   
250    // find the overlap of the voxel with the b    
251    G4double volScaling=1.0;                       
252    G4bool edgeVoxel =  ( kInside != fCloud->In    
253    if (edgeVoxel)                                 
254   {                                               
255    volScaling = VoxelOverlap(voxelCenter, 100,    
256       }                                           
257    // calculates number of droplets based off     
258   fGridMean[i] = std::max(0.0, volScaling*fDis    
259     }                                             
260   }                                               
261                                                   
262 // must scale fGridMean[i] so that the desired    
263 // this is because no restrictions are applied    
264 G4double tempScaling = (fCloud->GetCubicVolume    
265   for (int i=0; i<fNumGridCells; i++) {fGridMe    
266 }                                                 
267                                                   
268 //////////////////////////////////////////////    
269 //                                                
270 // Calculate the overlap of the voxel with the    
271 //                                                
272 // The method is largely copied from G4VSolid:    
273 //                                                
274 G4double FastAerosol::VoxelOverlap(G4ThreeVect    
275 {                                                 
276  G4double cenX = voxelCenter.x();                 
277  G4double cenY = voxelCenter.y();                 
278  G4double cenZ = voxelCenter.z();                 
279                                                   
280  G4int iInside=0;                                 
281  G4double px,py,pz;                               
282  G4ThreeVector p;                                 
283  G4bool in;                                       
284                                                   
285  if(nStat < 100)    nStat   = 100;                
286  if(epsilon > 0.01) epsilon = 0.01;               
287                                                   
288  for(G4int i = 0; i < nStat; i++ )                
289  {                                                
290   px = cenX+(fGridPitch-epsilon)*(G4UniformRan    
291   py = cenY+(fGridPitch-epsilon)*(G4UniformRan    
292   pz = cenZ+(fGridPitch-epsilon)*(G4UniformRan    
293   p  = G4ThreeVector(px,py,pz);                   
294   in = (fCloud->Inside(p) == kInside) && (fClo    
295   if(in) iInside++;                               
296  }                                                
297                                                   
298  return (double)iInside/nStat;                    
299 }                                                 
300                                                   
301 //////////////////////////////////////////////    
302 //                                                
303 // Populate all voxels                            
304 //                                                
305 // Allows for partially populated clouds.         
306 //                                                
307 void FastAerosol::PopulateAllGrids()              
308 {                                                 
309  unsigned int gi;                                 
310                                                   
311  for (int xi=0; xi<fNx; xi++)                     
312   {                                               
313    for (int yi=0; yi<fNy; yi++)                   
314     {                                             
315      for (int zi=0; zi<fNz; zi++)                 
316   {                                               
317     // PopulateGrid takes unsigned arguments      
318     // fNx, fNy, and fNz  are signed so that s    
319     // thus we iterate with xi, yi, and zi sig    
320     PopulateGrid((unsigned)xi,(unsigned)yi,(un    
321    }                                              
322       }                                           
323   }                                               
324 }                                                 
325                                                   
326 //////////////////////////////////////////////    
327 //                                                
328 // Populate a single grid                         
329 //                                                
330 // If grid is already populated, does nothing.    
331 //                                                
332 void FastAerosol::PopulateGrid(unsigned int xi    
333 {                                                 
334  gi = GetGridIndex(xi,yi,zi);                     
335                                                   
336  // Check if this grid needs update               
337  bool tmpValid = fGridValid[gi].load(std::memo    
338  // have to do this weirdly because we are usi    
339  std::atomic_thread_fence(std::memory_order_ac    
340                                                   
341  if (!tmpValid) // if true then either outside    
342  {                                                
343   G4AutoLock lockGrid(&gridMutex);                
344  // Check again now that we have the lock - in    
345   tmpValid = fGridValid[gi].load(std::memory_o    
346                                                   
347   if (!tmpValid)                                  
348    {                                              
349 // uniquely set the seed to randomly place dro    
350 // changing global seed gaurantees a totally n    
351     fCloudEngine.setSeed((long)gi + (long)fNum    
352                                                   
353 // find if the voxel is near the bulk edge. In    
354 // if not an edge voxel, we can place droplets    
355   G4ThreeVector voxelCenter = fGridPitch*G4Thr    
356  G4bool edgeVoxel = ( kInside != fCloud->Insid    
357                                                   
358  // number of droplets to place                   
359   unsigned int numDropletsToPlace = CLHEP::Ran    
360                                                   
361  // actually add the points                       
362  G4ThreeVector point;                             
363                                                   
364  while (numDropletsToPlace > 0)                   
365   {                                               
366    // find a new point not overlapping with th    
367    if (FindNewPoint(edgeVoxel, fGridPitch, fGr    
368    {                                              
369     fGrid[gi].push_back(point);                   
370     fNumDroplets++;                               
371     }                                             
372    numDropletsToPlace--;                          
373  }                                                
374                                                   
375 // Memory fence to ensure sequential consisten    
376 // because fGridValid is read without a lock      
377 // Voxel data update must be complete before u    
378       std::atomic_thread_fence(std::memory_ord    
379                                                   
380 fGridValid[gi].store(true, std::memory_order_r    
381  }                                                
382                                                   
383 lockGrid.unlock();                                
384  }                                                
385 }                                                 
386                                                   
387 //////////////////////////////////////////////    
388 //                                                
389 // Find a new droplet position in a box of ful    
390 // with minimum corner at (minX, minY, minZ).     
391 //                                                
392 // -------------------------------------------    
393 //                                                
394 // Ex: FindNewPoint(fGridPitch, fGridPitch, fG    
395 //                   (G4double)xi*fGridPitch-f    
396 //                   (G4double)zi*fGridPitch-f    
397 //                                                
398 // 1) CLHEP::RandFlat::shoot(&fCloudEngine)*fG    
399 //    [0, fGridPitch]                             
400 //                                                
401 // 1.5) Note that the minimum x value of the (    
402 //      xi*fGridPitch-fDx                         
403 //                                                
404 // 2) xi*fGridPitch-fDx + (1) adds the minimum    
405 //    voxel                                       
406 //                                                
407 // -------------------------------------------    
408 //                                                
409 // Ex: FindNewPoint(2.0*fDx, 2.0*fDy, 2.0*fDz,    
410 //                                                
411 // 1) CLHEP::RandFlat::shoot(&fCloudEngine)*2.    
412 //     range [0, 2.0*fDx]                         
413 //                                                
414 // 2) add -fDx to change range into [-fDx, fDx    
415 //                                                
416 G4bool FastAerosol::FindNewPoint(G4bool edgeVo    
417  {                                                
418  G4int tries = 0;                                 
419  // counter of tries. Give up after fNumNewPoi    
420                                                   
421  G4double x, y, z;                                
422                                                   
423  G4ThreeVector point;                             
424  G4bool placedOutside = false;                    
425  // variable whether or not the point was plac    
426                                                   
427  // Generate a droplet and check if it overlap    
428  do {                                             
429      tries++;                                     
430      if (tries > fNumNewPointTries)               
431       // skip if we tried more than fNumNewPoi    
432   {                                               
433    fNumDropped++;                                 
434    if (fNumDropped < fMaxDropCount)               
435    // return error if we skipped more than fMa    
436    {                                              
437      return false;                                
438    }                                              
439                                                   
440   std::ostringstream message;                     
441   message << "Threw out too many droplets for     
442           << GetName()  << G4endl                 
443     << "        Tried to place individual drop    
444     << fNumNewPointTries << " times." << G4end    
445     << "        This failed for " << fMaxDropC    
446     << " droplets.";                              
447     G4Exception("FastAerosol::FindNewPoint()",    
448     FatalErrorInArgument, message);               
449     }                                             
450                                                   
451     x = minX + CLHEP::RandFlat::shoot(&fCloudE    
452     y = minY + CLHEP::RandFlat::shoot(&fCloudE    
453     z = minZ + CLHEP::RandFlat::shoot(&fCloudE    
454                                                   
455   if (edgeVoxel)                                  
456    {                                              
457     point = G4ThreeVector(x,y,z);                 
458     placedOutside = (fCloud->Inside(point) !=     
459     }                                             
460   }                                               
461   while (CheckCollision(x,y,z) || placedOutsid    
462                                                   
463   foundPt = G4ThreeVector(x,y,z);                 
464   return true;                                    
465 }                                                 
466                                                   
467 //////////////////////////////////////////////    
468 //                                                
469 // Get absolutely nearest droplet                 
470 //                                                
471 // Maximum search radius is maxSearch             
472 //                                                
473 // Start in grid of point p, check if any sper    
474 // and then go out one more (spherically-shape    
475 // and so on, until the whole volume has been     
476 // in general as you go out, you always need t    
477 // the one you have (at the closer level) is t    
478 //                                                
479 bool FastAerosol::GetNearestDroplet(const G4Th    
480 {                                                 
481  G4double cloudDistance = fCloud->DistanceToIn    
482                                                   
483  // starting radius/diam of voxel layer           
484  G4int searchRad = (G4int)floor(0.5+cloudDista    
485                                                   
486  // find the voxel containing p                   
487  int xGrid, yGrid, zGrid;                         
488  GetGrid(p, xGrid, yGrid, zGrid);                 
489  // it is OK if the starting point is outside     
490                                                   
491  // initialize the containers for all candidat    
492  std::vector<G4ThreeVector> candidates;           
493  std::vector<G4double> distances;                 
494  // initialize distances to indicate that no d    
495  G4double minDistance = kInfinity;                
496  G4double maxCheckDistance = kInfinity;           
497                                                   
498  bool unsafe = true;                              
499                                                   
500  while (unsafe)                                   
501  {                                                
502   // limit maximum search radius                  
503   if (searchRad*fGridPitch > maxSearch+fGridPi    
504                                                   
505    SearchSphere(searchRad, minDistance, candid    
506   maxCheckDistance = minDistance+fdR;             
507                                                   
508   // theory says that, to safely have searched    
509     // *** unsure if fully accurate. Calculati    
510  unsafe = searchRad < std::ceil(0.25+maxCheckD    
511                                                   
512  searchRad++;                                     
513  }                                                
514                                                   
515 // delete any collected droplets that are too     
516  unsigned int index = 0;                          
517                                                   
518  G4ThreeVector tempCenter;                        
519  G4double tempDistance;                           
520                                                   
521  minRealDistance = kInfinity;                     
522                                                   
523  while (index < distances.size())                 
524  {                                                
525   if (distances[index]>maxCheckDistance)          
526    {                                              
527     candidates.erase(candidates.begin()+index)    
528     distances.erase(distances.begin()+index);     
529    }                                              
530   else                                            
531   {                                               
532    tempCenter = candidates[index];                
533    tempDistance = droplet->DistanceToIn( rotat    
534                                                   
535    if (tempDistance < minRealDistance)            
536     {                                             
537       minRealDistance = tempDistance;             
538       center = tempCenter;                        
539     }                                             
540     index++; // move onto next droplet            
541     }                                             
542   }                                               
543                                                   
544   return true;                                    
545 }                                                 
546                                                   
547 //////////////////////////////////////////////    
548 //                                                
549 // Search sphere of radius R for droplets         
550 //                                                
551 // Saves any droplets to "center" if they are     
552 // (updating this minimum found distance at ea    
553 //                                                
554 // Returns if minDistance<infinity (i.e., if w    
555 //                                                
556 void FastAerosol::SearchSphere(G4int searchRad    
557 {                                                 
558  // search variables                              
559  G4int xSearch, ySearch, zSearch;                 
560  // x, y, and z coordinates of the currently s    
561  // in the shell voxel layer variables            
562  fSphereType shell; // the shell that we are s    
563                                                   
564 // we pre-calculate spheres up to radius fPreS    
565 // any sphere smaller than that does not need     
566 if (searchRad > fPreSphereR)                      
567  {                                                
568   // need to consider making a sphere             
569   G4AutoLock lockSphere(&sphereMutex);            
570   if (searchRad > fMaxSphereR)                    
571    {                                              
572     // actually need to make a sphere             
573     shell = MakeSphere(searchRad);                
574    }                                              
575 else                                              
576   {                                               
577     // we have previously made a sphere large     
578     shell = fSphereCollection[searchRad];         
579    }                                              
580   lockSphere.unlock();                            
581  }                                                
582  else                                             
583  {                                                
584   shell = fSphereCollection[searchRad];           
585  }                                                
586                                                   
587  // search spherical voxel layer                  
588  for (int i=0; i<(2*searchRad+1); i++)            
589  {                                                
590   xSearch = xGrid+i-searchRad;                    
591   if (0<=xSearch && xSearch<fNx)                  
592    {                                              
593     for (int j=0; j<(2*searchRad+1); j++)         
594      {                                            
595        ySearch = yGrid+j-searchRad;               
596                                                   
597        if (0<=ySearch && ySearch<fNy)             
598   {                                               
599     for (auto it = shell[i][j].begin(); it !=     
600     zSearch = *it+zGrid;                          
601                                                   
602     if (0<=zSearch && zSearch<fNz)                
603       {           GetNearestDropletInsideGrid(    
604       }                                           
605   }                                               
606   }                                               
607       }                                           
608     }                                             
609    }                                              
610 }                                                 
611                                                   
612 //////////////////////////////////////////////    
613 //                                                
614 // Make voxelized spheres up to radius R for f    
615 //                                                
616 // These spheres are used for searching for dr    
617 //                                                
618 // To create them, we first create a helper ha    
619 // voxel/point in zr represents a layer of our    
620 // zr represents the radius of the layer and t    
621 // layer's displacement from the origin along     
622 // z in our convention).                          
623 //                                                
624 std::vector<std::vector<std::vector<int>>> Fas    
625 // inductively make smaller spheres since fSph    
626 if (fMaxSphereR<(R-1)) { MakeSphere(R-1);}        
627                                                   
628 std::vector<int> z;                               
629 // z-coordinates of voxels in (x,y) of sphere     
630                                                   
631 std::vector<std::vector<int>> y(2*R+1, z);        
632 // plane YZ of sphere                             
633                                                   
634 fSphereType x(2*R+1, y); // entire sphere         
635                                                   
636 x[R][R].push_back(R);// add (0,0,R)               
637 x[R][R].push_back(-R);// add (0,0,-R)             
638 fCircleType zr = MakeHalfCircle(R);               
639 // break sphere into a collection of circles c    
640                                                   
641 for (auto it1 = zr.begin(); it1 != zr.end(); +    
642 {                                                 
643  std::vector<int> pt1 = *it1;                     
644  fCircleType circ = MakeCircle(pt1[1]); // mak    
645                                                   
646   for (auto it2 = circ.begin(); it2 != circ.en    
647     {                                             
648      std::vector<int> pt2 = *it2;                 
649       x[pt2[0]+R][pt2[1]+R].push_back(pt1[0]);    
650      }                                            
651 }                                                 
652                                                   
653  fSphereCollection.push_back(x);                  
654  fMaxSphereR = R;                                 
655  return x;                                        
656 }                                                 
657                                                   
658 //////////////////////////////////////////////    
659 //                                                
660 // Make voxelized circles up to radius R for f    
661 //                                                
662 // These circles are used to create voxelized     
663 // used for searching for droplets). This just    
664 // a half-circle, adding the points (R,0) and     
665 // circle along the x-axis.                       
666 //                                                
667 std::vector<std::vector<int>> FastAerosol::Mak    
668 {                                                 
669 // inductively make smaller circles since fCir    
670  if (fMaxCircleR<R)                               
671   {                                               
672    if (fMaxCircleR<(R-1)) {MakeCircle(R-1);}      
673    fCircleType voxels = {{R, 0}, {-R, 0}};        
674    // add (R,0) and (-R,0) since half circle e    
675    fCircleType halfVoxels = MakeHalfCircle(R);    
676                                                   
677    // join the voxels, halfVoxels, and -halfVo    
678    for (auto it = halfVoxels.begin(); it != ha    
679     {                                             
680      std::vector<int> pt = *it;                   
681      voxels.push_back(pt);                        
682      voxels.push_back({-pt[0], -pt[1]});          
683     }                                             
684     fCircleCollection.push_back(voxels);          
685     fMaxCircleR++;                                
686   }                                               
687                                                   
688  return fCircleCollection[R];                     
689 }                                                 
690                                                   
691 //////////////////////////////////////////////    
692 //                                                
693 // Make half circle by a modified midpoint cir    
694 //                                                
695 // Modifies the algorithm so it doesn't have '    
696 //                                                
697 // See B. Roget, J. Sitaraman, "Wall distance     
698 // voxelized marching spheres," Journal of Com    
699 // May 2013, pp. 76-94, https://doi.org/10.101    
700 //                                                
701 std::vector<std::vector<int>> FastAerosol::Mak    
702  {                                                
703 // makes an octant of a voxelized circle in th    
704  fCircleType voxels = {{0, R}}; // hard code i    
705  int x = 1;                                       
706  int y = R;                                       
707  int dx = 4-R;                                    
708  // measure of whether (x+1,y) will be outside    
709  int dxup = 1;                                    
710  // measure of whether (x+1,y+1) will be outsi    
711  while (x<y)                                      
712  {                                                
713   // mirror the points to be added to change o    
714   voxels.push_back({ x, y});                      
715   voxels.push_back({ y, x});                      
716   voxels.push_back({-x, y});                      
717  voxels.push_back({-y, x});                       
718   // if next point outside the sphere, de-incr    
719   if (dx>0)                                       
720    {                                              
721   // if dxup<=0, the layer above just moves to    
722   // this would create a hole at (x+1,y) unles    
723     dxup = dx + 2*y -2*R-1;                       
724     if ((dxup<=0) && (x+1<y))                     
725      {                                            
726   voxels.push_back({ 1+x,   y});                  
727   voxels.push_back({   y, 1+x});                  
728   voxels.push_back({-1-x,   y});                  
729   voxels.push_back({  -y, 1+x});                  
730      }                                            
731                                                   
732     dx += 4-2*y;                                  
733     y--;                                          
734    }                                              
735                                                   
736   dx += 1+2*x;                                    
737   x++;                                            
738  }                                                
739                                                   
740  // add points on y=+-x                           
741  if (x==y || (x==y+1 && dxup<=0))                 
742  {                                                
743   voxels.push_back({x,x});                        
744   voxels.push_back({-x,x});                       
745  }                                                
746                                                   
747  return voxels;                                   
748 }                                                 
749                                                   
750 //////////////////////////////////////////////    
751 //                                                
752 // Get nearest droplet inside a voxel at (xGri    
753 //                                                
754 void FastAerosol::GetNearestDropletInsideGrid(    
755 {                                                 
756  unsigned int gi;                                 
757  PopulateGrid(xGrid, yGrid, zGrid, gi);           
758                                                   
759  // initialize values                             
760  G4double foundDistance;                          
761 //  std::vector<G4ThreeVector>::iterator bestP    
762                                                   
763  // find closest droplet                          
764  for (auto it = fGrid[gi].begin(); it != fGrid    
765   {                                               
766    foundDistance = std::sqrt((p-*it).mag2());     
767                                                   
768    if (foundDistance < minDistance+fdR)           
769     {                                             
770   minDistance = std::min(minDistance, foundDis    
771   candidates.push_back(*it);                      
772   distances.push_back(foundDistance);             
773   }                                               
774  }                                                
775 }                                                 
776                                                   
777 //////////////////////////////////////////////    
778 //                                                
779 // Get nearest droplet along vector               
780 //                                                
781 // Maximum search radius is fVectorSearchRadiu    
782 // Maximum distance travelled along vector is     
783 //                                                
784 // 1) Find the closest voxel along v that is i    
785 // 2) Search for droplets in a box width (2*fV    
786 // that our ray would  intersect                  
787 // 3) If so, save the droplet that is closest     
788 // 4) If not, step 0.99*fGridPitch along v unt    
789 // 5) If outside bulk, jump until inside bulk     
790 // 6) If inside bulk, search the new voxels in    
791 // centered at our new point                      
792 // 7) If we find a point, do as in (3). If not    
793 //                                                
794 // This is searching box-shaped regions of vox    
795 // for droplets which fit inside a voxel, whic    
796 // to make sure that fR < fGridPitch at initia    
797 //                                                
798 bool FastAerosol::GetNearestDroplet(const G4Th    
799 {                                                 
800  // get the grid box this initial position is     
801  int xGrid, yGrid, zGrid;                         
802  GetGrid(p, xGrid, yGrid, zGrid);                 
803                                                   
804  // initialize some values                        
805  G4ThreeVector currentP = p;                      
806  G4double distanceAlongVector = 0.0;              
807  G4double deltaDistanceAlongVector;               
808                                                   
809  int newXGrid = 0; int newYGrid = 0; int newZG    
810  int deltaX = 0; int deltaY = 0; int deltaZ =     
811  int edgeX = 0 ; int edgeY = 0; int edgeZ = 0;    
812  G4bool boxSearch = true;                         
813                                                   
814  G4double distanceToCloud;                        
815                                                   
816  minDistance = kInfinity;                         
817                                                   
818  // actual search                                 
819  while(true)                                      
820   {                                               
821    deltaDistanceAlongVector = 0.0;                
822    // Jump gaps                                   
823    distanceToCloud = DistanceToCloud(currentP,    
824    if (distanceToCloud == kInfinity)              
825   {return(minDistance != kInfinity);}             
826     else if (distanceToCloud > fGridPitch)        
827     {                                             
828     deltaDistanceAlongVector = distanceToCloud    
829     distanceAlongVector += deltaDistanceAlongV    
830     boxSearch = true;// if jumping gap, use bo    
831           currentP += deltaDistanceAlongVector    
832           // skip gaps                            
833     }                                             
834                                                   
835   // Search for droplets                          
836   if (distanceAlongVector > maxSearch)// quit     
837    {                                              
838    return(minDistance != kInfinity);              
839     }                                             
840    else                                           
841      {                                            
842     if (boxSearch)  // we jumped                  
843       {                                           
844        GetGrid(currentP, xGrid, yGrid, zGrid);    
845         GetNearestDropletInsideRegion(minDista    
846                     fVectorSearchRadius, fVect    
847       }                                           
848     else      // didn't jump                      
849     {                                             
850     // Searching endcaps                          
851     // =================                          
852     if (deltaX != 0)                              
853     {                                             
854     GetNearestDropletInsideRegion(minDistance,    
855     edgeX, yGrid, zGrid, 0, fVectorSearchRadiu    
856     droplet, rotation);}                          
857                                                   
858      if (deltaY != 0)                             
859      {                                            
860       GetNearestDropletInsideRegion(minDistanc    
861       xGrid, edgeY, zGrid, fVectorSearchRadius    
862      }                                            
863                                                   
864      if (deltaZ != 0)                             
865      {                                            
866       GetNearestDropletInsideRegion(minDistanc    
867      }                                            
868                                                   
869 // Search bars                                    
870 // ===========                                    
871 // (a bar joins two endcaps)                      
872 if (deltaX != 0 && deltaY != 0)                   
873 {         GetNearestDropletInsideRegion(minDis    
874                               0, 0, fVectorSea    
875             droplet, rotation);                   
876         }                                         
877                                                   
878  if (deltaX != 0 && deltaZ != 0)                  
879  {            GetNearestDropletInsideRegion(mi    
880                                                   
881 if (deltaY != 0 && deltaZ != 0)                   
882  {                                                
883  GetNearestDropletInsideRegion(minDistance, ce    
884                                xGrid, edgeY, e    
885                                fVectorSearchRa    
886                                p, normalizedV,    
887  }                                                
888                                                   
889 // Search corner                                  
890 // =============                                  
891  if (deltaX != 0 && deltaY != 0 && deltaZ != 0    
892                                                   
893 // Update position                                
894 // ================================               
895 xGrid = newXGrid; yGrid = newYGrid; zGrid = ne    
896 }                                                 
897                                                   
898 // Check if we are done                           
899 // ====================                           
900 if (distanceAlongVector>minDistance+fdR) {retu    
901                                                   
902 // walk along the grid                            
903 // advance by 0.99 fGridPitch  (0.99 rather th    
904 //caused by rounding errors for lines parallel    
905                                                   
906 while (true) {                                    
907  deltaDistanceAlongVector = fGridPitch*0.99;      
908  distanceAlongVector += deltaDistanceAlongVect    
909                                                   
910  // exit returning false if we have traveled m    
911  if (distanceAlongVector > maxSearch) { return    
912                                                   
913  currentP += deltaDistanceAlongVector*normaliz    
914  GetGrid(currentP, newXGrid, newYGrid, newZGri    
915                                                   
916  if ((newXGrid != xGrid) || (newYGrid != yGrid    
917  {                                                
918   deltaX = newXGrid - xGrid;                      
919   edgeX = xGrid+deltaX*(1+fVectorSearchRadius)    
920   deltaY = newYGrid - yGrid;                      
921   edgeY = yGrid+deltaY*(1+fVectorSearchRadius)    
922   deltaZ = newZGrid - zGrid;                      
923   edgeZ = zGrid+deltaZ*(1+fVectorSearchRadius)    
924                                                   
925   break;                                          
926   }                                               
927  }                                                
928   boxSearch = false;                              
929   }                                               
930   }                                               
931                                                   
932 return false;                                     
933 }                                                 
934                                                   
935 //////////////////////////////////////////////    
936 //                                                
937 // Get nearest droplet (along a vector normali    
938 // (xGridCenter, yGridCenter, zGridCenter) of     
939 //                                                
940 // This searches box-shaped regions.              
941 //                                                
942 void FastAerosol::GetNearestDropletInsideRegio    
943 {                                                 
944  for (int xGrid = (xGridCenter-xWidth); xGrid<    
945  {                                                
946   if (0<=xGrid && xGrid<fNx)                      
947     {                                             
948      for (int yGrid = (yGridCenter-yWidth); yG    
949   {                                               
950     if (0<=yGrid && yGrid<fNy)                    
951      {                                            
952       for (int zGrid = (zGridCenter-zWidth); z    
953        {                                          
954     if (0<=zGrid && zGrid<fNz)                    
955       {                                           
956               GetNearestDropletInsideGrid(minD    
957        }                                          
958         }                                         
959         }                                         
960   }                                               
961      }                                            
962   }                                               
963 }                                                 
964                                                   
965 //////////////////////////////////////////////    
966 //                                                
967 // Get nearest droplet inside a voxel at (xGri    
968 //                                                
969 // Return the closest one, as measured along t    
970 //                                                
971 void FastAerosol::GetNearestDropletInsideGrid(    
972 {                                                 
973  unsigned int gi;                                 
974  PopulateGrid(xGrid, yGrid, zGrid, gi);           
975                                                   
976  G4double foundDistance;                          
977                                                   
978  // find closest droplet                          
979  for (auto it = fGrid[gi].begin(); it != fGrid    
980   {                                               
981 // could have the following check to see if th    
982 /*                                                
983 deltaP = *it-p;                                   
984 foundDistance = normalizedV.dot(deltaP);          
985                                                   
986 if ((0<=foundDistance) && ((deltaP - normalize    
987 */                                                
988                                                   
989  G4RotationMatrix irotm = rotation(*it).invers    
990                                                   
991   G4ThreeVector relPos = irotm*(p - *it);         
992                                                   
993   if (droplet->Inside(relPos) == kInside)         
994   {                                               
995    minDistance = 0;                               
996    center = *it;                                  
997   }                                               
998   else                                            
999   {                                               
1000    foundDistance = droplet->DistanceToIn( rel    
1001                                                  
1002     if (foundDistance<minDistance)               
1003   {                                              
1004    minDistance = foundDistance;                  
1005    center = *it;                                 
1006    }                                             
1007    }                                             
1008  }                                               
1009 }                                                
1010                                                  
1011 /////////////////////////////////////////////    
1012 //                                               
1013 // Check if a droplet at the indicated point     
1014 //                                               
1015 // Search neighboring voxels, too. Returns tr    
1016 //                                               
1017 bool FastAerosol::CheckCollision(G4double x,     
1018 {                                                
1019  G4ThreeVector p(x,y,z);                         
1020                                                  
1021  std::pair<int, int> minMaxXGrid, minMaxYGrid    
1022                                                  
1023  int xGrid, yGrid, zGrid;                        
1024  GetGrid(p, xGrid, yGrid, zGrid);                
1025                                                  
1026  minMaxXGrid = GetMinMaxSide(xGrid, fNx);        
1027  minMaxYGrid = GetMinMaxSide(yGrid, fNy);        
1028  minMaxZGrid = GetMinMaxSide(zGrid, fNz);        
1029                                                  
1030  for (int xi=minMaxXGrid.first; xi <= minMaxX    
1031   for (int yi = minMaxYGrid.first; yi <= minM    
1032     for (int zi = minMaxZGrid.first; zi <= mi    
1033   if (CheckCollisionInsideGrid(x, y, z, (unsi    
1034     fNumCollisions++;  // log number of colli    
1035     return(true);                                
1036     }                                            
1037           }                                      
1038     }                                            
1039   }                                              
1040   return(false);                                 
1041 }                                                
1042                                                  
1043 /////////////////////////////////////////////    
1044 //                                               
1045 // Check if a droplet at the indicated point     
1046 // droplet inside a specific grid                
1047 //                                               
1048 // Note that you don't need to lock the mutex    
1049 // that already has the mutex (always called     
1050 //                                               
1051 bool FastAerosol::CheckCollisionInsideGrid(G4    
1052 {                                                
1053  std::vector<G4ThreeVector> *thisGrid = &(fGr    
1054  unsigned int numel = (unsigned int)thisGrid-    
1055                                                  
1056  for (unsigned int i=0; i < numel; i++)          
1057  {                                               
1058   if (CheckCollisionWithDroplet(x, y, z, (*th    
1059    return(true);                                 
1060  }                                               
1061                                                  
1062  return(false);                                  
1063 }                                                
1064                                                  
1065 /////////////////////////////////////////////    
1066 //                                               
1067 // Check for collsion with a specific droplet    
1068 //                                               
1069 bool FastAerosol::CheckCollisionWithDroplet(G    
1070 {                                                
1071 return( std::pow(x-p.x(), 2.0) + std::pow(y-p    
1072 }                                                
1073                                                  
1074 /////////////////////////////////////////////    
1075 //                                               
1076 // Save droplet positions to a file for visua    
1077 //                                               
1078 void FastAerosol::SaveToFile(const char* file    
1079 {                                                
1080  G4cout << "Saving droplet positions to " <<     
1081  std::ofstream file;                             
1082  file.open(filename);                            
1083                                                  
1084  std::vector<G4ThreeVector> voxel;               
1085  G4ThreeVector pt;                               
1086                                                  
1087  for (auto it1 = fGrid.begin(); it1 != fGrid.    
1088       {                                          
1089   voxel = *it1;                                  
1090                                                  
1091    for (auto it2 = voxel.begin(); it2 != voxe    
1092         {                                        
1093     pt = *it2;                                   
1094                                                  
1095     double x = pt.x();                           
1096     double y = pt.y();                           
1097     double z = pt.z();                           
1098     file << std::setprecision(15) << x/mm <<     
1099          << y/mm << "," << z/mm << "\n";         
1100     }                                            
1101   }                                              
1102  file.close();                                   
1103 }                                                
1104