From 12ba989621c5356aa9019714b674436d860942e2 Mon Sep 17 00:00:00 2001 From: Ross Knapman Date: Sat, 23 Jan 2021 14:42:54 +0100 Subject: [PATCH 1/3] Clear region if previously defined Say we have a thin film where m = Uniform(0, 0, 1), and we define a circular region 1 with m = Uniform (0, 0, -1). Let's say we now want region 1 to be centred at some new coordinates (x', y'). We "reset" the system by calling m = Uniform(0, 0, 1) again, then redefine the region by again calling DefRegion(1, Circle(...).Transl(...)), and set m to be Uniform (0, 0, -1) in the region. Currently, both circles at (x, y) and (x', y') will be seen in the output, as region 1 wasn't reset back to zero everywhere. This commit defines a new function clear(), and calls it when DefRegion(id, shape) is called. I ran into this problem when I was running a simulation in which I was using a for loop to loop through various positions of a specific texture, and output the energy of each configuration. --- engine/regions.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/engine/regions.go b/engine/regions.go index 7ccf8f791..13e1b35bf 100644 --- a/engine/regions.go +++ b/engine/regions.go @@ -42,6 +42,8 @@ func (r *Regions) resize() { // Define a region with id (0-255) to be inside the Shape. func DefRegion(id int, s Shape) { defRegionId(id) + regions.clear(id) + f := func(x, y, z float64) int { if s(x, y, z) { return id @@ -60,13 +62,14 @@ func (r *Regions) render(f func(x, y, z float64) int) { l := r.HostList() // need to start from previous state arr := reshapeBytes(l, r.Mesh().Size()) + // Loop through the system, getting the region of each cell for iz := 0; iz < n[Z]; iz++ { for iy := 0; iy < n[Y]; iy++ { for ix := 0; ix < n[X]; ix++ { r := Index2Coord(ix, iy, iz) - region := f(r[X], r[Y], r[Z]) + region := f(r[X], r[Y], r[Z]) // f is a function that returns the region, given the cell coordinates if region >= 0 { - arr[iz][iy][ix] = byte(region) + arr[iz][iy][ix] = byte(region) // Here, we get the region of the cell } } } @@ -75,6 +78,25 @@ func (r *Regions) render(f func(x, y, z float64) int) { r.gpuCache.Upload(l) } + +func (r *Regions) clear(id int) { + n := Mesh().Size() + l := r.HostList() // need to start from previous state + arr := reshapeBytes(l, r.Mesh().Size()) + + // Loop through the system, getting the region of each cell + for iz := 0; iz < n[Z]; iz++ { + for iy := 0; iy < n[Y]; iy++ { + for ix := 0; ix < n[X]; ix++ { + if arr[iz][iy][ix] == byte(id) { + arr[iz][iy][ix] = byte(0) + } + } + } + } + r.gpuCache.Upload(l) +} + // get the region for position R based on the history func (r *Regions) get(R data.Vector) int { // reverse order, last one set wins. From fa46a9c2dad874bd9320092d0400710a568687ee Mon Sep 17 00:00:00 2001 From: Ross Knapman Date: Mon, 10 May 2021 20:00:14 +0200 Subject: [PATCH 2/3] Rework clear region functionality as redefining the region index instead --- engine/regions.go | 27 ++++++++++++++---------- test/redefregion.mx3 | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 test/redefregion.mx3 diff --git a/engine/regions.go b/engine/regions.go index 13e1b35bf..0d82e1db9 100644 --- a/engine/regions.go +++ b/engine/regions.go @@ -12,6 +12,7 @@ const NREGION = 256 // maximum number of regions, limited by size of byte. func init() { DeclFunc("DefRegion", DefRegion, "Define a material region with given index (0-255) and shape") + DeclFunc("RedefRegion", RedefRegion, "Reassign all cells with a given region (first argument) to a new region (second argument)") DeclROnly("regions", ®ions, "Outputs the region index for each cell") DeclFunc("DefRegionCell", DefRegionCell, "Set a material region (first argument) in one cell "+ "by the index of the cell (last three arguments)") @@ -42,8 +43,6 @@ func (r *Regions) resize() { // Define a region with id (0-255) to be inside the Shape. func DefRegion(id int, s Shape) { defRegionId(id) - regions.clear(id) - f := func(x, y, z float64) int { if s(x, y, z) { return id @@ -55,6 +54,14 @@ func DefRegion(id int, s Shape) { regions.hist = append(regions.hist, f) } +// Redefine a region with a given ID to a new ID +func RedefRegion(startId, endId int) { + // Checks validity of input region IDs + defRegionId(startId) + defRegionId(endId) + regions.redefine(startId, endId) +} + // renders (rasterizes) shape, filling it with region number #id, between x1 and x2 // TODO: a tidbit expensive func (r *Regions) render(f func(x, y, z float64) int) { @@ -62,14 +69,13 @@ func (r *Regions) render(f func(x, y, z float64) int) { l := r.HostList() // need to start from previous state arr := reshapeBytes(l, r.Mesh().Size()) - // Loop through the system, getting the region of each cell for iz := 0; iz < n[Z]; iz++ { for iy := 0; iy < n[Y]; iy++ { for ix := 0; ix < n[X]; ix++ { r := Index2Coord(ix, iy, iz) - region := f(r[X], r[Y], r[Z]) // f is a function that returns the region, given the cell coordinates + region := f(r[X], r[Y], r[Z]) if region >= 0 { - arr[iz][iy][ix] = byte(region) // Here, we get the region of the cell + arr[iz][iy][ix] = byte(region) } } } @@ -78,18 +84,17 @@ func (r *Regions) render(f func(x, y, z float64) int) { r.gpuCache.Upload(l) } - -func (r *Regions) clear(id int) { +func (r *Regions) redefine(startId, endId int) { + // Loop through all cells, if their region ID matches startId, change it to endId n := Mesh().Size() l := r.HostList() // need to start from previous state arr := reshapeBytes(l, r.Mesh().Size()) - // Loop through the system, getting the region of each cell for iz := 0; iz < n[Z]; iz++ { for iy := 0; iy < n[Y]; iy++ { for ix := 0; ix < n[X]; ix++ { - if arr[iz][iy][ix] == byte(id) { - arr[iz][iy][ix] = byte(0) + if arr[iz][iy][ix] == byte(startId) { + arr[iz][iy][ix] = byte(endId) } } } @@ -290,4 +295,4 @@ func (r *Regions) Mesh() *data.Mesh { return Mesh() } func prod(s [3]int) int { return s[0] * s[1] * s[2] -} +} \ No newline at end of file diff --git a/test/redefregion.mx3 b/test/redefregion.mx3 new file mode 100644 index 000000000..7c99d096a --- /dev/null +++ b/test/redefregion.mx3 @@ -0,0 +1,50 @@ +/* + Test the RedefRegion function. +*/ + +Nx := 128 +Ny := 64 +Nz := 1 +c := 1e-9 + +SetGridSize(Nx, Ny, Nz) +SetCellSize(c, c, c) + +m = Uniform(0, 0, 1) + +// Assign the left-hand half of the sample region id 1, and set the magnetization within it along +x +DefRegion(1, Rect(Nx*c/2, Ny*c).Transl(-Nx*c/4, 0, 0)) +m.SetRegion(1, Uniform(1, 0, 0)) + +// Left-hand-side region id reset to zero +RedefRegion(1, 0) + +// "Reset" the system to its initial state +m = Uniform(0, 0, 1) + +// Now the right-hand-side of the system ONLY should have id 1 +DefRegion(1, Rect(Nx*c/2, Ny*c).Transl(Nx*c/4, 0, 0)) +m.SetRegion(1, Uniform(1, 0, 0)) + +// Ensure that RHS now has id 1 and LHS is back to zero +Expect("Region", regions.GetCell(Nx/4, 0, 0), 0, 0.1) +Expect("Region", regions.GetCell(3*Nx/4, 0, 0), 1, 0.1) +ExpectV("m", m.GetCell(Nx/4, Ny/2, 0), Vector(0, 0, 1), 1e-5) +ExpectV("m", m.GetCell(3*Nx/4, Ny/2, 0), Vector(1, 0, 0), 1e-5) + +// Reset the system to initial state, then set the (now undefined) region 1 to have m along +y, and ensure that this does not affect average magnetization +RedefRegion(1, 0) +m = Uniform(0, 0, 1) +m.SetRegion(1, Uniform(0, 1, 0)) +Expect("m", m.comp(2).average(), 1, 1e-5) + +// Test with several regions: start with different magnetizations; add first region to second, set them both to a single direction, and check both regions now have this +RedefRegion(1, 0) +DefRegion(1, Rect(Nx*c/2, Ny*c/2).Transl(Nx*c/4, Ny*c/4, 0)) +m.SetRegion(1, Uniform(0, 1, 0)) +DefRegion(2, Rect(Nx*c/2, Ny*c/2).Transl(Nx*c/4, -Ny*c/4, 0)) +m.SetRegion(2, Uniform(0, -1, 0)) +RedefRegion(1, 2) +m.SetRegion(2, Uniform(1, 0, 0)) +ExpectV("m", m.GetCell(3*Nx/4, 3*Ny/4, 0), Vector(1, 0, 0), 1e-5) +ExpectV("m", m.GetCell(3*Nx/4, Ny/4, 0), Vector(1, 0, 0), 1e-5) From b806f7dcc6da276aea058ab3a409dcf584a05e26 Mon Sep 17 00:00:00 2001 From: Jonathan Maes Date: Fri, 27 Sep 2024 18:01:35 +0200 Subject: [PATCH 3/3] regions.hist now tracks effect of RedefRegion --- engine/regions.go | 21 ++++++++++++++++++++- test/redefregion.mx3 | 14 ++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/engine/regions.go b/engine/regions.go index 0d82e1db9..a168da884 100644 --- a/engine/regions.go +++ b/engine/regions.go @@ -59,7 +59,26 @@ func RedefRegion(startId, endId int) { // Checks validity of input region IDs defRegionId(startId) defRegionId(endId) + + hist_len := len(regions.hist) // Only consider hist before this Redef to avoid recursion + f := func(x, y, z float64) int { + value := -1 + for i := hist_len - 1; i >= 0; i-- { + f_other := regions.hist[i] + region := f_other(x, y, z) + if region >= 0 { + value = region + break + } + } + if value == startId { + return endId + } else { + return value + } + } regions.redefine(startId, endId) + regions.hist = append(regions.hist, f) } // renders (rasterizes) shape, filling it with region number #id, between x1 and x2 @@ -295,4 +314,4 @@ func (r *Regions) Mesh() *data.Mesh { return Mesh() } func prod(s [3]int) int { return s[0] * s[1] * s[2] -} \ No newline at end of file +} diff --git a/test/redefregion.mx3 b/test/redefregion.mx3 index 7c99d096a..e4421125b 100644 --- a/test/redefregion.mx3 +++ b/test/redefregion.mx3 @@ -17,7 +17,9 @@ DefRegion(1, Rect(Nx*c/2, Ny*c).Transl(-Nx*c/4, 0, 0)) m.SetRegion(1, Uniform(1, 0, 0)) // Left-hand-side region id reset to zero +SnapshotAs(regions, "regions_1_before.png") RedefRegion(1, 0) +SnapshotAs(regions, "regions_1_redefined.png") // "Reset" the system to its initial state m = Uniform(0, 0, 1) @@ -33,18 +35,30 @@ ExpectV("m", m.GetCell(Nx/4, Ny/2, 0), Vector(0, 0, 1), 1e-5) ExpectV("m", m.GetCell(3*Nx/4, Ny/2, 0), Vector(1, 0, 0), 1e-5) // Reset the system to initial state, then set the (now undefined) region 1 to have m along +y, and ensure that this does not affect average magnetization +SnapshotAs(regions, "regions_2_before.png") RedefRegion(1, 0) +SnapshotAs(regions, "regions_2_redefined.png") m = Uniform(0, 0, 1) m.SetRegion(1, Uniform(0, 1, 0)) Expect("m", m.comp(2).average(), 1, 1e-5) // Test with several regions: start with different magnetizations; add first region to second, set them both to a single direction, and check both regions now have this RedefRegion(1, 0) +SnapshotAs(regions, "regions_2_redefinedagain.png") DefRegion(1, Rect(Nx*c/2, Ny*c/2).Transl(Nx*c/4, Ny*c/4, 0)) m.SetRegion(1, Uniform(0, 1, 0)) DefRegion(2, Rect(Nx*c/2, Ny*c/2).Transl(Nx*c/4, -Ny*c/4, 0)) m.SetRegion(2, Uniform(0, -1, 0)) +SnapshotAs(regions, "regions_3_before.png") RedefRegion(1, 2) +SnapshotAs(regions, "regions_3_redefined.png") m.SetRegion(2, Uniform(1, 0, 0)) ExpectV("m", m.GetCell(3*Nx/4, 3*Ny/4, 0), Vector(1, 0, 0), 1e-5) ExpectV("m", m.GetCell(3*Nx/4, Ny/4, 0), Vector(1, 0, 0), 1e-5) + +// Test with resized grid: start from previous situation. If regions.hist is not tracked correctly in the Go source, this will not give the correct result. +SetGridSize(2*Nx, 2*Ny, Nz) +SnapshotAs(regions, "regions_3_resized.png") +Expect("Region", regions.GetCell(3*Nx/4, Ny/2, 0), 0, 0.1) +Expect("Region", regions.GetCell(5*Nx/4, 3*Ny/2-1, 0), 2, 0.1) +Expect("Region", regions.GetCell(5*Nx/4, Ny/2, 0), 2, 0.1)