Skip to content

Commit d13bcdc

Browse files
Version 1.1.1 - debug sph. inc.
- Debugging of spherical inclusion geometry to ensure that spheres are non-overlapping even across periodic boundary conditions. - Minor updates to Counterparts data formatting.
1 parent 4f7fb84 commit d13bcdc

File tree

4 files changed

+97
-72
lines changed

4 files changed

+97
-72
lines changed

Core/3D/Geometry_SphericalInclusionpy.py

Lines changed: 86 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ def _initializeSampleGeometryMemory(self):
173173
self.Radii = np.array(self.Radii)
174174
vf = 4/3*np.pi*np.sum([rad**3 for rad in self.Radii])/(self.lx*self.ly*self.lz)
175175
if self.flVerbose:
176-
print(f"Placed a total of {len(self.Radii)} spheres, resulting in a final volume fraction of {vf}")
176+
print(f"Placed a total of {len(self.Radii)} spheres, resulting in a final volume fraction of {vf}")
177+
177178
# sample what material in each sphere according to user specified average material abundance in spheres
178179
self.MatInds = []
179180
for _ in range(0,len(self.Radii)):
@@ -235,43 +236,45 @@ def _generateRandomPointFastRSA(self, radius):
235236

236237
#Select a random point within the cell
237238
if self.xperiodic:
238-
if xhi > self.xbounds[1]:
239-
xhi = self.xbounds[1]
239+
if xhi > self.xbounds[1]: xhi = self.xbounds[1]
240240
else:
241-
if ix == 0:
242-
xlo = xlo+radius
243-
if ix == self.nbinx-1:
244-
xhi = self.xbounds[1] - radius
241+
if ix == 0 : xlo = xlo+radius
242+
if ix == self.nbinx-1 : xhi = self.xbounds[1] - radius
245243
x = self.Rng.uniform(xlo, xhi)
246244

247245
if self.yperiodic:
248-
if yhi > self.ybounds[1]:
249-
yhi = self.ybounds[1]
246+
if yhi > self.ybounds[1]: yhi = self.ybounds[1]
250247
else:
251-
if iy == 0:
252-
ylo = ylo+radius
253-
if iy == self.nbiny-1:
254-
yhi = self.ybounds[1] - radius
248+
if iy == 0 : ylo = ylo+radius
249+
if iy == self.nbiny-1 : yhi = self.ybounds[1] - radius
255250
y = self.Rng.uniform(ylo, yhi)
256251

257252
if self.zperiodic:
258-
if zhi > self.zbounds[1]:
259-
zhi = self.zbounds[1]
253+
if zhi > self.zbounds[1]: zhi = self.zbounds[1]
260254
else:
261-
if iz == 0:
262-
zlo = zlo+radius
263-
if iz == self.nbinz-1:
264-
zhi = self.zbounds[1] - radius
255+
if iz == 0 : zlo = zlo+radius
256+
if iz == self.nbinz-1 : zhi = self.zbounds[1] - radius
265257
z = self.Rng.uniform(zlo, zhi)
266258

267259
return x, y, z
268260

269261

270262
## \brief Returns material at specified location
271263
#
264+
# Distance calculation used with periodic boundaries uses the minimum image convention
265+
#
272266
# \returns material type of location
273267
def _samplePointNoGrid(self):
274-
distances = np.sqrt( (self.Centers[:,0]-self.Part.x)**2 + (self.Centers[:,1]-self.Part.y)**2 + (self.Centers[:,2]-self.Part.z)**2 )
268+
dx = self.Centers[:,0]-self.Part.x
269+
if self.xperiodic: dx = dx - np.int32(2*dx/self.lx)*self.lx
270+
271+
dy = self.Centers[:,1]-self.Part.y
272+
if self.yperiodic: dy = dy - np.int32(2*dy/self.ly)*self.ly
273+
274+
dz = self.Centers[:,2]-self.Part.z
275+
if self.zperiodic: dz = dz - np.int32(2*dz/self.lz)*self.lz
276+
277+
distances = np.sqrt(dx**2 + dy **2 + dz**2)
275278
overlapIndices = np.where(self.Radii > distances)[0]
276279
if len(overlapIndices) > 0: self.CurrentMatInd = self.MatInds[overlapIndices[0]]
277280
else : self.CurrentMatInd = self.matMatrix
@@ -287,7 +290,7 @@ def _samplePointGrid(self):
287290

