
Percolation Simulation

This is a simulation of aspects of mathematical percolation theory.

For other percolation simulations, see Category:Percolation Simulations, or:
1D finite grid simulation
Mean run density 
2D finite grid simulations
Site percolation | Bond percolation | Mean cluster density

Given an 



M
×
N


{\displaystyle M\times N}

 rectangular array of cells numbered 




c
e
l
l

[
0..
M
−
1
,
0..
N
−
1
]


{\displaystyle \mathrm {cell} [0..M-1,0..N-1]}

assume 



M


{\displaystyle M}

 is horizontal and 



N


{\displaystyle N}

 is downwards.

Assume that the probability of any cell being filled is a constant 



p


{\displaystyle p}

 where

 



0.0
≤
p
≤
1.0


{\displaystyle 0.0\leq p\leq 1.0}


The task

Simulate creating the array of cells with probability 



p


{\displaystyle p}

 and then
testing if there is a route through adjacent filled cells from any on row 



0


{\displaystyle 0}

 to any on row 



N


{\displaystyle N}

, i.e. testing for site percolation.

Given 



p


{\displaystyle p}

 repeat the percolation 



t


{\displaystyle t}

 times to estimate the proportion of times that the fluid can percolate to the bottom for any given 



p


{\displaystyle p}

.

Show how the probability of percolating through the random grid changes with 



p


{\displaystyle p}

 going from 



0.0


{\displaystyle 0.0}

 to 



1.0


{\displaystyle 1.0}

 in 



0.1


{\displaystyle 0.1}

 increments and with the number of repetitions to estimate the fraction at any given 



p


{\displaystyle p}

 as 



t
>=
100


{\displaystyle t>=100}

.

Use an 



M
=
15
,
N
=
15


{\displaystyle M=15,N=15}

 grid of cells for all cases.

Optionally depict a percolation through a cell grid graphically.

Show all output on this page.

package main
 
import (
	"bytes"
	"fmt"
	"math/rand"
	"time"
)
 
func main() {
	const (
		m, n           = 15, 15
		t              = 1e4
		minp, maxp, Δp = 0, 1, 0.1
	)
 
	rand.Seed(2) // Fixed seed for repeatable example grid
	g := NewGrid(.5, m, n)
	g.Percolate()
	fmt.Println(g)
 
	rand.Seed(time.Now().UnixNano()) // could pick a better seed
	for p := float64(minp); p < maxp; p += Δp {
		count := 0
		for i := 0; i < t; i++ {
			g := NewGrid(p, m, n)
			if g.Percolate() {
				count++
			}
		}
		fmt.Printf("p=%.2f, %.4f\n", p, float64(count)/t)
	}
}
 
const (
	full  = '.'
	used  = '#'
	empty = ' '
)
 
type grid struct {
	cell [][]byte // row first, i.e. [y][x]
}
 
func NewGrid(p float64, xsize, ysize int) *grid {
	g := &grid{cell: make([][]byte, ysize)}
	for y := range g.cell {
		g.cell[y] = make([]byte, xsize)
		for x := range g.cell[y] {
			if rand.Float64() < p {
				g.cell[y][x] = full
			} else {
				g.cell[y][x] = empty
			}
		}
	}
	return g
}
 
func (g *grid) String() string {
	var buf bytes.Buffer
	// Don't really need to call Grow but it helps avoid multiple
	// reallocations if the size is large.
	buf.Grow((len(g.cell) + 2) * (len(g.cell[0]) + 3))
 
	buf.WriteByte('+')
	for _ = range g.cell[0] {
		buf.WriteByte('-')
	}
	buf.WriteString("+\n")
 
	for y := range g.cell {
		buf.WriteByte('|')
		buf.Write(g.cell[y])
		buf.WriteString("|\n")
	}
 
	buf.WriteByte('+')
	ly := len(g.cell) - 1
	for x := range g.cell[ly] {
		if g.cell[ly][x] == used {
			buf.WriteByte(used)
		} else {
			buf.WriteByte('-')
		}
	}
	buf.WriteByte('+')
	return buf.String()
}
 
func (g *grid) Percolate() bool {
	for x := range g.cell[0] {
		if g.use(x, 0) {
			return true
		}
	}
	return false
}
 
func (g *grid) use(x, y int) bool {
	if y < 0 || x < 0 || x >= len(g.cell[0]) || g.cell[y][x] != full {
		return false // Off the edges, empty, or used
	}
	g.cell[y][x] = used
	if y+1 == len(g.cell) {
		return true // We're on the bottom
	}
 
	// Try down, right, left, up in that order.
	return g.use(x, y+1) ||
		g.use(x+1, y) ||
		g.use(x-1, y) ||
		g.use(x, y-1)
}