tadd "impermable" grid boundary condition - Granular.jl - Julia package for granular dynamics simulation
 (HTM) git clone git://src.adamsgaard.dk/Granular.jl
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit c9448d73e65ad2b3d4b243b7ad6803774afbde1b
 (DIR) parent a9fb9ff582212dcc6d22f83851987d97bda76f4f
 (HTM) Author: Anders Damsgaard <andersd@riseup.net>
       Date:   Mon,  6 Nov 2017 16:06:00 -0500
       
       add "impermable" grid boundary condition
       
       Diffstat:
         A grid-boundaries.jl                  |     203 +++++++++++++++++++++++++++++++
         M src/datatypes.jl                    |       2 +-
         M src/grid.jl                         |      76 +++++++++++++++++++++++++++++--
         M src/packing.jl                      |      10 ++++++++--
         M src/temporal_integration.jl         |       1 +
         D test/periodic-boundaries.jl         |     190 ------------------------------
         M test/runtests.jl                    |       2 +-
       
       7 files changed, 285 insertions(+), 199 deletions(-)
       ---
 (DIR) diff --git a/grid-boundaries.jl b/grid-boundaries.jl
       t@@ -0,0 +1,203 @@
       +#!/usr/bin/env julia
       +
       +info("#### $(basename(@__FILE__)) ####")
       +
       +verbose=false
       +i = 0
       +
       +info("## Inactive/Periodic BCs")
       +
       +info("Testing assignment and reporting of grid boundary conditions")
       +ocean = Granular.createEmptyOcean()
       +
       +Test.@test ocean.bc_west == 1
       +Test.@test ocean.bc_east == 1
       +Test.@test ocean.bc_north == 1
       +Test.@test ocean.bc_south == 1
       +
       +const originalSTDOUT = STDOUT
       +(out_r, out_w) = redirect_stdout()
       +Granular.reportGridBoundaryConditions(ocean)
       +close(out_w)
       +redirect_stdout(originalSTDOUT)
       +output = convert(String, readavailable(out_r))
       +Test.@test output == """West  (-x): inactive\t(1)
       +East  (+x): inactive\t(1)
       +South (-y): inactive\t(1)
       +North (+y): inactive\t(1)
       +"""
       +
       +(out_r, out_w) = redirect_stdout()
       +Granular.setGridBoundaryConditions!(ocean, "periodic", "south, west", verbose=true)
       +close(out_w)
       +redirect_stdout(originalSTDOUT)
       +output = convert(String, readavailable(out_r))
       +Test.@test output == """West  (-x): periodic\t(2)
       +East  (+x): inactive\t(1)
       +South (-y): periodic\t(2)
       +North (+y): inactive\t(1)
       +"""
       +Test.@test ocean.bc_west == 2
       +Test.@test ocean.bc_east == 1
       +Test.@test ocean.bc_north == 1
       +Test.@test ocean.bc_south == 2
       +
       +(out_r, out_w) = redirect_stdout()
       +Granular.setGridBoundaryConditions!(ocean, "inactive", "all", verbose=false)
       +close(out_w)
       +redirect_stdout(originalSTDOUT)
       +output = convert(String, readavailable(out_r))
       +Test.@test output == ""
       +Test.@test ocean.bc_west == 1
       +Test.@test ocean.bc_east == 1
       +Test.@test ocean.bc_north == 1
       +Test.@test ocean.bc_south == 1
       +
       +(out_r, out_w) = redirect_stdout()
       +Granular.setGridBoundaryConditions!(ocean, "periodic", "all")
       +close(out_w)
       +output = convert(String, readavailable(out_r))
       +redirect_stdout(originalSTDOUT)
       +Test.@test output == """West  (-x): periodic\t(2)
       +East  (+x): periodic\t(2)
       +South (-y): periodic\t(2)
       +North (+y): periodic\t(2)
       +"""
       +Test.@test ocean.bc_west == 2
       +Test.@test ocean.bc_east == 2
       +Test.@test ocean.bc_north == 2
       +Test.@test ocean.bc_south == 2
       +
       +(out_r, out_w) = redirect_stdout()
       +Granular.setGridBoundaryConditions!(ocean, "inactive")
       +close(out_w)
       +output = convert(String, readavailable(out_r))
       +redirect_stdout(originalSTDOUT)
       +Test.@test output == """West  (-x): inactive\t(1)
       +East  (+x): inactive\t(1)
       +South (-y): inactive\t(1)
       +North (+y): inactive\t(1)
       +"""
       +Test.@test ocean.bc_west == 1
       +Test.@test ocean.bc_east == 1
       +Test.@test ocean.bc_north == 1
       +Test.@test ocean.bc_south == 1
       +
       +(out_r, out_w) = redirect_stdout()
       +Granular.setGridBoundaryConditions!(ocean, "periodic")
       +close(out_w)
       +output = convert(String, readavailable(out_r))
       +redirect_stdout(originalSTDOUT)
       +Test.@test output == """West  (-x): periodic\t(2)
       +East  (+x): periodic\t(2)
       +South (-y): periodic\t(2)
       +North (+y): periodic\t(2)
       +"""
       +Test.@test ocean.bc_west == 2
       +Test.@test ocean.bc_east == 2
       +Test.@test ocean.bc_north == 2
       +Test.@test ocean.bc_south == 2
       +
       +Test.@test_throws ErrorException Granular.setGridBoundaryConditions!(ocean,
       +                                                                     "periodic",
       +                                                                     "asdf")
       +
       +Test.@test_throws ErrorException Granular.setGridBoundaryConditions!(ocean,
       +                                                                     "asdf")
       +
       +
       +info("Testing granular interaction across periodic boundaries")
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "periodic")
       +Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false)
       +Granular.addGrainCylindrical!(sim, [0.9, 0.5], 0.11, 0.1, verbose=false)
       +
       +# there should be an error if all-to-all contact search is used
       +Test.@test_throws ErrorException Granular.findContacts!(sim)
       +Test.@test_throws ErrorException Granular.findContacts!(sim, method="all to all")
       +Test.@test_throws ErrorException Granular.findContactsAllToAll!(sim)
       +
       +Granular.sortGrainsInGrid!(sim, sim.ocean, verbose=false)
       +Granular.findContacts!(sim, method="ocean grid")
       +Test.@test 2 == sim.grains[1].contacts[1]
       +Test.@test 1 == sim.grains[1].n_contacts
       +Test.@test 1 == sim.grains[2].n_contacts
       +
       +
       +info("Test grain position adjustment across periodic boundaries")
       +# do not readjust inside grid, inactive boundaries
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "inactive", verbose=false)
       +Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false)
       +Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       +Test.@test [0.1, 0.5] ≈ sim.grains[1].lin_pos
       +
       +# do not readjust inside grid, periodic boundaries
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       +Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false)
       +Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       +Test.@test [0.1, 0.5] ≈ sim.grains[1].lin_pos
       +
       +# do not readjust outside grid, inactive boundaries
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "inactive", verbose=false)
       +Granular.addGrainCylindrical!(sim, [-0.1, 0.5], 0.11, 0.1, verbose=false)
       +Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       +Test.@test [-0.1, 0.5] ≈ sim.grains[1].lin_pos
       +
       +# readjust outside grid, periodic boundaries, -x
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       +Granular.addGrainCylindrical!(sim, [-0.1, 0.5], 0.11, 0.1, verbose=false)
       +Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       +Test.@test [0.9, 0.5] ≈ sim.grains[1].lin_pos
       +
       +# readjust outside grid, periodic boundaries, +x
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       +Granular.addGrainCylindrical!(sim, [1.1, 0.5], 0.11, 0.1, verbose=false)
       +Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       +Test.@test [0.1, 0.5] ≈ sim.grains[1].lin_pos
       +
       +# readjust outside grid, periodic boundaries, -y
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       +Granular.addGrainCylindrical!(sim, [0.3, -0.1], 0.11, 0.1, verbose=false)
       +Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       +Test.@test [0.3, 0.9] ≈ sim.grains[1].lin_pos
       +
       +# readjust outside grid, periodic boundaries, +y
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       +Granular.addGrainCylindrical!(sim, [0.3, 1.1], 0.11, 0.1, verbose=false)
       +Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       +Test.@test [0.3, 0.1] ≈ sim.grains[1].lin_pos
       +
       +# throw error if atmosphere and ocean BCs differ
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +sim.atmosphere = Granular.createRegularAtmosphereGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       +Granular.addGrainCylindrical!(sim, [0.3, 1.1], 0.11, 0.1, verbose=false)
       +Test.@test_throws ErrorException Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       +
       +
       +info("## Impermeable BCs")
       +
       +info("Test grain velocity adjustment across impermeable boundaries")
       +# do not readjust inside grid, inactive boundaries
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       +Granular.setGridBoundaryConditions!(sim.ocean, "inactive", verbose=false)
       +Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false)
       +Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       +Test.@test [0.1, 0.5] ≈ sim.grains[1].lin_pos
 (DIR) diff --git a/src/datatypes.jl b/src/datatypes.jl
       t@@ -308,5 +308,5 @@ mutable struct Simulation
        end
        
        # Mappings between boundary condition keys (Integers) and strings
       -const grid_bc_strings = ["inactive", "periodic"]
       +const grid_bc_strings = ["inactive", "periodic", "impermeable"]
        const grid_bc_flags = Dict(zip(grid_bc_strings, 1:length(grid_bc_strings)))
 (DIR) diff --git a/src/grid.jl b/src/grid.jl
       t@@ -643,10 +643,10 @@ Grains can interact mechanically across the periodic boundary.
            `"east"` (+x), `"north"` (+y).  The values may be delimited in any way.
            Also, and by default, all boundaries can be selected with `"all"` (-x, -y,
            +x, +y), which overrides any other face selection.
       -* `mode::String`: Boundary behavior, accepted values are `"inactive"` and
       -    `"periodic"`.  You cannot specify more than one mode at a time, so if
       -    several modes are desired as boundary conditions for the grid, several calls
       -    to this function should be made.
       +* `mode::String`: Boundary behavior, accepted values are `"inactive"`,
       +    `"periodic"`, and `"impermeable"`.  You cannot specify more than one mode at
       +    a time, so if several modes are desired as boundary conditions for the grid,
       +    several calls to this function should be made.
        * `verbose::Bool`: Confirm boundary conditions by reporting values to console.
        
        # Examples
       t@@ -742,7 +742,6 @@ called after temporal integration of the grain positions.
        """
        function moveGrainsAcrossPeriodicBoundaries!(sim::Simulation)
        
       -
            # return if grids are not enabled
            if typeof(sim.ocean.input_file) == Bool && 
                typeof(sim.atmosphere.input_file) == Bool
       t@@ -793,6 +792,73 @@ function moveGrainsAcrossPeriodicBoundaries!(sim::Simulation)
            nothing
        end
        
       +"""
       +    reflectGrainsFromImpermeableBoundaries!(simulation::Simulation)
       +
       +If the ocean or atmosphere grids are impermeable, reflect grain trajectories by
       +reversing the velocity vectors normal to the boundary.  This function is to be
       +called after temporal integration of the grain positions.
       +"""
       +function reflectGrainsFromImpermeableBoundaries!(sim::Simulation)
       +
       +    # return if grids are not enabled
       +    if typeof(sim.ocean.input_file) == Bool && 
       +        typeof(sim.atmosphere.input_file) == Bool
       +        return nothing
       +    end
       +
       +    # return immediately if no boundaries are periodic
       +    if sim.ocean.bc_west != 3 && 
       +        sim.ocean.bc_south != 3 && 
       +        sim.ocean.bc_east != 3 && 
       +        sim.ocean.bc_north != 3
       +        return nothing
       +    end
       +
       +    # throw error if ocean and atmosphere grid BCs are different and both are
       +    # enabled
       +    if (typeof(sim.ocean.input_file) != Bool &&
       +        typeof(sim.atmosphere.input_file) != Bool) &&
       +        (sim.ocean.bc_west != sim.atmosphere.bc_west &&
       +         sim.ocean.bc_south != sim.atmosphere.bc_south &&
       +         sim.ocean.bc_east != sim.atmosphere.bc_east &&
       +         sim.ocean.bc_north != sim.atmosphere.bc_north)
       +        error("Ocean and Atmosphere grid boundary conditions differ")
       +    end
       +
       +    for grain in sim.grains
       +
       +        # -x
       +        if sim.ocean.bc_west == 3 && 
       +            grain.lin_pos[1] - grain.contact_radius < sim.ocean.xq[1]
       +
       +            grain.lin_vel[1] *= -1.
       +        end
       +
       +        # -y
       +        if sim.ocean.bc_south == 3 && 
       +            grain.lin_pos[2] - grain.contact_radius < sim.ocean.yq[1]
       +
       +            grain.lin_vel[2] *= -1.
       +        end
       +
       +        # +x
       +        if sim.ocean.bc_east == 3 &&
       +            grain.lin_pos[1] + grain.contact_radius > sim.ocean.xq[end]
       +
       +            grain.lin_vel[1] *= -1.
       +        end
       +
       +        # +y
       +        if sim.ocean.bc_east == 3 && 
       +            grain.lin_pos[2] + grain.contact_radius > sim.ocean.yq[end]
       +
       +            grain.lin_vel[2] *= -1.
       +        end
       +    end
       +    nothing
       +end
       +
        export fitGridToGrains!
        """
            fitGridToGrains!(simulation, grid[, padding])
 (DIR) diff --git a/src/packing.jl b/src/packing.jl
       t@@ -34,8 +34,10 @@ function regularPacking!(simulation::Simulation,
                                 seed::Integer=1)
        
            r_rand = 0.
       +    pos = zeros(2)
            h = .5   # disc tickness
            dx = r_max*2.*(1. + padding_factor)  # cell size
       +    dx_padding = r_max*2.*padding_factor
            srand(seed)
        
            for iy in 1:n[2]
       t@@ -48,8 +50,12 @@ function regularPacking!(simulation::Simulation,
                        r_rand = rand()*(r_max - r_min) + r_min
                    end
        
       -            addGrainCylindrical!(simulation, [ix*dx - .5*dx, iy*dx - .5*dx],
       -                                 r_rand, h, verbose=false)
       +            # Determine position from grid index and sample randomly from within
       +            # padding
       +            pos .= [ix*dx - .5*dx, iy*dx - .5*dx] .+
       +                rand(2) .* dx_padding .- .5*dx_padding
       +
       +            addGrainCylindrical!(simulation, pos, r_rand, h, verbose=false)
                end
            end
        
 (DIR) diff --git a/src/temporal_integration.jl b/src/temporal_integration.jl
       t@@ -39,6 +39,7 @@ function updateGrainKinematics!(simulation::Simulation;
                error("Unknown integration method '$method'")
            end
            moveGrainsAcrossPeriodicBoundaries!(simulation)
       +    reflectGrainsFromImpermeableBoundaries!(simulation)
            nothing
        end
        
 (DIR) diff --git a/test/periodic-boundaries.jl b/test/periodic-boundaries.jl
       t@@ -1,190 +0,0 @@
       -#!/usr/bin/env julia
       -
       -info("#### $(basename(@__FILE__)) ####")
       -
       -verbose=false
       -i = 0
       -
       -info("Testing assignment and reporting of grid boundary conditions")
       -ocean = Granular.createEmptyOcean()
       -
       -Test.@test ocean.bc_west == 1
       -Test.@test ocean.bc_east == 1
       -Test.@test ocean.bc_north == 1
       -Test.@test ocean.bc_south == 1
       -
       -const originalSTDOUT = STDOUT
       -(out_r, out_w) = redirect_stdout()
       -Granular.reportGridBoundaryConditions(ocean)
       -close(out_w)
       -redirect_stdout(originalSTDOUT)
       -output = convert(String, readavailable(out_r))
       -Test.@test output == """West  (-x): inactive\t(1)
       -East  (+x): inactive\t(1)
       -South (-y): inactive\t(1)
       -North (+y): inactive\t(1)
       -"""
       -
       -(out_r, out_w) = redirect_stdout()
       -Granular.setGridBoundaryConditions!(ocean, "periodic", "south, west", verbose=true)
       -close(out_w)
       -redirect_stdout(originalSTDOUT)
       -output = convert(String, readavailable(out_r))
       -Test.@test output == """West  (-x): periodic\t(2)
       -East  (+x): inactive\t(1)
       -South (-y): periodic\t(2)
       -North (+y): inactive\t(1)
       -"""
       -Test.@test ocean.bc_west == 2
       -Test.@test ocean.bc_east == 1
       -Test.@test ocean.bc_north == 1
       -Test.@test ocean.bc_south == 2
       -
       -(out_r, out_w) = redirect_stdout()
       -Granular.setGridBoundaryConditions!(ocean, "inactive", "all", verbose=false)
       -close(out_w)
       -redirect_stdout(originalSTDOUT)
       -output = convert(String, readavailable(out_r))
       -Test.@test output == ""
       -Test.@test ocean.bc_west == 1
       -Test.@test ocean.bc_east == 1
       -Test.@test ocean.bc_north == 1
       -Test.@test ocean.bc_south == 1
       -
       -(out_r, out_w) = redirect_stdout()
       -Granular.setGridBoundaryConditions!(ocean, "periodic", "all")
       -close(out_w)
       -output = convert(String, readavailable(out_r))
       -redirect_stdout(originalSTDOUT)
       -Test.@test output == """West  (-x): periodic\t(2)
       -East  (+x): periodic\t(2)
       -South (-y): periodic\t(2)
       -North (+y): periodic\t(2)
       -"""
       -Test.@test ocean.bc_west == 2
       -Test.@test ocean.bc_east == 2
       -Test.@test ocean.bc_north == 2
       -Test.@test ocean.bc_south == 2
       -
       -(out_r, out_w) = redirect_stdout()
       -Granular.setGridBoundaryConditions!(ocean, "inactive")
       -close(out_w)
       -output = convert(String, readavailable(out_r))
       -redirect_stdout(originalSTDOUT)
       -Test.@test output == """West  (-x): inactive\t(1)
       -East  (+x): inactive\t(1)
       -South (-y): inactive\t(1)
       -North (+y): inactive\t(1)
       -"""
       -Test.@test ocean.bc_west == 1
       -Test.@test ocean.bc_east == 1
       -Test.@test ocean.bc_north == 1
       -Test.@test ocean.bc_south == 1
       -
       -(out_r, out_w) = redirect_stdout()
       -Granular.setGridBoundaryConditions!(ocean, "periodic")
       -close(out_w)
       -output = convert(String, readavailable(out_r))
       -redirect_stdout(originalSTDOUT)
       -Test.@test output == """West  (-x): periodic\t(2)
       -East  (+x): periodic\t(2)
       -South (-y): periodic\t(2)
       -North (+y): periodic\t(2)
       -"""
       -Test.@test ocean.bc_west == 2
       -Test.@test ocean.bc_east == 2
       -Test.@test ocean.bc_north == 2
       -Test.@test ocean.bc_south == 2
       -
       -Test.@test_throws ErrorException Granular.setGridBoundaryConditions!(ocean,
       -                                                                     "periodic",
       -                                                                     "asdf")
       -
       -Test.@test_throws ErrorException Granular.setGridBoundaryConditions!(ocean,
       -                                                                     "asdf")
       -
       -
       -info("Testing granular interaction across periodic boundaries")
       -sim = Granular.createSimulation()
       -sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       -Granular.setGridBoundaryConditions!(sim.ocean, "periodic")
       -Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false)
       -Granular.addGrainCylindrical!(sim, [0.9, 0.5], 0.11, 0.1, verbose=false)
       -
       -# there should be an error if all-to-all contact search is used
       -Test.@test_throws ErrorException Granular.findContacts!(sim)
       -Test.@test_throws ErrorException Granular.findContacts!(sim, method="all to all")
       -Test.@test_throws ErrorException Granular.findContactsAllToAll!(sim)
       -
       -Granular.sortGrainsInGrid!(sim, sim.ocean, verbose=false)
       -Granular.findContacts!(sim, method="ocean grid")
       -Test.@test 2 == sim.grains[1].contacts[1]
       -Test.@test 1 == sim.grains[1].n_contacts
       -Test.@test 1 == sim.grains[2].n_contacts
       -
       -
       -info("Test grain position adjustment across periodic boundaries")
       -# do not readjust inside grid, inactive boundaries
       -sim = Granular.createSimulation()
       -sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       -Granular.setGridBoundaryConditions!(sim.ocean, "inactive", verbose=false)
       -Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false)
       -Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       -Test.@test [0.1, 0.5] ≈ sim.grains[1].lin_pos
       -
       -# do not readjust inside grid, periodic boundaries
       -sim = Granular.createSimulation()
       -sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       -Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       -Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false)
       -Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       -Test.@test [0.1, 0.5] ≈ sim.grains[1].lin_pos
       -
       -# do not readjust outside grid, inactive boundaries
       -sim = Granular.createSimulation()
       -sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       -Granular.setGridBoundaryConditions!(sim.ocean, "inactive", verbose=false)
       -Granular.addGrainCylindrical!(sim, [-0.1, 0.5], 0.11, 0.1, verbose=false)
       -Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       -Test.@test [-0.1, 0.5] ≈ sim.grains[1].lin_pos
       -
       -# readjust outside grid, periodic boundaries, -x
       -sim = Granular.createSimulation()
       -sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       -Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       -Granular.addGrainCylindrical!(sim, [-0.1, 0.5], 0.11, 0.1, verbose=false)
       -Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       -Test.@test [0.9, 0.5] ≈ sim.grains[1].lin_pos
       -
       -# readjust outside grid, periodic boundaries, +x
       -sim = Granular.createSimulation()
       -sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       -Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       -Granular.addGrainCylindrical!(sim, [1.1, 0.5], 0.11, 0.1, verbose=false)
       -Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       -Test.@test [0.1, 0.5] ≈ sim.grains[1].lin_pos
       -
       -# readjust outside grid, periodic boundaries, -y
       -sim = Granular.createSimulation()
       -sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       -Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       -Granular.addGrainCylindrical!(sim, [0.3, -0.1], 0.11, 0.1, verbose=false)
       -Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       -Test.@test [0.3, 0.9] ≈ sim.grains[1].lin_pos
       -
       -# readjust outside grid, periodic boundaries, +y
       -sim = Granular.createSimulation()
       -sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       -Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       -Granular.addGrainCylindrical!(sim, [0.3, 1.1], 0.11, 0.1, verbose=false)
       -Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       -Test.@test [0.3, 0.1] ≈ sim.grains[1].lin_pos
       -
       -# throw error if atmosphere and ocean BCs differ
       -sim = Granular.createSimulation()
       -sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.])
       -sim.atmosphere = Granular.createRegularAtmosphereGrid([5, 5, 2], [1., 1., 1.])
       -Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false)
       -Granular.addGrainCylindrical!(sim, [0.3, 1.1], 0.11, 0.1, verbose=false)
       -Test.@test_throws ErrorException Granular.moveGrainsAcrossPeriodicBoundaries!(sim)
       -
 (DIR) diff --git a/test/runtests.jl b/test/runtests.jl
       t@@ -13,7 +13,7 @@ include("netcdf.jl")
        include("vtk.jl")
        include("jld.jl")
        include("grid.jl")
       -include("periodic-boundaries.jl")
       +include("grid-boundaries.jl")
        include("ocean.jl")
        include("atmosphere.jl")
        include("memory-management.jl")