tAdd wall-normal viscosity in parallel to elasticity - 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 b320d55a80891c9261ff58288e9c751826b581fa
 (DIR) parent 5ce691186cd1e035b45adafe40e4ba9067902886
 (HTM) Author: Anders Damsgaard <andersd@riseup.net>
       Date:   Tue, 19 Dec 2017 10:22:57 -0500
       
       Add wall-normal viscosity in parallel to elasticity
       
       Diffstat:
         M src/datatypes.jl                    |       1 +
         M src/interaction.jl                  |      39 ++++++++++++++++++++++++++-----
         M src/wall.jl                         |       8 +++++++-
         M test/wall.jl                        |     105 ++++++++++++++++++++++++++++++-
       
       4 files changed, 145 insertions(+), 8 deletions(-)
       ---
 (DIR) diff --git a/src/datatypes.jl b/src/datatypes.jl
       t@@ -83,6 +83,7 @@ mutable struct WallLinearFrictionless
            vel::Float64              # Velocity (constant when bc == "normal stress")
            acc::Float64              # Acceleration (zero when bc == "velocity")
            force::Float64            # Sum of normal forces on wall
       +    contact_viscosity_normal::Float64 # Wall-normal contact viscosity
        end
        
        # Type for gathering data from grain objects into single arrays
 (DIR) diff --git a/src/interaction.jl b/src/interaction.jl
       t@@ -49,6 +49,9 @@ function interactWalls!(sim::Simulation)
            orientation::Float64 = 0.0
            δ_n::Float64 = 0.0
            k_n::Float64 = 0.0
       +    γ_n::Float64 = 0.0
       +    vel_n::Float64 = 0.0
       +    force_n::Float64 = 0.0
        
            for iw=1:length(sim.walls)
                for i=1:length(sim.grains)
       t@@ -57,10 +60,12 @@ function interactWalls!(sim::Simulation)
                                           sim.grains[i].lin_pos) - sim.walls[iw].pos)
        
                    # get overlap distance by projecting grain position onto wall-normal
       -            # vector
       +            # vector. Overlap when δ_n < 0.0
                    δ_n = abs(dot(sim.walls[iw].normal, sim.grains[i].lin_pos) -
                              sim.walls[iw].pos) - sim.grains[i].contact_radius
        
       +            vel_n = dot(sim.walls[iw].normal, sim.grains[i].lin_vel)
       +
                    if δ_n < 0.
                        if sim.grains[i].youngs_modulus > 0.
                            k_n = sim.grains[i].youngs_modulus * sim.grains[i].thickness
       t@@ -68,9 +73,31 @@ function interactWalls!(sim::Simulation)
                            k_n = sim.grains[i].contact_stiffness_normal
                        end
        
       -                sim.grains[i].force += -k_n * δ_n .* sim.walls[iw].normal .*
       +                γ_n = sim.walls[iw].contact_viscosity_normal
       +
       +                if k_n ≈ 0. && γ_n ≈ 0.  # No interaction
       +                    force_n = 0.
       +
       +                elseif k_n > 0. && γ_n ≈ 0.  # Elastic (Hookean)
       +                    force_n = k_n * δ_n
       +
       +                elseif k_n > 0. && γ_n > 0.  # Elastic-viscous (Kelvin-Voigt)
       +                    force_n = k_n * δ_n + γ_n * vel_n
       +
       +                    # Make sure that the visous component doesn't dominate over
       +                    # elasticity
       +                    if force_n > 0.
       +                        force_n = 0.
       +                    end
       +
       +                else
       +                    error("unknown contact_normal_rheology " *
       +                          "(k_n = $k_n, γ_n = $γ_n")
       +                end
       +
       +                sim.grains[i].force += -force_n .* sim.walls[iw].normal .*
                            orientation
       -                sim.walls[iw].force += k_n * δ_n * orientation
       +                sim.walls[iw].force += force_n * orientation
                    end
                end
            end
       t@@ -162,13 +189,13 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int)
                               simulation.grains[j].contact_dynamic_friction)
        
            # Determine contact forces
       -    if k_n ≈ 0. && γ_n ≈ 0.
       +    if k_n ≈ 0. && γ_n ≈ 0.  # No interaction
                force_n = 0.
        
       -    elseif k_n > 0. && γ_n ≈ 0.
       +    elseif k_n > 0. && γ_n ≈ 0.  # Elastic (Hookean)
                force_n = -k_n*δ_n
        
       -    elseif k_n > 0. && γ_n > 0.
       +    elseif k_n > 0. && γ_n > 0.  # Elastic-viscous (Kelvin-Voigt)
                force_n = -k_n*δ_n - γ_n*vel_n
                if force_n < 0.
                    force_n = 0.
 (DIR) diff --git a/src/wall.jl b/src/wall.jl
       t@@ -35,6 +35,10 @@ The only required arguments are
            parameter. Units: [m/s]
        * `force::Float64=0.`: sum of normal forces on the wall from interaction with
            grains [N].
       +* `contact_viscosity_normal::Float64=0.`: viscosity to apply in parallel to
       +    elasticity in interactions between wall and particles [N/(m/s)]. When this
       +    term is larger than zero, the wall-grain interaction acts like a sink of
       +    kinetic energy.
        * `verbose::Bool=true`: show verbose information during function call.
        
        # Examples
       t@@ -74,6 +78,7 @@ function addWallLinearFrictionless!(simulation::Simulation,
                                            vel::Float64 = 0.,
                                            acc::Float64 = 0.,
                                            force::Float64 = 0.,
       +                                    contact_viscosity_normal = 0.,
                                            verbose::Bool=true)
        
            # Check input values
       t@@ -143,7 +148,8 @@ function addWallLinearFrictionless!(simulation::Simulation,
                                          normal_stress,
                                          vel,
                                          acc,
       -                                  force)
       +                                  force,
       +                                  contact_viscosity_normal)
        
            # Add to simulation object
            addWall!(simulation, wall, verbose)
 (DIR) diff --git a/test/wall.jl b/test/wall.jl
       t@@ -50,7 +50,7 @@ Granular.addWallLinearFrictionless!(sim, [0., 1.], 1., verbose=false)
        @test Granular.getWallSurfaceArea(sim, 1) ≈ 20.0*2.0
        @test Granular.getWallSurfaceArea(sim, 2) ≈ 10.0*2.0
        
       -info("# Test wall-grain interaction")
       +info("# Test wall-grain interaction: elastic")
        
        info("Wall present but no contact")
        sim = Granular.createSimulation()
       t@@ -118,6 +118,109 @@ Granular.interactWalls!(sim)
        @test sim.grains[1].force[1] ≈ 0.
        @test sim.grains[1].force[2] < 0.
        
       +info("# Test wall-grain interaction: elastic-viscous")
       +
       +info("Wall present but no contact")
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([1, 1, 1], [10., 20., 1.0])
       +Granular.addGrainCylindrical!(sim, [ 0., 0.], 1., 2., verbose=false)
       +Granular.addWallLinearFrictionless!(sim, [1., 0.], -1.01, verbose=false)
       +sim.walls[1].contact_viscosity_normal = 1e3
       +Granular.setTimeStep!(sim, verbose=false)
       +Granular.interactWalls!(sim)
       +@test sim.walls[1].force ≈ 0.
       +@test sim.grains[1].force[1] ≈ 0.
       +@test sim.grains[1].force[2] ≈ 0.
       +
       +info("Wall present but no contact")
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([1, 1, 1], [10., 20., 1.0])
       +Granular.addGrainCylindrical!(sim, [ 0., 0.], 1., 2., verbose=false)
       +Granular.addWallLinearFrictionless!(sim, [1., 0.], +2.01, verbose=false)
       +sim.walls[1].contact_viscosity_normal = 1e3
       +Granular.setTimeStep!(sim, verbose=false)
       +Granular.interactWalls!(sim)
       +@test sim.walls[1].force ≈ 0.
       +@test sim.grains[1].force[1] ≈ 0.
       +@test sim.grains[1].force[2] ≈ 0.
       +
       +info("Wall at -x")
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([1, 1, 1], [10., 20., 1.0])
       +Granular.addGrainCylindrical!(sim, [ 0., 0.], 1., 2., verbose=false)
       +Granular.addWallLinearFrictionless!(sim, [1., 0.], -1. + .01, verbose=false)
       +sim.walls[1].contact_viscosity_normal = 1e3
       +Granular.setTimeStep!(sim, verbose=false)
       +Granular.interactWalls!(sim)
       +@test sim.walls[1].force < 0.
       +@test sim.grains[1].force[1] > 0.
       +@test sim.grains[1].force[2] ≈ 0.
       +
       +info("Wall at +x")
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([1, 1, 1], [10., 20., 1.0])
       +Granular.addGrainCylindrical!(sim, [ 0., 0.], 1., 2., verbose=false)
       +Granular.addWallLinearFrictionless!(sim, [1., 0.], +1. - .01, verbose=false)
       +sim.walls[1].contact_viscosity_normal = 1e3
       +Granular.setTimeStep!(sim, verbose=false)
       +Granular.interactWalls!(sim)
       +@test sim.walls[1].force > 0.
       +@test sim.grains[1].force[1] < 0.
       +@test sim.grains[1].force[2] ≈ 0.
       +
       +info("Wall at -y")
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([1, 1, 1], [10., 20., 1.0])
       +Granular.addGrainCylindrical!(sim, [ 0., 0.], 1., 2., verbose=false)
       +Granular.addWallLinearFrictionless!(sim, [0., 1.], -1. + .01, verbose=false)
       +sim.walls[1].contact_viscosity_normal = 1e3
       +Granular.setTimeStep!(sim, verbose=false)
       +Granular.interactWalls!(sim)
       +@test sim.walls[1].force < 0.
       +@test sim.grains[1].force[1] ≈ 0.
       +@test sim.grains[1].force[2] > 0.
       +
       +info("Wall at +y")
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([1, 1, 1], [10., 20., 1.0])
       +Granular.addGrainCylindrical!(sim, [ 0., 0.], 1., 2., verbose=false)
       +Granular.addWallLinearFrictionless!(sim, [0., 1.], +1. - .01, verbose=false)
       +sim.walls[1].contact_viscosity_normal = 1e3
       +Granular.setTimeStep!(sim, verbose=false)
       +Granular.interactWalls!(sim)
       +@test sim.walls[1].force > 0.
       +@test sim.grains[1].force[1] ≈ 0.
       +@test sim.grains[1].force[2] < 0.
       +
       +info("Full collision with wall")
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([1, 1, 1], [10., 20., 1.0])
       +Granular.addGrainCylindrical!(sim, [1.2, 0.], 1., 2., verbose=false)
       +Granular.addWallLinearFrictionless!(sim, [1., 0.], 0., verbose=false)
       +sim.walls[1].contact_viscosity_normal = 1e3
       +sim.grains[1].lin_vel[1] = -0.2
       +Granular.setTimeStep!(sim, verbose=false)
       +Granular.setTotalTime!(sim, 5.)
       +lin_vel0 = sim.grains[1].lin_vel
       +Granular.run!(sim)
       +@test sim.grains[1].lin_vel[1] < abs(lin_vel0[1])
       +@test sim.grains[1].lin_vel[2] ≈ 0.
       +lin_vel1 = sim.grains[1].lin_vel
       +
       +sim = Granular.createSimulation()
       +sim.ocean = Granular.createRegularOceanGrid([1, 1, 1], [10., 20., 1.0])
       +Granular.addGrainCylindrical!(sim, [1.2, 0.], 1., 2., verbose=false)
       +Granular.addWallLinearFrictionless!(sim, [1., 0.], 0., verbose=false)
       +sim.walls[1].contact_viscosity_normal = 1e4
       +sim.grains[1].lin_vel[1] = -0.2
       +Granular.setTimeStep!(sim, verbose=false)
       +Granular.setTotalTime!(sim, 5.)
       +lin_vel0 = sim.grains[1].lin_vel
       +Granular.run!(sim)
       +@test sim.grains[1].lin_vel[1] < abs(lin_vel0[1])
       +@test sim.grains[1].lin_vel[1] < lin_vel1[1]
       +@test sim.grains[1].lin_vel[2] ≈ 0.
       +
        
        info("# Testing wall dynamics")