File indexing completed on 2025-01-18 09:16:36
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032 #include "FastAerosol.hh"
0033
0034 #include "G4SystemOfUnits.hh"
0035 #include "G4GeometryTolerance.hh"
0036
0037 #include <numeric> // for summing vectors with accumulate
0038
0039
0040
0041 #include "G4AutoLock.hh"
0042 namespace
0043 {
0044 G4Mutex gridMutex = G4MUTEX_INITIALIZER;
0045 G4Mutex sphereMutex = G4MUTEX_INITIALIZER;
0046 }
0047
0048
0049
0050
0051
0052
0053
0054 FastAerosol::FastAerosol(const G4String& pName, G4VSolid* pCloud,
0055 G4double pR, G4double pMinD, G4double pAvgNumDens,
0056 G4double pdR,
0057 std::function<G4double (G4ThreeVector)> pDistribution)
0058 : fName(pName), fCloud(pCloud), fMinD(pMinD), fDistribution(pDistribution)
0059 {
0060 kCarTolerance = G4GeometryTolerance::GetInstance()->GetSurfaceTolerance();
0061
0062
0063 G4ThreeVector minBounding, maxBounding;
0064 fCloud->BoundingLimits(minBounding, maxBounding);
0065 G4ThreeVector halfSizeVec = 0.5*(maxBounding - minBounding);
0066
0067 G4double pX = halfSizeVec[0];
0068 G4double pY = halfSizeVec[1];
0069 G4double pZ = halfSizeVec[2];
0070
0071 if (pX < 2*kCarTolerance ||
0072 pY < 2*kCarTolerance ||
0073 pZ < 2*kCarTolerance)
0074 {
0075 std::ostringstream message;
0076 message << "Dimensions too small for cloud: "
0077 << GetName() << "!" << G4endl
0078 << " fDx, fDy, fDz = " << pX << ", " << pY << ", " << pZ;
0079 G4Exception("FastAerosol::FastAerosol()", "GeomSolids0002",
0080 FatalException, message);
0081 }
0082 fDx = pX;
0083 fDy = pY;
0084 fDz = pZ;
0085
0086
0087
0088 if (pR < 0.0)
0089 {
0090 std::ostringstream message;
0091 message << "Invalid radius for cloud: " << GetName() << "!" << G4endl
0092 << " Radius must be positive."
0093 << " Inputs: pR = " << pR;
0094 G4Exception("FastAerosol::FastAerosol()", "GeomSolids0002",
0095 FatalErrorInArgument, message);
0096 }
0097 fR = pR;
0098 fR2 = fR*fR;
0099
0100
0101
0102 if (pdR < 0.0 || pdR > fR)
0103 {
0104 std::ostringstream message;
0105 message << "Invalid sphericality measure for cloud: " << GetName() << "!" << G4endl
0106 << " Radius uncertainty must be between 0 and fR."
0107 << " Inputs: pdR = " << pdR
0108 << " Inputs: pR = " << pR;
0109 G4Exception("FastAerosol::FastAerosol()", "GeomSolids0002",
0110 FatalErrorInArgument, message);
0111 }
0112 fdR = pdR;
0113
0114
0115
0116 if (pAvgNumDens <= 0)
0117 {
0118 std::ostringstream message;
0119 message << "Invalid average number density for cloud: " << GetName() << "!" << G4endl
0120 << " pAvgNumDens = " << pAvgNumDens;
0121 G4Exception("FastAerosol::FastAerosol()", "GeomSolids0002",
0122 FatalException, message);
0123 }
0124 fAvgNumDens = pAvgNumDens;
0125
0126
0127
0128
0129 fCollisionLimit2 = (2*fR + fMinD)*(2*fR + fMinD);
0130
0131
0132 fMaxDropCount = (G4int)floor(fAvgNumDens*(fCloud->GetCubicVolume())*(0.01*fMaxDropPercent));
0133
0134
0135 InitializeGrid();
0136
0137
0138
0139
0140 G4AutoLock lockSphere(&sphereMutex);
0141
0142 fCircleCollection.push_back({{0, 0}});
0143 fSphereCollection.push_back({{{0}}});
0144
0145 fMaxCircleR = 0;
0146 fMaxSphereR = 0;
0147
0148 MakeSphere(fPreSphereR);
0149
0150 lockSphere.unlock();
0151
0152
0153
0154 fVectorSearchRadius = (G4int)ceill(fR/fGridPitch);
0155 }
0156
0157
0158
0159
0160
0161
0162
0163 FastAerosol::FastAerosol(const G4String& pName, G4VSolid* pCloud, G4double pR, G4double pMinD, G4double pNumDens, G4double pdR):
0164 FastAerosol(pName, pCloud, pR, pMinD, pNumDens, pdR,
0165 [](G4ThreeVector) {return 1.0;})
0166 {}
0167
0168
0169
0170
0171
0172
0173
0174
0175 FastAerosol::FastAerosol(const G4String& pName, G4VSolid* pCloud, G4double pR, G4double pMinD, G4double pNumDens): FastAerosol(pName, pCloud, pR, pMinD, pNumDens, 0.0,[](G4ThreeVector) {return 1.0;})
0176 {}
0177
0178
0179
0180
0181
0182
0183
0184
0185 void FastAerosol::InitializeGrid()
0186 {
0187
0188 fGridPitch = std::pow(fDropletsPerVoxel/fAvgNumDens,1.0/3.0);
0189
0190
0191
0192 fEdgeDistance = fGridPitch*std::sqrt(3.0)/2.0 + fR;
0193
0194
0195 fNx = (G4int)ceill(2*fDx / fGridPitch);
0196 fNy = (G4int)ceill(2*fDy / fGridPitch);
0197 fNz = (G4int)ceill(2*fDz / fGridPitch);
0198 fNumGridCells = (long) fNx*fNy*fNz;
0199 fNxy = fNx*fNy;
0200
0201
0202
0203 try
0204 {
0205 std::vector<G4ThreeVector> emptyVoxel{};
0206 fGrid.resize(fNumGridCells, emptyVoxel);
0207 fGridMean.resize(fNumGridCells, 0);
0208 fGridValid = new std::atomic<bool>[fNumGridCells];
0209 }
0210 catch ( const std::bad_alloc&)
0211 {
0212 std::ostringstream message;
0213 message << "Out of memory! Grid pitch too small for cloud: "
0214 << GetName() << "!" << G4endl
0215 << " Asked for fNumGridCells = "
0216 << fNumGridCells << G4endl
0217 << " each with element of size "
0218 << sizeof(std::vector<G4ThreeVector>) << G4endl
0219 << " each with element of size " << sizeof(bool)
0220 << G4endl
0221 << " but the max size is " << fGrid.max_size() << "!";
0222 G4Exception("FastAerosol::FastAerosol()", "GeomSolids0002",
0223 FatalErrorInArgument, message); }
0224
0225
0226 G4ThreeVector voxelCenter;
0227 G4bool valid;
0228
0229 for (int i=0; i<fNumGridCells; i++)
0230 {
0231 voxelCenter = fGridPitch*(GetIndexCoord(i)+G4ThreeVector(0.5,0.5,0.5));
0232 voxelCenter -= G4ThreeVector(fDx,fDy,fDz);
0233
0234
0235
0236
0237
0238
0239 valid = (fCloud->Inside(voxelCenter) != kInside && fCloud->DistanceToIn(voxelCenter) >= fEdgeDistance);
0240
0241 if (valid)
0242 {
0243
0244 fGridValid[i].store(true, std::memory_order_release);
0245 }
0246 else
0247 {
0248 fGridValid[i].store(false, std::memory_order_release);
0249
0250
0251 G4double volScaling=1.0;
0252 G4bool edgeVoxel = ( kInside != fCloud->Inside(voxelCenter) || fCloud->DistanceToOut(voxelCenter) < fEdgeDistance );
0253 if (edgeVoxel)
0254 {
0255 volScaling = VoxelOverlap(voxelCenter, 100, 0.0);
0256 }
0257
0258 fGridMean[i] = std::max(0.0, volScaling*fDistribution(voxelCenter));
0259 }
0260 }
0261
0262
0263
0264 G4double tempScaling = (fCloud->GetCubicVolume())*fAvgNumDens/accumulate(fGridMean.begin(), fGridMean.end(), 0.0);
0265 for (int i=0; i<fNumGridCells; i++) {fGridMean[i] *= tempScaling;}
0266 }
0267
0268
0269
0270
0271
0272
0273
0274 G4double FastAerosol::VoxelOverlap(G4ThreeVector voxelCenter, G4int nStat, G4double epsilon)
0275 {
0276 G4double cenX = voxelCenter.x();
0277 G4double cenY = voxelCenter.y();
0278 G4double cenZ = voxelCenter.z();
0279
0280 G4int iInside=0;
0281 G4double px,py,pz;
0282 G4ThreeVector p;
0283 G4bool in;
0284
0285 if(nStat < 100) nStat = 100;
0286 if(epsilon > 0.01) epsilon = 0.01;
0287
0288 for(G4int i = 0; i < nStat; i++ )
0289 {
0290 px = cenX+(fGridPitch-epsilon)*(G4UniformRand()-0.5);
0291 py = cenY+(fGridPitch-epsilon)*(G4UniformRand()-0.5);
0292 pz = cenZ+(fGridPitch-epsilon)*(G4UniformRand()-0.5);
0293 p = G4ThreeVector(px,py,pz);
0294 in = (fCloud->Inside(p) == kInside) && (fCloud->DistanceToOut(p) >= fR);
0295 if(in) iInside++;
0296 }
0297
0298 return (double)iInside/nStat;
0299 }
0300
0301
0302
0303
0304
0305
0306
0307 void FastAerosol::PopulateAllGrids()
0308 {
0309 unsigned int gi;
0310
0311 for (int xi=0; xi<fNx; xi++)
0312 {
0313 for (int yi=0; yi<fNy; yi++)
0314 {
0315 for (int zi=0; zi<fNz; zi++)
0316 {
0317
0318
0319
0320 PopulateGrid((unsigned)xi,(unsigned)yi,(unsigned)zi,gi);
0321 }
0322 }
0323 }
0324 }
0325
0326
0327
0328
0329
0330
0331
0332 void FastAerosol::PopulateGrid(unsigned int xi, unsigned int yi, unsigned int zi, unsigned int& gi)
0333 {
0334 gi = GetGridIndex(xi,yi,zi);
0335
0336
0337 bool tmpValid = fGridValid[gi].load(std::memory_order_acquire);
0338
0339 std::atomic_thread_fence(std::memory_order_acquire);
0340
0341 if (!tmpValid)
0342 {
0343 G4AutoLock lockGrid(&gridMutex);
0344
0345 tmpValid = fGridValid[gi].load(std::memory_order_acquire);
0346
0347 if (!tmpValid)
0348 {
0349
0350
0351 fCloudEngine.setSeed((long)gi + (long)fNumGridCells*(long)fSeed);
0352
0353
0354
0355 G4ThreeVector voxelCenter = fGridPitch*G4ThreeVector(xi+0.5,yi+0.5,zi+0.5)-G4ThreeVector(fDx,fDy,fDz);
0356 G4bool edgeVoxel = ( kInside != fCloud->Inside(voxelCenter) || fCloud->DistanceToOut(voxelCenter) < fEdgeDistance );
0357
0358
0359 unsigned int numDropletsToPlace = CLHEP::RandPoisson::shoot(&fCloudEngine, fGridMean[gi]);
0360
0361
0362 G4ThreeVector point;
0363
0364 while (numDropletsToPlace > 0)
0365 {
0366
0367 if (FindNewPoint(edgeVoxel, fGridPitch, fGridPitch, fGridPitch, (G4double)xi*fGridPitch-fDx, (G4double)yi*fGridPitch-fDy, (G4double)zi*fGridPitch-fDz, point) )
0368 {
0369 fGrid[gi].push_back(point);
0370 fNumDroplets++;
0371 }
0372 numDropletsToPlace--;
0373 }
0374
0375
0376
0377
0378 std::atomic_thread_fence(std::memory_order_release);
0379
0380 fGridValid[gi].store(true, std::memory_order_release);
0381 }
0382
0383 lockGrid.unlock();
0384 }
0385 }
0386
0387
0388
0389
0390
0391
0392
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402
0403
0404
0405
0406
0407
0408
0409
0410
0411
0412
0413
0414
0415
0416 G4bool FastAerosol::FindNewPoint(G4bool edgeVoxel, G4double dX, G4double dY, G4double dZ, G4double minX, G4double minY, G4double minZ, G4ThreeVector &foundPt)
0417 {
0418 G4int tries = 0;
0419
0420
0421 G4double x, y, z;
0422
0423 G4ThreeVector point;
0424 G4bool placedOutside = false;
0425
0426
0427
0428 do {
0429 tries++;
0430 if (tries > fNumNewPointTries)
0431
0432 {
0433 fNumDropped++;
0434 if (fNumDropped < fMaxDropCount)
0435
0436 {
0437 return false;
0438 }
0439
0440 std::ostringstream message;
0441 message << "Threw out too many droplets for cloud: "
0442 << GetName() << G4endl
0443 << " Tried to place individual droplest "
0444 << fNumNewPointTries << " times." << G4endl
0445 << " This failed for " << fMaxDropCount
0446 << " droplets.";
0447 G4Exception("FastAerosol::FindNewPoint()", "GeomSolids0002",
0448 FatalErrorInArgument, message);
0449 }
0450
0451 x = minX + CLHEP::RandFlat::shoot(&fCloudEngine)*dX;
0452 y = minY + CLHEP::RandFlat::shoot(&fCloudEngine)*dY;
0453 z = minZ + CLHEP::RandFlat::shoot(&fCloudEngine)*dZ;
0454
0455 if (edgeVoxel)
0456 {
0457 point = G4ThreeVector(x,y,z);
0458 placedOutside = (fCloud->Inside(point) != kInside) || (fCloud->DistanceToOut(point) < fR);
0459 }
0460 }
0461 while (CheckCollision(x,y,z) || placedOutside);
0462
0463 foundPt = G4ThreeVector(x,y,z);
0464 return true;
0465 }
0466
0467
0468
0469
0470
0471
0472
0473
0474
0475
0476
0477
0478
0479 bool FastAerosol::GetNearestDroplet(const G4ThreeVector &p, G4ThreeVector ¢er, G4double &minRealDistance, G4double maxSearch, G4VSolid* droplet, std::function<G4RotationMatrix (G4ThreeVector)> rotation)
0480 {
0481 G4double cloudDistance = fCloud->DistanceToIn(p);
0482
0483
0484 G4int searchRad = (G4int)floor(0.5+cloudDistance/fGridPitch);
0485
0486
0487 int xGrid, yGrid, zGrid;
0488 GetGrid(p, xGrid, yGrid, zGrid);
0489
0490
0491
0492 std::vector<G4ThreeVector> candidates;
0493 std::vector<G4double> distances;
0494
0495 G4double minDistance = kInfinity;
0496 G4double maxCheckDistance = kInfinity;
0497
0498 bool unsafe = true;
0499
0500 while (unsafe)
0501 {
0502
0503 if (searchRad*fGridPitch > maxSearch+fGridPitch) { return false; }
0504
0505 SearchSphere(searchRad, minDistance, candidates, distances, xGrid, yGrid, zGrid, p);
0506 maxCheckDistance = minDistance+fdR;
0507
0508
0509
0510 unsafe = searchRad < std::ceil(0.25+maxCheckDistance/fGridPitch);
0511
0512 searchRad++;
0513 }
0514
0515
0516 unsigned int index = 0;
0517
0518 G4ThreeVector tempCenter;
0519 G4double tempDistance;
0520
0521 minRealDistance = kInfinity;
0522
0523 while (index < distances.size())
0524 {
0525 if (distances[index]>maxCheckDistance)
0526 {
0527 candidates.erase(candidates.begin()+index);
0528 distances.erase(distances.begin()+index);
0529 }
0530 else
0531 {
0532 tempCenter = candidates[index];
0533 tempDistance = droplet->DistanceToIn( rotation(tempCenter).inverse()*(p - tempCenter) );
0534
0535 if (tempDistance < minRealDistance)
0536 {
0537 minRealDistance = tempDistance;
0538 center = tempCenter;
0539 }
0540 index++;
0541 }
0542 }
0543
0544 return true;
0545 }
0546
0547
0548
0549
0550
0551
0552
0553
0554
0555
0556 void FastAerosol::SearchSphere(G4int searchRad, G4double &minDistance, std::vector<G4ThreeVector> &candidates, std::vector<G4double> &distances, G4int xGrid, G4int yGrid, G4int zGrid, const G4ThreeVector &p)
0557 {
0558
0559 G4int xSearch, ySearch, zSearch;
0560
0561
0562 fSphereType shell;
0563
0564
0565
0566 if (searchRad > fPreSphereR)
0567 {
0568
0569 G4AutoLock lockSphere(&sphereMutex);
0570 if (searchRad > fMaxSphereR)
0571 {
0572
0573 shell = MakeSphere(searchRad);
0574 }
0575 else
0576 {
0577
0578 shell = fSphereCollection[searchRad];
0579 }
0580 lockSphere.unlock();
0581 }
0582 else
0583 {
0584 shell = fSphereCollection[searchRad];
0585 }
0586
0587
0588 for (int i=0; i<(2*searchRad+1); i++)
0589 {
0590 xSearch = xGrid+i-searchRad;
0591 if (0<=xSearch && xSearch<fNx)
0592 {
0593 for (int j=0; j<(2*searchRad+1); j++)
0594 {
0595 ySearch = yGrid+j-searchRad;
0596
0597 if (0<=ySearch && ySearch<fNy)
0598 {
0599 for (auto it = shell[i][j].begin(); it != shell[i][j].end(); ++it) {
0600 zSearch = *it+zGrid;
0601
0602 if (0<=zSearch && zSearch<fNz)
0603 { GetNearestDropletInsideGrid(minDistance, candidates, distances, (unsigned)xSearch, (unsigned)ySearch, (unsigned)zSearch, p);
0604 }
0605 }
0606 }
0607 }
0608 }
0609 }
0610 }
0611
0612
0613
0614
0615
0616
0617
0618
0619
0620
0621
0622
0623
0624 std::vector<std::vector<std::vector<int>>> FastAerosol::MakeSphere(G4int R) {
0625
0626 if (fMaxSphereR<(R-1)) { MakeSphere(R-1);}
0627
0628 std::vector<int> z;
0629
0630
0631 std::vector<std::vector<int>> y(2*R+1, z);
0632
0633
0634 fSphereType x(2*R+1, y);
0635
0636 x[R][R].push_back(R);
0637 x[R][R].push_back(-R);
0638 fCircleType zr = MakeHalfCircle(R);
0639
0640
0641 for (auto it1 = zr.begin(); it1 != zr.end(); ++it1)
0642 {
0643 std::vector<int> pt1 = *it1;
0644 fCircleType circ = MakeCircle(pt1[1]);
0645
0646 for (auto it2 = circ.begin(); it2 != circ.end(); ++it2)
0647 {
0648 std::vector<int> pt2 = *it2;
0649 x[pt2[0]+R][pt2[1]+R].push_back(pt1[0]);
0650 }
0651 }
0652
0653 fSphereCollection.push_back(x);
0654 fMaxSphereR = R;
0655 return x;
0656 }
0657
0658
0659
0660
0661
0662
0663
0664
0665
0666
0667 std::vector<std::vector<int>> FastAerosol::MakeCircle(G4int R)
0668 {
0669
0670 if (fMaxCircleR<R)
0671 {
0672 if (fMaxCircleR<(R-1)) {MakeCircle(R-1);}
0673 fCircleType voxels = {{R, 0}, {-R, 0}};
0674
0675 fCircleType halfVoxels = MakeHalfCircle(R);
0676
0677
0678 for (auto it = halfVoxels.begin(); it != halfVoxels.end(); ++it)
0679 {
0680 std::vector<int> pt = *it;
0681 voxels.push_back(pt);
0682 voxels.push_back({-pt[0], -pt[1]});
0683 }
0684 fCircleCollection.push_back(voxels);
0685 fMaxCircleR++;
0686 }
0687
0688 return fCircleCollection[R];
0689 }
0690
0691
0692
0693
0694
0695
0696
0697
0698
0699
0700
0701 std::vector<std::vector<int>> FastAerosol::MakeHalfCircle(G4int R)
0702 {
0703
0704 fCircleType voxels = {{0, R}};
0705 int x = 1;
0706 int y = R;
0707 int dx = 4-R;
0708
0709 int dxup = 1;
0710
0711 while (x<y)
0712 {
0713
0714 voxels.push_back({ x, y});
0715 voxels.push_back({ y, x});
0716 voxels.push_back({-x, y});
0717 voxels.push_back({-y, x});
0718
0719 if (dx>0)
0720 {
0721
0722
0723 dxup = dx + 2*y -2*R-1;
0724 if ((dxup<=0) && (x+1<y))
0725 {
0726 voxels.push_back({ 1+x, y});
0727 voxels.push_back({ y, 1+x});
0728 voxels.push_back({-1-x, y});
0729 voxels.push_back({ -y, 1+x});
0730 }
0731
0732 dx += 4-2*y;
0733 y--;
0734 }
0735
0736 dx += 1+2*x;
0737 x++;
0738 }
0739
0740
0741 if (x==y || (x==y+1 && dxup<=0))
0742 {
0743 voxels.push_back({x,x});
0744 voxels.push_back({-x,x});
0745 }
0746
0747 return voxels;
0748 }
0749
0750
0751
0752
0753
0754 void FastAerosol::GetNearestDropletInsideGrid(G4double &minDistance, std::vector<G4ThreeVector> &candidates, std::vector<G4double> &distances, unsigned int xGrid, unsigned int yGrid, unsigned int zGrid, const G4ThreeVector &p)
0755 {
0756 unsigned int gi;
0757 PopulateGrid(xGrid, yGrid, zGrid, gi);
0758
0759
0760 G4double foundDistance;
0761
0762
0763
0764 for (auto it = fGrid[gi].begin(); it != fGrid[gi].end(); ++it)
0765 {
0766 foundDistance = std::sqrt((p-*it).mag2());
0767
0768 if (foundDistance < minDistance+fdR)
0769 {
0770 minDistance = std::min(minDistance, foundDistance);
0771 candidates.push_back(*it);
0772 distances.push_back(foundDistance);
0773 }
0774 }
0775 }
0776
0777
0778
0779
0780
0781
0782
0783
0784
0785
0786
0787
0788
0789
0790
0791
0792
0793
0794
0795
0796
0797
0798 bool FastAerosol::GetNearestDroplet(const G4ThreeVector &p, const G4ThreeVector &normalizedV, G4ThreeVector ¢er, G4double &minDistance, G4double maxSearch, G4VSolid* droplet, std::function<G4RotationMatrix (G4ThreeVector)> rotation)
0799 {
0800
0801 int xGrid, yGrid, zGrid;
0802 GetGrid(p, xGrid, yGrid, zGrid);
0803
0804
0805 G4ThreeVector currentP = p;
0806 G4double distanceAlongVector = 0.0;
0807 G4double deltaDistanceAlongVector;
0808
0809 int newXGrid = 0; int newYGrid = 0; int newZGrid = 0;
0810 int deltaX = 0; int deltaY = 0; int deltaZ = 0;
0811 int edgeX = 0 ; int edgeY = 0; int edgeZ = 0;
0812 G4bool boxSearch = true;
0813
0814 G4double distanceToCloud;
0815
0816 minDistance = kInfinity;
0817
0818
0819 while(true)
0820 {
0821 deltaDistanceAlongVector = 0.0;
0822
0823 distanceToCloud = DistanceToCloud(currentP,normalizedV);
0824 if (distanceToCloud == kInfinity)
0825 {return(minDistance != kInfinity);}
0826 else if (distanceToCloud > fGridPitch)
0827 {
0828 deltaDistanceAlongVector = distanceToCloud-fGridPitch;
0829 distanceAlongVector += deltaDistanceAlongVector;
0830 boxSearch = true;
0831 currentP += deltaDistanceAlongVector*normalizedV;
0832
0833 }
0834
0835
0836 if (distanceAlongVector > maxSearch)
0837 {
0838 return(minDistance != kInfinity);
0839 }
0840 else
0841 {
0842 if (boxSearch)
0843 {
0844 GetGrid(currentP, xGrid, yGrid, zGrid);
0845 GetNearestDropletInsideRegion(minDistance, center, xGrid, yGrid, zGrid,
0846 fVectorSearchRadius, fVectorSearchRadius, fVectorSearchRadius, p, normalizedV, droplet, rotation);
0847 }
0848 else
0849 {
0850
0851
0852 if (deltaX != 0)
0853 {
0854 GetNearestDropletInsideRegion(minDistance, center,
0855 edgeX, yGrid, zGrid, 0, fVectorSearchRadius, fVectorSearchRadius, p, normalizedV,
0856 droplet, rotation);}
0857
0858 if (deltaY != 0)
0859 {
0860 GetNearestDropletInsideRegion(minDistance, center,
0861 xGrid, edgeY, zGrid, fVectorSearchRadius, 0, fVectorSearchRadius, p, normalizedV, droplet, rotation);
0862 }
0863
0864 if (deltaZ != 0)
0865 {
0866 GetNearestDropletInsideRegion(minDistance, center, xGrid, yGrid, edgeZ, fVectorSearchRadius, fVectorSearchRadius, 0, p, normalizedV, droplet, rotation);
0867 }
0868
0869
0870
0871
0872 if (deltaX != 0 && deltaY != 0)
0873 { GetNearestDropletInsideRegion(minDistance, center, edgeX, edgeY, zGrid,
0874 0, 0, fVectorSearchRadius, p, normalizedV,
0875 droplet, rotation);
0876 }
0877
0878 if (deltaX != 0 && deltaZ != 0)
0879 { GetNearestDropletInsideRegion(minDistance, center, edgeX, yGrid, edgeZ, 0, fVectorSearchRadius, 0, p, normalizedV, droplet, rotation);}
0880
0881 if (deltaY != 0 && deltaZ != 0)
0882 {
0883 GetNearestDropletInsideRegion(minDistance, center,
0884 xGrid, edgeY, edgeZ,
0885 fVectorSearchRadius, 0, 0,
0886 p, normalizedV,droplet, rotation);
0887 }
0888
0889
0890
0891 if (deltaX != 0 && deltaY != 0 && deltaZ != 0) { GetNearestDropletInsideRegion(minDistance, center, edgeX, edgeY, edgeZ, 0, 0, 0, p, normalizedV, droplet, rotation);}
0892
0893
0894
0895 xGrid = newXGrid; yGrid = newYGrid; zGrid = newZGrid;
0896 }
0897
0898
0899
0900 if (distanceAlongVector>minDistance+fdR) {return(true);}
0901
0902
0903
0904
0905
0906 while (true) {
0907 deltaDistanceAlongVector = fGridPitch*0.99;
0908 distanceAlongVector += deltaDistanceAlongVector;
0909
0910
0911 if (distanceAlongVector > maxSearch) { return(false); }
0912
0913 currentP += deltaDistanceAlongVector*normalizedV;
0914 GetGrid(currentP, newXGrid, newYGrid, newZGrid);
0915
0916 if ((newXGrid != xGrid) || (newYGrid != yGrid) || (newZGrid != zGrid))
0917 {
0918 deltaX = newXGrid - xGrid;
0919 edgeX = xGrid+deltaX*(1+fVectorSearchRadius);
0920 deltaY = newYGrid - yGrid;
0921 edgeY = yGrid+deltaY*(1+fVectorSearchRadius);
0922 deltaZ = newZGrid - zGrid;
0923 edgeZ = zGrid+deltaZ*(1+fVectorSearchRadius);
0924
0925 break;
0926 }
0927 }
0928 boxSearch = false;
0929 }
0930 }
0931
0932 return false;
0933 }
0934
0935
0936
0937
0938
0939
0940
0941
0942 void FastAerosol::GetNearestDropletInsideRegion(G4double &minDistance, G4ThreeVector ¢er, int xGridCenter, int yGridCenter, int zGridCenter, int xWidth, int yWidth, int zWidth, const G4ThreeVector &p, const G4ThreeVector &normalizedV, G4VSolid* droplet, std::function<G4RotationMatrix (G4ThreeVector)> rotation)
0943 {
0944 for (int xGrid = (xGridCenter-xWidth); xGrid<=(xGridCenter+xWidth); xGrid++)
0945 {
0946 if (0<=xGrid && xGrid<fNx)
0947 {
0948 for (int yGrid = (yGridCenter-yWidth); yGrid<=(yGridCenter+yWidth); yGrid++)
0949 {
0950 if (0<=yGrid && yGrid<fNy)
0951 {
0952 for (int zGrid = (zGridCenter-zWidth); zGrid<=(zGridCenter+zWidth); zGrid++)
0953 {
0954 if (0<=zGrid && zGrid<fNz)
0955 {
0956 GetNearestDropletInsideGrid(minDistance, center, (unsigned)xGrid, (unsigned)yGrid, (unsigned)zGrid, p, normalizedV, droplet, rotation);
0957 }
0958 }
0959 }
0960 }
0961 }
0962 }
0963 }
0964
0965
0966
0967
0968
0969
0970
0971 void FastAerosol::GetNearestDropletInsideGrid(G4double &minDistance, G4ThreeVector ¢er, unsigned int xGrid, unsigned int yGrid, unsigned int zGrid, const G4ThreeVector &p, const G4ThreeVector &normalizedV, G4VSolid* droplet, std::function<G4RotationMatrix (G4ThreeVector)> rotation)
0972 {
0973 unsigned int gi;
0974 PopulateGrid(xGrid, yGrid, zGrid, gi);
0975
0976 G4double foundDistance;
0977
0978
0979 for (auto it = fGrid[gi].begin(); it != fGrid[gi].end(); ++it)
0980 {
0981
0982
0983
0984
0985
0986
0987
0988
0989 G4RotationMatrix irotm = rotation(*it).inverse();
0990
0991 G4ThreeVector relPos = irotm*(p - *it);
0992
0993 if (droplet->Inside(relPos) == kInside)
0994 {
0995 minDistance = 0;
0996 center = *it;
0997 }
0998 else
0999 {
1000 foundDistance = droplet->DistanceToIn( relPos , irotm*normalizedV);
1001
1002 if (foundDistance<minDistance)
1003 {
1004 minDistance = foundDistance;
1005 center = *it;
1006 }
1007 }
1008 }
1009 }
1010
1011
1012
1013
1014
1015
1016
1017 bool FastAerosol::CheckCollision(G4double x, G4double y, G4double z)
1018 {
1019 G4ThreeVector p(x,y,z);
1020
1021 std::pair<int, int> minMaxXGrid, minMaxYGrid, minMaxZGrid;
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 <= minMaxXGrid.second; xi++) {
1031 for (int yi = minMaxYGrid.first; yi <= minMaxYGrid.second; yi++) {
1032 for (int zi = minMaxZGrid.first; zi <= minMaxZGrid.second; zi++) {
1033 if (CheckCollisionInsideGrid(x, y, z, (unsigned)xi, (unsigned)yi, (unsigned)zi)) {
1034 fNumCollisions++;
1035 return(true);
1036 }
1037 }
1038 }
1039 }
1040 return(false);
1041 }
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051 bool FastAerosol::CheckCollisionInsideGrid(G4double x, G4double y, G4double z, unsigned int xi, unsigned int yi, unsigned int zi)
1052 {
1053 std::vector<G4ThreeVector> *thisGrid = &(fGrid[GetGridIndex(xi, yi, zi)]);
1054 unsigned int numel = (unsigned int)thisGrid->size();
1055
1056 for (unsigned int i=0; i < numel; i++)
1057 {
1058 if (CheckCollisionWithDroplet(x, y, z, (*thisGrid)[i]))
1059 return(true);
1060 }
1061
1062 return(false);
1063 }
1064
1065
1066
1067
1068
1069 bool FastAerosol::CheckCollisionWithDroplet(G4double x, G4double y, G4double z, G4ThreeVector p )
1070 {
1071 return( std::pow(x-p.x(), 2.0) + std::pow(y-p.y(), 2.0) + std::pow(z-p.z(), 2.0) < fCollisionLimit2 );
1072 }
1073
1074
1075
1076
1077
1078 void FastAerosol::SaveToFile(const char* filename)
1079 {
1080 G4cout << "Saving droplet positions to " << filename << "..." << G4endl;
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.end(); ++it1)
1088 {
1089 voxel = *it1;
1090
1091 for (auto it2 = voxel.begin(); it2 != voxel.end(); ++it2)
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 }