Granular.jl

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

commit cd4e192c01d957b17d2ff205151b214458f1961b
parent 22209caf17f3edd5bdf18ce66a2570d9e0cc50ed
Author: Anders Damsgaard <andersd@riseup.net>
Date:   Wed,  8 Nov 2017 11:37:29 -0500

add sedimentation example, fix render command by removing import

Diffstat:
Mdocs/src/man/getting_started.md | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Aexamples/sedimentation.jl | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/io.jl | 4----
3 files changed, 182 insertions(+), 6 deletions(-)

diff --git a/docs/src/man/getting_started.md b/docs/src/man/getting_started.md @@ -20,7 +20,7 @@ name>`. An example: ```julia-repl julia> ?Granular.fitGridToGrains! - fitGridToGrains!(simulation, grid[, padding]) + fitGridToGrains!(simulation, grid[, padding, verbose]) Fit the ocean or atmosphere grid for a simulation to the current grains and their positions. @@ -38,7 +38,7 @@ julia> ?Granular.fitGridToGrains! ``` ## Collision between two particles -For this simple example (`example/two-grains.jl`), we will create two grains, +For this simple example (`examples/two-grains.jl`), we will create two grains, where one of the grains is bumping in to the other. As the first command, we import all the Granular.jl functionality: @@ -243,6 +243,12 @@ chosen parameter under the *Glyph1* object in the *Pipeline Browser* on the left, and selecting a different field for *Coloring*. Press the *Apply* button to see the changes in effect. +**Tip:** If you have the command `pvpython` (ParaView Python) available from +the command line, you can visualize the simulation directly from the command +line without entering ParaView by the command `sim.render()`. Furthermore, if +you have the `convert` command from ImageMagick installed (`brew install +imagemagick` on macOS), the output images will be merged into an animated GIF. + ### Exercises To gain more familiarity with the simulation procedure, I suggest experimenting with the following: @@ -256,3 +262,136 @@ with the following: affected by the choice of time step length? Try setting different time step values, e.g. with `sim.time_step = 0.1234` and rerun the simulation. +## Sedimentation of grains +Grains are known to settle under gravitation in water according to *Stoke's +law*, where resistive drag acts opposite of gravity and with a magnitude +according to the squareroot of velocity difference between water and grain. + +Granular.jl offers simple fluid grids with prescribed velocity fields, and the +grains are met with drag in this grid. + +In this example (`examples/sedimentation.jl`) we will initialize a range of +grain sizes in a loose configuration, add gravity and a surrounding fluid grid, +and let the grains settle towards the bottom. + +As in the previous example, we start by creating a fluid grid: + +```julia-repl +julia> import Granular +julia> sim = Granular.createSimulation(id="sedimentation.jl") +``` + +### Creating a pseudo-random grain packing +Instead of manually adding grains one by one, we can use the +`regularPacking!()` function to add a regular grid of random-sized grains to +the simulation. Below, we specify that we want the grid of grains to be 10 +grains wide along x, and 50 grains tall along y. We also specify the grain +radii to fall between 0.02 and 0.2 m. The sizes will be drawn from a power-law +distribution, by default. + +```julia-repl +julia> Granular.regularPacking!(sim, [10, 50], 0.02, 0.2) +``` + +Since we haven't explicitly set the grain sizes for this example, we can +inspect the values by plotting a histogram of sizes: + +```julia-repl +julia> Granular.plotGrainSizeDistribution(sim) +INFO: sedimentation-grain-size-distribution.png +``` + +The output informs us that we have the plot saved as an image with the file +name `sedimentation-grain-size-distribution.png`. + +### Creating a fluid grid +We can now create a fluid (ocean) grid spanning the extent of the grains +created above: + +```julia-repl +julia> Granular.fitGridToGrains!(sim, sim.ocean) +``` + +We want the boundaries of the above grid to be impermeable for the grains, so +they stack up at the bottom. Granular.jl acknowledges the boundary types with +a confirmation message: + +```julia-repl +julia> Granular.setGridBoundaryConditions!(sim.ocean, "impermeable") +West (-x): impermeable (3) +East (+x): impermeable (3) +South (-y): impermeable (3) +North (+y): impermeable (3) +``` + +### Adding gravitational acceleration +If we started the simulation now, nothing would happen as gravity is disabled +by default. We can enable gravitational acceleration as a constant body force +for each grain (`Force = mass * acceleration`): + +```julia-repl +julia> g = [0.0, -9.8]; +julia> for grain in sim.grains + Granular.addBodyForce!(grain, grain.mass*g) + end +``` + +### Setting temporal parameters +As before, we ask the code to select a suitable computational time step based +on grain sizes and properties: + +```julia-repl +julia> Granular.setTimeStep!(sim) +INFO: Time step length t=1.6995699879716792e-5 s +``` + +We also again set the total simulation time as well as the output file +interval: + +```julia-repl +julia> Granular.setTotalTime!(sim, 5.0) +julia> Granular.setOutputFileInterval!(sim, 0.2) +``` + +### Running the simulation +We are now ready to run the simulation: + +```julia-repl +julia> Granular.run!(sim) +INFO: Output file: ./sedimentation/sedimentation.grains.1.vtu +INFO: Output file: ./sedimentation/sedimentation.ocean.1.vts +INFO: wrote status to ./sedimentation/sedimentation.status.txt + t = 0.19884968859273294/5.0 s +INFO: Output file: ./sedimentation/sedimentation.grains.2.vtu +INFO: Output file: ./sedimentation/sedimentation.ocean.2.vts +INFO: wrote status to ./sedimentation/sedimentation.status.txt + t = 0.3993989471735396/5.0 s + +... + +INFO: Output file: ./sedimentation/sedimentation.grains.25.vtu +INFO: Output file: ./sedimentation/sedimentation.ocean.25.vts +INFO: wrote status to ./sedimentation/sedimentation.status.txt + t = 4.998435334626701/5.0 s +INFO: ./sedimentation/sedimentation.py written, execute with 'pvpython /Users/ad/code/Granular-ext/examples/sedimentation/sedimentation.py' +INFO: wrote status to ./sedimentation/sedimentation.status.txt + t = 5.00001593471549/5.0 s +``` + +The output can be plotted in ParaView as discribed in the `two-grain` example +above, or, if `pvpython` is available from the command line, directly from +Julia with the following command: + +```julia-repl +julia> Granular.render(sim, trim=false) +``` + +### Exercises +- How are the granular contact pressures distributed in the final result? +- Try running the above example, but without fluid drag. Disable the drag by + including the call `Granlar.disableOceanDrag!(grain)` in the `for` loop + where gravitational acceleration is set for each grain. +- How does the range of grain sizes affect the result? Try making all grains + bigger or smaller. +- How is the model performance effected if the grain-size distribution is + wide or narrow? diff --git a/examples/sedimentation.jl b/examples/sedimentation.jl @@ -0,0 +1,41 @@ +#/usr/bin/env julia +import Granular + +#### Create a loose granular assemblage and let it settle at towards -y +sim = Granular.createSimulation(id="sedimentation") + +# Generate 10 grains along x and 50 grains along y, with radii between 0.2 and +# 1.0 m. +Granular.regularPacking!(sim, [10, 50], 0.02, 0.2) + +# Visualize the grain-size distribution +Granular.plotGrainSizeDistribution(sim) + +# Create a grid for contact searching spanning the extent of the grains in the +# simulation +Granular.fitGridToGrains!(sim, sim.ocean) + +# Make the grid boundaries impermeable for the grains, which +Granular.setGridBoundaryConditions!(sim.ocean, "impermeable") + +# Add gravitational acceleration to all grains +g = [0., -9.8] +for grain in sim.grains + Granular.addBodyForce!(grain, grain.mass*g) +end + +# Automatically set the computational time step based on grain sizes and +# properties +Granular.setTimeStep!(sim) + +# Set the total simulation time for this step [s] +Granular.setTotalTime!(sim, 5.0) + +# Set the interval in model time between simulation files [s] +Granular.setOutputFileInterval!(sim, 0.2) + +# Start the simulation +Granular.run!(sim) + +# Try to render the simulation if `pvpython` is installed on the system +Granular.render(sim, trim=false) diff --git a/src/io.jl b/src/io.jl @@ -947,10 +947,6 @@ function render(simulation::Simulation; pvpython::String="pvpython", # use ImageMagick installed with Homebrew.jl if available, # otherwise search for convert in $PATH convert = "convert" - if is_apple() - import Homebrew - convert = Homebrew.prefix() * "/bin/convert" - end run(`$convert $trim_string +repage -delay 10 -transparent-color white