timplement several changes to file IO, use Gnuplot for plotting - 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 8b1cd340b84b6f8c0481f128f91a054872906aa8
 (DIR) parent 24ac44e780e53040fa737ea47821045f97588acd
 (HTM) Author: Anders Damsgaard <andersd@riseup.net>
       Date:   Fri, 17 Nov 2017 13:24:47 -0500
       
       implement several changes to file IO, use Gnuplot for plotting
       
       Diffstat:
         A deps/build.jl                       |      41 +++++++++++++++++++++++++++++++
         M src/grain.jl                        |      57 +++++++++++++++++++++----------
         M src/io.jl                           |      77 +++++++++++++++++++++++++++----
         M test/grain.jl                       |      25 +++++++++++--------------
         M test/jld.jl                         |      10 ++++++++++
       
       5 files changed, 169 insertions(+), 41 deletions(-)
       ---
 (DIR) diff --git a/deps/build.jl b/deps/build.jl
       t@@ -0,0 +1,41 @@
       +
       +#!/usr/bin/env julia
       +using BinDeps
       +@BinDeps.setup
       +
       +import Compat
       +import Compat.Sys
       +
       +gnuplot = library_dependency("gnuplot")
       +imagemagick = library_dependency("imagemagick", aliases = ["ImageMagick"])
       +
       +# Debian derivatives: https://www.debian.org/distrib/packages#search_packages
       +provides(AptGet, "gnuplot", gnuplot, os = :Linux)
       +provides(AptGet, "imagemagick", imagemagick, os = :Linux)
       +
       +# RHEL derivatives: http://rpm.pbone.net/index.php3/stat/2/simple/2
       +provides(Yum, "gnuplot", gnuplot, os = :Linux)
       +provides(Yum, Dict("ImageMagick" => imagemagick), os = :Linux)
       +
       +# Arch: https://www.archlinux.org/packages/
       +provides(Pacman, "gnuplot", gnuplot, os = :Linux)
       +provides(Pacman, "imagemagick", imagemagick, os = :Linux)
       +
       +# Mac: http://formulae.brew.sh/
       +if Compat.Sys.isapple()
       +    using Homebrew
       +    provides(Homebrew.HB, "gnuplot", gnuplot, os = :Darwin)
       +    provides(Homebrew.HB, "imagemagick", imagemagick, os = :Darwin)
       +end
       +
       +# Windows: http://software.opensuse.org/search
       +if Compat.Sys.iswindows()
       +    using WinRPM
       +    provides(WinRPM.RPM, "gnuplot", gnuplot, os = :Windows)
       +    provides(WinRPM.RPM, "imagemagick", imagemagick, os = :Windows)
       +end
       +
       +@BinDeps.install Dict([
       +                       (:gnuplot => :gnuplot),
       +                       (:imagemagick => :imagemagick),
       +                      ])
 (DIR) diff --git a/src/grain.jl b/src/grain.jl
       t@@ -1,12 +1,6 @@
        ## Manage grains in the model
        using Compat.Test
        
       -hasPyPlot = false
       -if typeof(Pkg.installed("PyPlot")) == VersionNumber
       -    import PyPlot
       -    hasPyPlot = true
       -end
       -
        export addGrainCylindrical!
        """
            function addGrainCylindrical!(simulation, lin_pos, contact_radius,
       t@@ -742,7 +736,8 @@ string, and the `filetype`, and is written to the current folder.
            true).
        * `skip_fixed::Bool`: ommit grains that are fixed in space from the size 
            distribution (default = true).
       -* `logy::Bool`: plot y-axis in log scale.
       +* `log_y::Bool`: plot y-axis in log scale.
       +* `show_plot::Bool`: show plot in pop-up window in addition to writing to disk.
        """
        function plotGrainSizeDistribution(simulation::Simulation;
                                             filename_postfix::String = "",
       t@@ -752,12 +747,9 @@ function plotGrainSizeDistribution(simulation::Simulation;
                                             filetype::String = "png",
                                             verbose::Bool = true,
                                             skip_fixed::Bool = true,
       -                                     log_y::Bool = true)
       +                                     log_y::Bool = false,
       +                                     show_plot::Bool = false)
        
       -    if !hasPyPlot
       -        error("Function not available because PyPlot is not installed")
       -        return
       -    end
            diameters = Float64[]
            for i=1:length(simulation.grains)
                if simulation.grains[i].fixed && skip_fixed
       t@@ -771,14 +763,43 @@ function plotGrainSizeDistribution(simulation::Simulation;
                    error("size_type '$size_type' not understood")
                end
            end
       -    PyPlot.pygui(false)
       -    PyPlot.figure(figsize=figsize)
       -    PyPlot.plt[:hist](diameters, nbins, log=log_y)
       -    PyPlot.xlabel("Diameter [m]")
       -    PyPlot.ylabel("Count [-]")
       +
            filename = string(simulation.id * filename_postfix * 
                              "-grain-size-distribution." * filetype)
       -    PyPlot.savefig(filename)
       +
       +    # write data to temporary file on disk
       +    datafile = Base.Filesystem.tempname()
       +    writedlm(datafile, diameters)
       +    gnuplotscript = Base.Filesystem.tempname()
       +
       +    open(gnuplotscript, "w") do f
       +
       +        write(f, """#!/usr/bin/env gnuplot
       +              set out "$(filename)"
       +              set xlabel "Diameter [m]"
       +              set ylabel "Count [-]"
       +              set style histogram
       +              plot "$(datafile)"
       +              """)
       +    end
       +
       +    try
       +        run(`gnuplot $gnuplotscript`)
       +    catch return_signal
       +        if isa(return_signal, Base.UVError)
       +            error("Could not launch external gnuplot process")
       +        end
       +    end
       +
       +
       +    xlabel = "Diameter [m]"
       +    ylabel = "Count [-]"
       +    if log_y
       +        error("Logarithmic axis scaling isn't yet supported with GR histograms")
       +        # try anyway
       +        GR.setscale(GR.OPTION_Y_LOG)
       +        GR.setscale(GR.OPTION_FLIP_X)
       +    end
            if verbose
                info(filename)
            end
 (DIR) diff --git a/src/io.jl b/src/io.jl
       t@@ -50,9 +50,14 @@ export readSimulation
            readSimulation(filename::String="";
                           verbose::Bool=true)
        
       -Read all content from `Simulation` from disk in JDL format.
       +Return `Simulation` content read from disk using the JDL format.
       +
       +# Arguments
       +* `filename::String`: path to file on disk containing the simulation
       +    information.
       +* `verbose::Bool=true`: confirm to console that the file has been read.
        """
       -function readSimulation(filename::String="";
       +function readSimulation(filename::String;
                                 verbose::Bool=true)
            if !hasJLD
                warn("Package JLD not found. Simulation save/read not supported. " * 
       t@@ -60,8 +65,42 @@ function readSimulation(filename::String="";
                     "requirements with `Pkg.add(\"JLD\")`.")
                nothing
            else
       +        return JLD.load(filename, "simulation")
       +        if verbose
       +            info("Read simulation from $filename")
       +        end
       +    end
       +end
       +"""
       +    readSimulation(simulation::Simulation;
       +                   step::Integer = -1,
       +                   verbose::Bool = true)
       +
       +Read the simulation state from disk and return as new simulation object.
       +
       +# Arguments
       +* `simulation::Simulation`: use the `simulation.id` to determine the file name
       +    to read from, and read information from the file into this object.
       +* `step::Integer=-1`: attempt to read this output file step. At its default
       +    value (`-1`), the function will try to read the latest file, determined by
       +    calling [`readSimulationStatus`](@ref).
       +* `verbose::Bool=true`: confirm to console that the file has been read.
       +"""
       +function readSimulation(simulation::Simulation;
       +                         step::Integer = -1,
       +                         verbose::Bool = true)
       +    if !hasJLD
       +        warn("Package JLD not found. Simulation save/read not supported. " * 
       +             "Please install JLD and its " *
       +             "requirements with `Pkg.add(\"JLD\")`.")
       +        nothing
       +    else
       +        if step == -1
       +            step = readSimulationStatus(simulation)
       +        end
       +        filename = string(simulation.id, "/", simulation.id, ".$step.jld")
                if verbose
       -            info("reading simulation from $filename")
       +            info("Read simulation from $filename")
                end
                return JLD.load(filename, "simulation")
            end
       t@@ -86,18 +125,22 @@ function writeSimulationStatus(simulation::Simulation;
                                simulation.time/simulation.time_total*100.
                                float(simulation.file_number)])
            if verbose
       -        info("wrote status to $filename")
       +        info("Wrote status to $filename")
            end
            nothing
        end
        
        export readSimulationStatus
        """
       -    readSimulationStatus(filename::String;
       -                         folder::String=".",
       -                         verbose::Bool=false)
       +    readSimulationStatus(simulation_id[, folder, verbose])
        
       -Write current simulation status to disk in a minimal txt file.
       +Read the current simulation status from disk (`<sim.id>/<sim.id>.status.txt`)
       +and return the last output file number.
       +
       +# Arguments
       +* `simulation_id::String`: the simulation identifying string.
       +* `folder::String="."`: the folder in which to search for the status file.
       +* `verbose::Bool=true`: show simulation status in console.
        """
        function readSimulationStatus(simulation_id::String;
                                      folder::String=".",
       t@@ -113,7 +156,23 @@ function readSimulationStatus(simulation_id::String;
                     "  complete:         $(data[2])%\n" *
                     "  last output file: $(Int(round(data[3])))\n")
            end
       -    return data[3]
       +    return Int(round(data[3]))
       +"""
       +    readSimulationStatus(simulation[, folder, verbose])
       +
       +Read the current simulation status from disk (`<sim.id>/<sim.id>.status.txt`)
       +and return the last output file number.
       +
       +# Arguments
       +* `simulation::Simulation`: the simulation to read the status for.
       +* `folder::String="."`: the folder in which to search for the status file.
       +* `verbose::Bool=true`: show simulation status in console.
       +"""
       +end
       +function readSimulationStatus(sim::Simulation;
       +                              folder::String=".",
       +                              verbose::Bool=true)
       +    readSimulationStatus(sim.id, folder=folder, verbose=verbose)
        end
        
        export status
 (DIR) diff --git a/test/grain.jl b/test/grain.jl
       t@@ -30,20 +30,17 @@ Granular.addGrainCylindrical!(sim, [ 0., 0.], 10., 1., verbose=false)
        Granular.addGrainCylindrical!(sim, [ 0., 0.], 10., 1., verbose=false)
        Granular.compareGrains(sim.grains[1], sim.grains[2])
        
       -if typeof(Pkg.installed("PyPlot")) == VersionNumber
       -    info("Testing GSD plotting ")
       -    Granular.plotGrainSizeDistribution(sim)
       -    rm("test-grain-size-distribution.png")
       -    Granular.plotGrainSizeDistribution(sim, skip_fixed=false)
       -    rm("test-grain-size-distribution.png")
       -    Granular.plotGrainSizeDistribution(sim, log_y=false)
       -    rm("test-grain-size-distribution.png")
       -    Granular.plotGrainSizeDistribution(sim, size_type="areal")
       -    rm("test-grain-size-distribution.png")
       -    @test_throws ErrorException Granular.plotGrainSizeDistribution(sim, size_type="asdf")
       -else
       -    @test_throws ErrorException Granular.plotGrainSizeDistribution(sim)
       -end
       +info("Testing GSD plotting ")
       +Granular.plotGrainSizeDistribution(sim)
       +@test isfile("test-grain-size-distribution.png")
       +rm("test-grain-size-distribution.png")
       +Granular.plotGrainSizeDistribution(sim, skip_fixed=false)
       +@test isfile("test-grain-size-distribution.png")
       +rm("test-grain-size-distribution.png")
       +Granular.plotGrainSizeDistribution(sim, size_type="areal")
       +@test isfile("test-grain-size-distribution.png")
       +rm("test-grain-size-distribution.png")
       +@test_throws ErrorException Granular.plotGrainSizeDistribution(sim, size_type="asdf")
        
        info("Testing external body force routines")
        sim = Granular.createSimulation(id="test")
 (DIR) diff --git a/test/jld.jl b/test/jld.jl
       t@@ -15,7 +15,17 @@ if typeof(Pkg.installed("JLD")) == VersionNumber
            Granular.writeVTK(sim, verbose=false)
        
            Granular.writeSimulation(sim)
       +    Granular.writeSimulationStatus(sim)
        
       +    info("Reading from JLD file by specifying the input file name")
            sim2 = Granular.readSimulation("./test/test.1.jld")
            Granular.compareSimulations(sim, sim2)
       +
       +    info("Reading and overwriting from JLD file by simulation id")
       +    sim3 = Granular.createSimulation("test")
       +    @test 1 == Granular.readSimulationStatus(sim3)
       +    sim3 = Granular.readSimulation(sim3)
       +    Granular.compareSimulations(sim, sim3)
       +
       +    rm("./test/test.1.jld")
        end