Solving a 2D diffusion problem with the Tensor package

Arjen Markus (15 december 2010) The program below solves a 2D diffusion problem using the Tensor package. It is not quite complete yet (the "wall" is done in the wrong way, but I was too lazy to do it correctly) but it does illustrate the potential of the package.

(Note to self: more experiments - a problem with spherical symmetry, one with non-linear boundary conditions and an N-body problem to explore the facilities that the Tensor package offers)


# diffusion.tcl --
#     Solve a two-dimensional diffusion problem:
#     - An initial condition consisting of 1.0 in four grid cells
#     - Zero concentration on the boundaries
#     - Constant diffusion
#
#     A small twist: there is an inner boundary that hinders
#     diffusion, though it is modelled too simply. I should
#     suppress the diffusion on the sides on the grid cells
#     rather than suppress the change in the grid cells.
#     Visually the effect is very similar.
#
package require Tk

lappend auto_path .
package require Tensor

# nextTime --
#     Compute the result for the next time
#
# Arguments:
#     None
#
# Side effects:
#     Tensor u updated (du is an auxiliary tensor)
#
proc nextTime {} {

    set ucoeff [expr {$::diffusion * $::deltt / (2.0*$::deltx**2)}]

    set end   [expr {$::number}]
    set endm1 [expr {$::number-1}]
    set endp1 [expr {$::number+1}]

    #
    # Bounds depend on the type of boundary condition - unfortunately
    #
    ::tensor::expr "du(i=1:$end,j=1:$end) = 4.0*u(i=1:$end,j=1:$end) - u(i=0:$endm1,j=1:$end) - u(i=2:$endp1,j=1:$end)
                                                                     - u(i=1:$end,j=0:$endm1) - u(i=1:$end,j=2:$endp1)"

    du *= tensor mask

    ::tensor::expr "u(i=1:$end,j=1:$end) = u(i=1:$end,j=1:$end) - $ucoeff * du(i=1:$end,j=1:$end)"

}

# setBoundaryConditions --
#     Set the boundary conditions - almost trivial in this case
#
# Arguments:
#     time           Time in the computation
#
# Side effects:
#     Tensor u is updated
#
proc setBoundaryConditions {time} {
    set end $::number

    u section [list 0             [list 0 $end]] = scalar 0.0
    u section [list $end          [list 0 $end]] = scalar 0.0
    u section [list [list 0 $end] 0            ] = scalar 0.0
    u section [list [list 0 $end] $end         ] = scalar 0.0
}

# initialisePlot --
#     Initialise the plot
#
# Arguments:
#     None
#
# Side effects:
#     Graph is updated
#
proc initialisePlot {} {

    set width  [.c cget -width]
    set height [.c cget -height]

    set wrect  [expr {$width  / ($::number+2)}]
    set hrect  [expr {$height / ($::number+2)}]

    for { set j 0 } { $j < $::number+1 } { incr j } {
        for { set i 0 } { $i < $::number+1 } { incr i } {
            set xl [expr { $i * $wrect }]
            set xr [expr { $xl + $wrect - 1}]
            set yu [expr { $j * $wrect }]
            set yd [expr { $yu + $wrect - 1}]

            set id [.c create rectangle $xl $yu $xr $yd -fill white]

            cell section [list $i $j] = scalar $id
        }
    }

    .c create text [expr {$width/2}] $height -text "Time: 0.0" -anchor s -tag time
}

# plotData --
#     Plot the result
#
# Arguments:
#     time           Time in the computation
#     values         List of values
#
# Side effects:
#     Graph is updated
#
proc plotData {time} {

    set colours [list darkblue blue  cyan green yellow orange red]
    set values  [list 0.0001   0.003 0.01 0.03  0.1    0.2    0.5]

    .c itemconfigure time -text "Time: [format "%5.2f" $time]"

    for { set j 0 } { $j < $::number+1 } { incr j } {
        for { set i 0 } { $i < $::number+1 } { incr i } {

            set id     [cell section [list $i $j]]
            set value  [u    section [list $i $j]]
            set active [mask section [list $i $j]]

            if { $active == 0.0 } {
                continue
            }
            foreach v $values c $colours {
                if { $value <= $v } {
                    break
                }
            }

            .c itemconfigure $id -fill $c
        }
    }

    after 100 {
        set go 1
    }
    vwait go
}

# main --
#     Create the tensors, store a few
#     computational paramters and then
#     run the time loop
#
set diffusion  0.1
set number    30
set length    10.0
set deltx      [expr {$length/$number}]
set deltt      0.001

set size       [expr {$number + 2}]

tensor::create u    -size [list $size $size] -type double
tensor::create du   -size [list $size $size] -type double

tensor::create cell -size [list $size $size] -type int
tensor::create mask -size [list $size $size] -type double

#
# Initial condition
#
u  = scalar 0.0
du = scalar 0.0

set mid [expr {$number/2}]
u section [list [list $mid [expr {$mid+1}]] [list $mid [expr {$mid+1}]]] = scalar 1.0

#
# Set several cells to inactive
#
mask = scalar 1.0

set inactiveRow [expr {$mid + 4}]
mask section [list $inactiveRow [list [expr {$mid - 4}] [expr {$mid + 4}]]] = scalar 0.0

#
# Loop over time
#
pack [canvas .c -bg white -width 500 -height 500]

tkwait visibility .c

initialisePlot

set t 0
while { $t < 100000} {

    set time [expr {$deltt * $t}]

    if { $t % 200 == 0 } {
        plotData $time
    }

    setBoundaryConditions $time

    nextTime

    incr t
}

And here is a screenshot - the white bar is the wall that hinders the diffusion:

Screenshot diffusion problem