288291
## \brief Adds sphere to bins that it could potentially overlap
289292
#
290-
# This is specific to FastRSA, potentially taking advantage of the case where radius is constant
293+
# This is specific to FastRSA, taking advantage of the radius being constant
291294
# by adding the sphere to a single bin.
292295
#
293296
# \param[in] x, y, x, float; coordinates of sphere center
@@ -300,7 +303,8 @@ def _addSphereToBinsFastRSA(self, x, y, z, rad, index):
300303
iz = int((z - self.zbounds[0]) / self.gridSize)
301304
gridIndex = self.cellCoordsToIndex[(ix,iy,iz)]
302305
self.grid[ix, iy, iz] = [[x, y, z, rad, index]]
303-
self.emptyCellList = np.delete(self.emptyCellList, self.emptyCellList == gridIndex)
306+
self.emptyCellList = np.delete(self.emptyCellList, self.emptyCellList == gridIndex)
307+
304308

305309
## \brief Adds sphere to bins that it could potentially overlap
306310
#
@@ -326,20 +330,25 @@ def _addSphereToBinsGrid(self, x0, y0, z0, rad, index):
326330
z = z0 - self.zbounds[0]
327331
maxOverlapDistance = rad+self.radMax
328332

329-
#Bin extents in x
333+
#Sphere is added to all grid cells where overlaps need to be checked.
334+
#ixlo/ixhi, iylo/iyhi, izlo/izhi are the extents of the grid subdomain,
335+
#as grid indices. Negative values of ixlo,iylo,izlo will be used to wrap
336+
#around periodic boundaries, if applicable
337+
338+
#Grid subdomain extents in x
330339
ixlo = int((x - maxOverlapDistance)/self.gridSize)-1
331-
if x < maxOverlapDistance:
332-
ixlo = -1
340+
if x < maxOverlapDistance: #Needed due to potential truncation of grid cell at upper domain boundary
341+
ixlo -= 1
333342
ixhi = int((x + maxOverlapDistance)/self.gridSize)+1
334343
if ixlo < 0 and not self.xperiodic:
335344
ixlo = 0
336345
if ixhi >= self.nbinx and not self.xperiodic:
337346
ixhi = self.nbinx-1
338347

