Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/complete grains at shape edge #335

Merged
merged 7 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 26 additions & 20 deletions engine/ext_make3dgrains.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import (
"math/rand"
)

var GrainCutShape = false // complete all voronoi grains whose centre lies within the shape. Warning, this also cuts away parts of the shape whose closest voronoi centre lies outside the shape

func init() {
DeclFunc("ext_make3dgrains", Voronoi3d, "3D Voronoi tesselation over shape (grain size, starting region number, num regions, shape, seed)")
DeclVar("GrainCutShape", &GrainCutShape, "Adds the complete voronoi grain, if its centre lies within the shape (default=false)")
}

func Voronoi3d(grainsize float64, startRegion int, numRegions int, inputShape Shape, seed int) {
Expand Down Expand Up @@ -54,9 +57,9 @@ func newTesselation3d(grainsize float64, nRegion int, seed int64, startRegion in

// Permutes the slice of cell locations. I don't understand why this needs to be done if we're choosing
// random (Intn()) cells out of the slice of cell locations, but hey, it seems to do the trick.
func shuffleCells(src []cellLocs) []cellLocs {
func (t *tesselation3d) shuffleCells(src []cellLocs) []cellLocs {
dest := make([]cellLocs, len(src))
perm := rand.Perm(len(src))
perm := t.rnd.Perm(len(src))
for i, v := range perm {
dest[v] = src[i]
}
Expand All @@ -66,7 +69,7 @@ func shuffleCells(src []cellLocs) []cellLocs {
func (t *tesselation3d) makeRandomCenters() {
//Make a list of all the cells in the shape.
cells := t.tabulateCells()
cells = shuffleCells(cells)
cells = t.shuffleCells(cells)

//Choose number of grains to make. Assume volume of grain is given by (4/3)*pi*r^3
shapeVolume := cellVolume() * float64(len(cells))
Expand All @@ -84,8 +87,6 @@ func (t *tesselation3d) makeRandomCenters() {
randRegion := t.startRegion + t.rnd.Intn(t.maxRegion)
t.centers[p].region = byte(randRegion)
}

return
}

// Creates a slice of all cells which fall in the shape specified in the constructor.
Expand All @@ -107,7 +108,7 @@ func (t *tesselation3d) tabulateCells() []cellLocs {
y := cell.Y()
z := cell.Z()

if t.shape(x, y, z) {
if t.shape(x, y, z) || GrainCutShape {
cells = append(cells, cellLocs{x, y, z})
}
}
Expand All @@ -120,23 +121,29 @@ func (t *tesselation3d) tabulateCells() []cellLocs {
return cells
}

// Find the nearest Voronoi center to the point (x, y, z). Only points inside the given shape will be
// assigned a region.
func (t *tesselation3d) RegionOf(x, y, z float64) int {
if t.shape(x, y, z) {
nearest := center3d{x, y, z, 0}
mindist := math.Inf(1)
for _, c := range t.centers {
dist := sqr(x-c.x) + sqr(y-c.y) + sqr(z-c.z)
if dist < mindist {
nearest = c
mindist = dist
}
// Check if the point is within the shape or if we're cutting the shape along the grains
if !(t.shape(x, y, z) || GrainCutShape) {
return -1 // Regions < 0 won't be rastered
}

// Find the nearest center point to the (x, y, z) position
nearest := center3d{x, y, z, 0}
mindist := math.Inf(1)
for _, c := range t.centers {
dist := sqr(x-c.x) + sqr(y-c.y) + sqr(z-c.z)
if dist < mindist {
nearest = c
mindist = dist
}
}

// Check if the nearest point's region should be returned
if (t.shape(x, y, z) && !GrainCutShape) || (t.shape(nearest.x, nearest.y, nearest.z) && GrainCutShape) {
return int(nearest.region)
} else {
return -1 //When the regions are rendered, any region < 0 will not be rastered.
}

return -1
}

// Generate normally distributed numbers; mean = lambda, variance = lambda. If generated number < 0, return 1.
Expand All @@ -149,5 +156,4 @@ func (t *tesselation3d) truncNorm(lambda float64) int {
} else {
return int(ret + 0.5)
}

}
32 changes: 32 additions & 0 deletions test/make3dgrains_shape_edge.mx3
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
N:=32
SetMesh(N, N, N, 1e-9,1e-9,1e-9, 0, 0, 0)

//set material params
alpha=1.
Msat=350e3
Aex=10e-12
Ku1=1.0e6

//define a spherical shape
diam:=16e-9
sphere := Ellipsoid(diam,diam,diam)
defregion(1, sphere)

seed:=238948790
randSeed(seed)
GrainCutShape=true
ext_make3Dgrains(8e-9, 2, 250, sphere, seed)

for i:=2; i<254; i+=1 {
anisU.setregion(i, vector(randNorm(),randNorm(),randNorm()))
}


//a cell in the sphere for which the nearest voronoi centre also lies in the sphere: nonzero region nr expected
expect("region", regions.getcell(15,15,15), 191, 0)

//a cell in the sphere for which the nearest voronoi centre lies outside the sphere: region nr 1 expected as it is not affected by make3Dgrains
expect("region", regions.getcell(9,15,15), 1, 0)

//a cell outside of the sphere for which the nearest voronoi centre lies inside the sphere: nonzero region nr expected as it was completed by make3Dgrains
expect("region", regions.getcell(16,4,15), 64, 0)