Task: Abelian sandpile model


C++: #include <iostream>
#include "xtensor/xarray.hpp"
#include "xtensor/xio.hpp"
#include "xtensor-io/ximage.hpp"

xt::xarray<int> init_grid (unsigned long x_dim, unsigned long y_dim)
{
    xt::xarray<int>::shape_type shape = { x_dim, y_dim };
    xt::xarray<int> grid(shape);

    grid(x_dim/2, y_dim/2) = 64000;
    
    return grid;
}

int print_grid(xt::xarray<int>& grid)
{
    
    
    

    
    xt::dump_image("grid.jpg", grid);

    return 0;
}

bool iterate_grid(xt::xarray<int>& grid, const unsigned long& x_dim, const unsigned long& y_dim)
{
    bool changed = false;

    for (unsigned long i=0; i < x_dim; ++i)
    {
        for (unsigned long j=0; j < y_dim; ++j)
        {
            if ( grid(i, j) >= 4 )
            {
                grid(i, j) -= 4;
                changed = true;
                try
                {
                    grid.at(i-1, j) += 1;
                    grid.at(i+1, j) += 1;
                    grid.at(i, j-1) += 1;
                    grid.at(i, j+1) += 1;
                }
                catch (const std::out_of_range& oor)
                {
                }
            }
        }
    }

    return changed;
}

int main(int argc, char* argv[])
{
    const unsigned long x_dim { 200 };
    const unsigned long y_dim { 200 };

    xt::xarray<int> grid = init_grid(x_dim, y_dim);
    bool changed { true };

    iterate_grid(grid, x_dim, y_dim);

    while (changed == true)
    {
        changed = iterate_grid(grid, x_dim, y_dim);
    }
    print_grid(grid);

    return 0;
}









Fortran: module abelian_sandpile_m

  implicit none

  private
  public :: pile

  type :: pile
    
    
    

    integer, allocatable :: grid(:,:)
    integer              :: n(2)

  contains
    procedure :: init
    procedure :: run

    procedure, private :: process_node
    procedure, private :: inside
  end type

contains

  logical function inside(this, i)
    class(pile), intent(in) :: this
    integer,     intent(in) :: i(2)

    inside = ((i(1) > 0) .and. (i(1) <= this%n(1)) .and. (i(2) > 0) .and. (i(2) <= this%n(2)) )
  end function

  recursive subroutine process_node(this, i)
    

    class(pile), intent(inout) :: this
    integer,     intent(in)    :: i(2)
      

    integer :: i0(2,2), j(2), d, k

    
    if (this%grid(i(1),i(2)) >= 4) then
      
      i0 = reshape([1,0,0,1], [2,2])

      
      this%grid(i(1),i(2)) = this%grid(i(1),i(2))-4

      
      do d = 1, 2               
        do k = -1, 1, 2         
          j = i+k*i0(:,d)       
          if (this%inside(j)) this%grid(j(1),j(2)) = this%grid(j(1),j(2)) + 1
        end do
      end do

      
      do d = 1, 2               
        do k = -1, 1, 2         
          j = i+k*i0(:,d)       
          if (this%inside(j)) call this%process_node(j)
        end do
      end do

      
      call this%process_node(i)
    end if
  end subroutine

  subroutine run(this)
    

    class(pile), intent(inout) :: this

    
    call this%process_node(this%n/2)
  end subroutine

  subroutine init(this, nx, ny, h)
    class(pile), intent(out) :: this
    integer,     intent(in)  :: nx, ny
      
    integer,     intent(in)  :: h
      

    this%n = [nx, ny]
    allocate (this%grid(nx,ny), source=0)
    this%grid(nx/2, ny/2) = h
  end subroutine

end module