Granular.jl

Julia package for granular dynamics simulation
git clone git://src.adamsgaard.dk/Granular.jl
Log | Files | Refs | README | LICENSE

commit f99ed0c1563094ca59a039ab144b9da3e76c1fed
parent b9af1feee431c70f1b40417dbf171db440188c50
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:
Msrc/datatypes.jl | 14++++++++++++++
Msrc/simulation.jl | 43++++++++++++++++++++++++++++++++-----------
Asrc/wall.jl | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/runtests.jl | 1+
Atest/wall.jl | 26++++++++++++++++++++++++++
5 files changed, 195 insertions(+), 11 deletions(-)

diff --git a/src/datatypes.jl b/src/datatypes.jl @@ -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 @@ -310,6 +322,8 @@ mutable struct Simulation atmosphere::Atmosphere Nc_max::Int + + walls::Vector{WallLinearFrictionless} end # Mappings between boundary condition keys (Integers) and strings diff --git a/src/simulation.jl b/src/simulation.jl @@ -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", @@ -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 diff --git a/src/wall.jl b/src/wall.jl @@ -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 + diff --git a/test/runtests.jl b/test/runtests.jl @@ -2,6 +2,7 @@ using Compat.Test import Granular include("grain.jl") +include("wall.jl") include("packing.jl") include("util.jl") include("temporal.jl") diff --git a/test/wall.jl b/test/wall.jl @@ -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 +