tadd dynamic walls in simulation object, dynamics still not implemented - 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 f99ed0c1563094ca59a039ab144b9da3e76c1fed
 (DIR) parent b9af1feee431c70f1b40417dbf171db440188c50
 (HTM) Author: Anders Damsgaard <andersd@riseup.net>
       Date:   Tue, 14 Nov 2017 11:01:45 -0500
       
       add dynamic walls in simulation object, dynamics still not implemented
       
       Diffstat:
         M src/datatypes.jl                    |      14 ++++++++++++++
         M src/simulation.jl                   |      43 +++++++++++++++++++++++--------
         A src/wall.jl                         |     122 +++++++++++++++++++++++++++++++
         M test/runtests.jl                    |       1 +
         A test/wall.jl                        |      26 ++++++++++++++++++++++++++
       
       5 files changed, 195 insertions(+), 11 deletions(-)
       ---
 (DIR) diff --git a/src/datatypes.jl b/src/datatypes.jl
       t@@ -70,6 +70,18 @@ mutable struct GrainCylindrical
            atmosphere_stress::Vector{Float64}
        end
        
       +# Type for linear (flat) and frictionless dynamic walls
       +mutable struct WallLinearFrictionless
       +    normal::Vector{Float64}   # Wall-face normal vector
       +    pos::Float64              # Position along axis parallel to normal vector
       +    bc::String                # Boundary condition
       +    mass::Float64             # Mass, used when bc != "fixed"
       +    thickness::Float64        # Wall thickness
       +    normal_stress::Float64    # Normal stress when bc == "normal stress"
       +    vel::Float64              # Velocity (constant when bc == "normal stress")
       +    force::Float64            # Sum of normal forces on wall
       +end
       +
        # Type for gathering data from grain objects into single arrays
        mutable struct GrainArrays
        
       t@@ -310,6 +322,8 @@ mutable struct Simulation
            atmosphere::Atmosphere
        
            Nc_max::Int
       +
       +    walls::Vector{WallLinearFrictionless}
        end
        
        # Mappings between boundary condition keys (Integers) and strings
 (DIR) diff --git a/src/simulation.jl b/src/simulation.jl
       t@@ -11,15 +11,16 @@ export createSimulation
                              file_number::Int=0,
                              grains=Array{GrainCylindrical, 1}[],
                              ocean::Ocean,
       -                      atmosphere::Atmosphere)
       +                      atmosphere::Atmosphere,
       +                      Nc_max::Int=16])
        
        Create a simulation object containing all relevant variables such as temporal 
       -parameters, and lists of grains and contacts. The parameter `id` is used to
       -uniquely identify the simulation when it is written to disk.
       -
       -# Arguments
       -* `id::String="unnamed"`:
       +parameters, fluid grids, grains, and contacts.  All arguments are optional. The
       +most important parameter is `id`, which is used to uniquely identify the
       +simulation when it is written to disk.
        
       +# Optional arguments
       +* `id::String="unnamed"`: simulation identifying string.
        
        """
        function createSimulation(;id::String="unnamed",
       t@@ -201,19 +202,39 @@ end
        export addGrain!
        """
            addGrain!(simulation::Simulation,
       -                grain::GrainCylindrical,
       -                verbose::Bool = False)
       +              grain::GrainCylindrical,
       +              verbose::Bool = false)
        
        Add an `grain` to the `simulation` object.  If `verbose` is true, a short 
        confirmation message will be printed to stdout.
        """
        function addGrain!(simulation::Simulation,
       -                     grain::GrainCylindrical,
       -                     verbose::Bool = False)
       +                   grain::GrainCylindrical,
       +                   verbose::Bool = false)
            push!(simulation.grains, grain)
        
            if verbose
       -        info("Added Grain $(length(simulation.grains))")
       +        info("Added grain $(length(simulation.grains))")
       +    end
       +    nothing
       +end
       +
       +export addWall!
       +"""
       +    addWall!(simulation::Simulation,
       +             wall::WallLinearFrictionless,
       +             verbose::Bool = false)
       +
       +Add an `wall` to the `simulation` object.  If `verbose` is true, a short 
       +confirmation message will be printed to stdout.
       +"""
       +function addWall!(simulation::Simulation,
       +                  grain::WallLinearFrictionless,
       +                  verbose::Bool = false)
       +    push!(simulation.walls, wall)
       +
       +    if verbose
       +        info("Added wall $(length(simulation.walls))")
            end
            nothing
        end
 (DIR) diff --git a/src/wall.jl b/src/wall.jl
       t@@ -0,0 +1,122 @@
       +## Manage dynamic walls in the model
       +
       +export addWallLinearFrictionless!
       +"""
       +    function addWallLinear!(simulation, normal, pos[, bc, mass, thickness, 
       +                            normal_stress, vel, force, verbose])
       +
       +Creates and adds a linear (flat) and frictionless dynamic wall to a grain to a
       +simulation. Most of the arguments are optional, and come with default values.
       +The only required arguments are 
       +`simulation`, `normal`, `pos`, and `bc`.
       +
       +# Arguments
       +* `simulation::Simulation`: the simulation object where the wall should be
       +    added to.
       +* `normal::Vector{Float64}`: 2d vector denoting the normal to the wall [m].
       +* `pos::Float64`: position along axis parallel to the normal vector [m].
       +* `bc::String="fixed"`: boundary condition, possible values are `"fixed"`
       +    (default), `"normal stress"`, or `"velocity"`.
       +* `mass::Float64=NaN`: wall mass, which is used if wall boundary conditions
       +    differs from `bc="fixed"`.  If the parameter is left to its default value,
       +    the wall mass is set to be equal the total mass of grains in the simulation.
       +    Units: [kg]
       +* `thickness::Float64=NaN`: wall thickness, which is used for determining wall
       +    surface area.  If the parameter is left to its default value, the wall
       +    thickness is set to be equal to the thickest grain in the simulation.
       +    Units: [m].
       +* `normal_stress::Float64=0.`: the wall normal stress when `bc == "normal
       +    stress"` [Pa].
       +* `vel::Float64=0.`: the wall velocity along the `normal` vector.  If the
       +    wall boundary condition is `bc = "velocity"` the wall will move according to
       +    this constant value.  If `bc = "normal stress"` the velocity will be a free
       +    parameter. Units: [m/s]
       +* `force::Float64=0.`: sum of normal forces on the wall from interaction with
       +    grains [N].
       +* `verbose::Bool=true`: show verbose information during function call.
       +
       +# Examples
       +The most basic example adds a new fixed wall to the simulation `sim`, with a 
       +wall-face normal of `[1., 0.]` (wall along *y* and normal to *x*), a position of
       +`1.5` meter:
       +
       +```julia
       +Granular.addWallLinearFrictionless!(sim, [1., 0.], 1.5)
       +```
       +
       +The following example creates a wall with a velocity of 0.5 m/s towards *-y*:
       +
       +```julia
       +Granular.addWallLinearFrictionless!(sim, [0., 1.], 1.5,
       +                                    bc="velocity",
       +                                    vel=-0.5)
       +```
       +
       +To create a wall parallel to the *x* axis with a constant normal stress of 100
       +kPa:
       +
       +```julia
       +Granular.addWallLinearFrictionless!(sim, [1., 0.], 3.5,
       +                                    bc="normal stress",
       +                                    normal_stress=100e3)
       +```
       +"""
       +function addWallLinearFrictionless!(simulation::Simulation,
       +                                    normal::Vector{Float64},
       +                                    pos::Float64;
       +                                    bc::String = "fixed",
       +                                    mass::Float64 = NaN,
       +                                    thickness::Float64 = NaN,
       +                                    normal_stress::Float64 = 0.,
       +                                    vel::Float64 = 0.,
       +                                    force::Float64 = 0.,
       +                                    verbose::Boolw=true)
       +
       +    # Check input values
       +    if length(normal) != 2
       +        error("Wall normal must be a two-element array (normal = ",
       +              "$normal)")
       +    end
       +
       +    if !(normal ≈ [1., 0.]) || !(normal ≈ [0., 1.])
       +        error("Currently only walls with normals orthogonal to the " *
       +              "coordinate system are allowed, i.e. normals parallel to the " *
       +              "x or y axes.  Accepted values for `normal` " *
       +              "are [1., 0.] and [0., 1.]")
       +    end
       +
       +    # if not set, set wall mass to equal the mass of all grains.
       +    if isnan(mass)
       +        mass = 0.
       +        for grain in sim.grains
       +            mass += grain.mass
       +        end
       +        info("Setting wall mass to total grain mass: $mass kg")
       +    end
       +
       +    # if not set, set wall thickness to equal largest grain thickness
       +    if isnan(thickness)
       +        thickness = -Inf
       +        for grain in sim.grains
       +            if grain.thickess > thickness
       +                thickness = grain.thickness
       +            end
       +        end
       +        info("Setting wall thickness to largest grain thickness: $thickness m")
       +    end
       +
       +    # Create wall object
       +    wall = WallLinearFrictionless(normal,
       +                                   pos,
       +                                   bc,
       +                                   mass,
       +                                   thickness,
       +                                   normal_stress,
       +                                   vel,
       +                                   force)
       +
       +    # Add to simulation object
       +    addWall!(simulation, wall, verbose)
       +    nothing
       +end
       +
 (DIR) diff --git a/test/runtests.jl b/test/runtests.jl
       t@@ -2,6 +2,7 @@ using Compat.Test
        import Granular
        
        include("grain.jl")
       +include("wall.jl")
        include("packing.jl")
        include("util.jl")
        include("temporal.jl")
 (DIR) diff --git a/test/wall.jl b/test/wall.jl
       t@@ -0,0 +1,26 @@
       +#!/usr/bin/env julia
       +
       +# Check the basic dynamic wall functionality
       +
       +info("#### $(basename(@__FILE__)) ####")
       +
       +sim = Granular.createSimulation(id="test")
       +info("Testing grain value checks ")
       +@test_throws ErrorException Granular.addWallLinearFrictionless!(sim,
       +                                                                [.1, .1, .1],
       +                                                                1.)
       +@test_throws ErrorException Granular.addWallLinearFrictionless!(sim,
       +                                                                [1., 1.],
       +                                                                1.)
       +
       +
       +info("Check that wall mass equals total grain mass and max. thickness")
       +sim = Granular.createSimulation(id="test")
       +@test length(sim.walls) == 0
       +Granular.addGrainCylindrical!(sim, [ 0., 0.], 10., 2., verbose=false)
       +sim.grains[1].mass = 1.0
       +Granular.addWallLinearFrictionless!(sim, [1., 0.], 1., verbose=true)
       +@test length(sim.walls) == 1
       +@test sim.walls.mass ≈ 1.0
       +@test sim.walls.mass ≈ 2.0
       +