339-
#Bin extents in y
348+
#Grid subdomain extens in y
340349
iylo = int((y - maxOverlapDistance)/self.gridSize)-1
341350
if y < maxOverlapDistance:
342-
iylo = -1
351+
iylo -= 1
343352
iyhi = int((y + maxOverlapDistance)/self.gridSize)+1
344353
if iylo < 0 and not self.yperiodic:
345354
iylo = 0
@@ -349,7 +358,7 @@ def _addSphereToBinsGrid(self, x0, y0, z0, rad, index):
349358
#Bin extents in z
350359
izlo = int((z - maxOverlapDistance)/self.gridSize)-1
351360
if z < maxOverlapDistance:
352-
izlo = -1
361+
izlo -= 1
353362
izhi = int((z + maxOverlapDistance)/self.gridSize)+1
354363
if izlo < 0 and not self.zperiodic:
355364
izlo = 0
@@ -367,6 +376,7 @@ def _addSphereToBinsGrid(self, x0, y0, z0, rad, index):
367376
yc = (iiy + 0.5)*self.gridSize
368377
zc = (iiz + 0.5)*self.gridSize
369378
if (xs-xc)*(xs-xc) + (ys-yc)*(ys-yc) + (zs-zc)*(zs-zc) < (maxOverlapDistance + self.bindiag)*(maxOverlapDistance + self.bindiag):
379+
#If sphere is within relevant distance of grid cell center, add sphere to grid cell
370380
if self.grid[iix, iiy, iiz] is None:
371381
self.grid[iix, iiy, iiz] = []
372382
self.grid[iix, iiy, iiz].append([xs + self.xbounds[0],
@@ -380,14 +390,24 @@ def _addSphereToBinsGrid(self, x0, y0, z0, rad, index):
380390
## \brief Check if a given sphere overlaps any existing spheres
381391
#
382392
# Checks all possible pairs of spheres, hence scaling is N^2
393+
# Distance calculation used with periodic boundaries uses the minimum image convention
383394
#
384395
# \param[in] x, y, x, float; coordinates of sphere center
385396
# \param[in] rad, float: sphere radius
386397
# \returns Index of sphere that is overlapped; -1 if no overlap
387398
def _checkSphereOverlapNoGrid(self, x, y, z, rad):
388399
if len(self.Centers) == 0: return -1
389400
centers = np.array(self.Centers)
390-
distances = np.sqrt( (centers[:,0]-x)**2 + (centers[:,1]-y)**2 + (centers[:,2]-z)**2 )
401+
dx = centers[:,0]-x
402+
if self.xperiodic: dx = dx - np.int32(2*dx/self.lx)*self.lx
403+
404+
dy = centers[:,1]-y
405+
if self.yperiodic: dy = dy - np.int32(2*dy/self.ly)*self.ly
406+
407+
dz = centers[:,2]-z
408+
if self.zperiodic: dz = dz - np.int32(2*dz/self.lz)*self.lz
409+
410+
distances = np.sqrt(dx**2 + dy**2 + dz**2)
391411
overlapIndices = distances < self.Radii + rad
392412
if np.any(overlapIndices): return np.where(overlapIndices)[0][0]
393413
else : return -1
@@ -399,48 +419,58 @@ def _checkSphereOverlapNoGrid(self, x, y, z, rad):
399419
# to the fast RSA method, where only the nearest neighboring bins need to be
400420
# checked.
401421
#
402-
# \param[in] x, y, z, float; coordinates of sphere center
422+
# The sphere is checked against all grid cells where overlaps could occur.
423+
# Variables ixlo/ixhi, iylo/iyhi, and izlo/izhi are the extents of the grid
424+
# subdomain where overlap checking is relevant, as grid indices.
425+
# Negative values of ixlo, iylo, and izlo will be used to wrap around
426+
# periodic boundaries, if applicable.
427+
#
428+
# \param[in] x0, y0, z0, float; coordinates of sphere
403429
# \param[in] rad, float: sphere radius
404430
# \returns Index of sphere that is overlapped; -1 if no overlap
405-
def _checkSphereOverlapGridFastRSA(self, x, y, z, rad):
406-
407-
ix = int((x - self.xbounds[0])//self.gridSize)
408-
iy = int((y - self.ybounds[0])//self.gridSize)
409-
iz = int((z - self.zbounds[0])//self.gridSize)
431+
def _checkSphereOverlapGridFastRSA(self, x0, y0, z0, rad):
432+
x = x0 - self.xbounds[0]
433+
y = y0 - self.ybounds[0]
434+
z = z0 - self.zbounds[0]
435+
436+
ix = int(x/self.gridSize)
437+
iy = int(y/self.gridSize)
438+
iz = int(z/self.gridSize)
439+
410440

411441
ixlo = ix - self.fastRSANumGridCheck
412-
ixhi = ix + self.fastRSANumGridCheck
413-
if ixlo < 0 and not self.xperiodic:
414-
ixlo = 0
415-
if ixhi >= self.nbinx and not self.xperiodic:
416-
ixhi = self.nbinx-1
442+
ixhi = ix + self.fastRSANumGridCheck
443+
if self.xperiodic: ixlo -= 1
444+
else :
445+
if ixlo < 0 : ixlo = 0
446+
if ixhi >= self.nbinx: ixhi = self.nbinx-1
417447

418448
iylo = iy - self.fastRSANumGridCheck
419449
iyhi = iy + self.fastRSANumGridCheck
420-
if iylo < 0 and not self.yperiodic:
421-
iylo = 0
422-
if iyhi >= self.nbiny and not self.yperiodic:
423-
iyhi = self.nbiny-1
450+
if self.yperiodic: iylo -= 1
451+
else :
452+
if iylo < 0 : iylo = 0
453+
if iyhi >= self.nbiny: iyhi = self.nbiny-1
424454

425455
izlo = iz - self.fastRSANumGridCheck
426456
izhi = iz + self.fastRSANumGridCheck
427-
if izlo < 0 and not self.zperiodic:
428-
izlo = 0
429-
if izhi >= self.nbinz and not self.zperiodic:
430-
izhi = self.nbinz-1
457+
if self.zperiodic: izlo -= 1
458+
else :
459+
if izlo < 0 : izlo = 0
460+
if izhi >= self.nbinz: izhi = self.nbinz-1
431461

432462
for ix in np.arange(ixlo, ixhi+1):
433-
iix, xs = self._wrap_bins(ix, x, self.nbinx, self.lx, self.xperiodic)
463+
iix, xs = self._wrap_bins(ix, x0, self.nbinx, self.lx, self.xperiodic)
434464
for iy in np.arange(iylo, iyhi+1):
435-
iiy, ys = self._wrap_bins(iy, y, self.nbiny, self.ly, self.yperiodic)
465+
iiy, ys = self._wrap_bins(iy, y0, self.nbiny, self.ly, self.yperiodic)
436466
for iz in np.arange(izlo, izhi+1):
437-
iiz, zs = self._wrap_bins(iz, z, self.nbinz, self.lz, self.zperiodic)
438-
bin = self.grid[iix, iiy, iiz]
467+
iiz, zs = self._wrap_bins(iz, z0, self.nbinz, self.lz, self.zperiodic)
468+
bin = self.grid[iix, iiy, iiz]
439469
if bin is None:
440470
continue
441471
for particle in bin:
442472
px, py, pz, prad, index = particle
443-
rx = xs - px
473+
rx = xs - px
444474
ry = ys - py
445475
rz = zs - pz
446476
r2 = np.sqrt(rx*rx + ry*ry + rz*rz)
@@ -468,23 +498,16 @@ def _checkSphereOverlapGrid(self, x, y, z, rad):
468498
for particle in bin:
469499
px, py, pz, prad, index = particle
470500
rx = x - px
471-
if (self.xperiodic):
472-
rx = rx - int(rx/self.lx)*self.lx
473501
ry = y - py
474-
if (self.yperiodic):
475-
ry = ry - int(ry/self.ly)*self.ly
476-
rz = z - pz
477-
if (self.zperiodic):
478-
rz = rz - int(rz/self.lz)*self.lz
479-
502+
rz = z - pz
480503
r2 = np.sqrt(rx*rx + ry*ry + rz*rz)
481504
if r2 < rad + prad:
482505
return index
483506
return -1
484507

485508
## \brief Wrap bin index and sphere coordinates around periodic boundaries
486509
#
487-
# Helper function for wrapping bins and sphere coordinates around PBCs
510+
# Helper function for wrapping bins and sphere coordinates around periodic boundaries
488511
#
489512
# \param[in] ibin, int; index of bin along relevant direction
490513
# \param[in] coord, float; coordinate of sphere along relevant direction
@@ -498,7 +521,7 @@ def _wrap_bins(self, ibin, coord, nbins, length, periodic):
498521
if ibin < 0:
499522
wbin = np.max((nbins + ibin, 0))
500523
wcoord = coord + length
501-
if ibin >= nbins:
524+
elif ibin >= nbins:
502525
wbin = np.min((ibin - nbins, nbins-1))
503526
wcoord = coord - length
504527
else:
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
,1a-Suite1-CoPS3PO,1b-Suite1-CoPS3PO,1c-Suite1-CoPS3PO,2a-Suite1-CoPS3PO,2b-Suite1-CoPS3PO,2c-Suite1-CoPS3PO,3a-Suite1-CoPS3PO,3b-Suite1-CoPS3PO,3c-Suite1-CoPS3PO
1+
,1a-Suite1-CoPS3PO,1b-Suite1-CoPS3PO,1c-Suite1-CoPS3PO,2a-Suite1-CoPS3PO,2b-Suite1-CoPS3PO,2c-Suite1-CoPS3PO,3a-Suite1-CoPS3PO,3b-Suite1-CoPS3PO,3c-Suite1-CoPS3PO
22
Refl,0.4368,0.0849,0.4775,0.2375,0.2865,0.4332,0.6909,0.0363,0.4447
3-
uncRefl,0.0005,0.0003,0.0005,0.0004,0.0005,0.0005,0.0005,0.0002,0.0005
3+
Refl_unc,0.0005,0.0003,0.0005,0.0004,0.0005,0.0005,0.0005,0.0002,0.0005
44
Trans,0.0149,0.00163,0.0159,0.0983,0.1958,0.187,0.1643,0.0763,0.1039
5-
uncTrans,0.0001,0.00004,0.0001,0.0003,0.0004,0.0004,0.00004,0.0003,0.0003
5+
Trans_unc,0.0001,0.00004,0.0001,0.0003,0.0004,0.0004,0.00004,0.0003,0.0003
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
,1a-Suite1-CoPS3PO,1b-Suite1-CoPS3PO,1c-Suite1-CoPS3PO,2a-Suite1-CoPS3PO,2b-Suite1-CoPS3PO,2c-Suite1-CoPS3PO,3a-Suite1-CoPS3PO,3b-Suite1-CoPS3PO,3c-Suite1-CoPS3PO
1+
,1a-Suite1-CoPS3PO,1b-Suite1-CoPS3PO,1c-Suite1-CoPS3PO,2a-Suite1-CoPS3PO,2b-Suite1-CoPS3PO,2c-Suite1-CoPS3PO,3a-Suite1-CoPS3PO,3b-Suite1-CoPS3PO,3c-Suite1-CoPS3PO
22
Refl,0.4368,0.0849,0.4775,0.2375,0.2865,0.4332,0.6909,0.0363,0.4447
3-
uncRefl,0.0005,0.0003,0.0005,0.0004,0.0005,0.0005,0.0005,0.0002,0.0005
3+
Refl_unc,0.0005,0.0003,0.0005,0.0004,0.0005,0.0005,0.0005,0.0002,0.0005
44
Trans,0.0149,0.00163,0.0159,0.0983,0.1958,0.187,0.1643,0.0763,0.1039
5-
uncTrans,0.0001,0.00004,0.0001,0.0003,0.0004,0.0004,0.00004,0.0003,0.0003
5+
Trans_unc,0.0001,0.00004,0.0001,0.0003,0.0004,0.0004,0.00004,0.0003,0.0003

Examples/3D/3DBruteForceSMTransportDriver-SphericalInclusions.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
raddist = 'Constant' #'Constant', 'Uniform', 'Exponential'
2525
Geomsize = 10.0
2626
case = '2' #'1','2','3'; case number from BrantleyMC2011 and BrantleyANS2014
27-
volfrac = '0.05' #'0.05','0.10','0.15','0.20','0.25','0.30'; volume fraction of spheres as a string
28-
sphSampleModel= 'NoGrid' #'NoGrid', 'GenericGrid', or 'FastRSA'
27+
volfrac = '0.20' #'0.05','0.10','0.15','0.20','0.25','0.30'; volume fraction of spheres as a string
28+
sphSampleModel= 'FastRSA' #'NoGrid', 'GenericGrid', or 'FastRSA'
2929
abundanceModel= 'ensemble' #'ensemble' or 'sample'
3030
numtalbins = 20
3131

@@ -57,7 +57,9 @@
5757
Geom.defineGeometryBoundaries(xbounds=[-Geomsize/2,Geomsize/2],ybounds=[-Geomsize/2,Geomsize/2],zbounds=[-Geomsize/2,Geomsize/2])
5858
Geom.defineBoundaryConditions(xBCs=['reflective','reflective'],yBCs=['reflective','reflective'],zBCs=['vacuum','vacuum'])
5959
Geom.defineCrossSections(totxs=CaseInp.Sigt[:],scatxs=CaseInp.Sigs[:])
60-
Geom.defineMixingParams(sphereFrac=CaseInp.sphereFrac,radMin=CaseInp.radMin,radAve=CaseInp.radAve,radMax=CaseInp.radMax,sizeDistribution=CaseInp.sizeDistribution,matSphProbs=CaseInp.matSphProbs,matMatrix=CaseInp.matMatrix,xperiodic=False,yperiodic=False,zperiodic=False)
60+
Geom.defineMixingParams(sphereFrac=CaseInp.sphereFrac,radMin=CaseInp.radMin,radAve=CaseInp.radAve,radMax=CaseInp.radMax,
61+
sizeDistribution=CaseInp.sizeDistribution,matSphProbs=CaseInp.matSphProbs,
62+
matMatrix=CaseInp.matMatrix,xperiodic=False,yperiodic=False,zperiodic=False)
6163
Geom.defineGeometryGenerationParams(maxPlacementAttempts=10000, sphSampleModel=sphSampleModel, gridSize=None)
6264

6365
#Instantiate and associate the general Monte Carlo particle solver

0 commit comments

Comments
 (0)