Granular.jl

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

commit 08b8db16d56247139487ea246241ae7493c2cb32
parent 89e656eb476345cf8349c6904bbf08f315ba136e
Author: Anders Damsgaard <andersd@riseup.net>
Date:   Wed,  8 Nov 2017 22:20:56 -0600

add PyPlot as a requirement and update the documentation for clarity

Diffstat:
MREQUIRE | 1+
Mdocs/src/man/getting_started.md | 160++++++++++++++++++++++++++++++++++++++++---------------------------------------
2 files changed, 83 insertions(+), 78 deletions(-)

diff --git a/REQUIRE b/REQUIRE @@ -2,6 +2,7 @@ julia 0.6 WriteVTK NetCDF Documenter +PyPlot BinDeps 0.2.12- Compat 0.9.1 @osx Homebrew 0.0.4- diff --git a/docs/src/man/getting_started.md b/docs/src/man/getting_started.md @@ -8,11 +8,10 @@ commands of Granular.jl. For more examples, see the scripts in the `examples/` directory. The relevant functions are all contained in the `Granular` module, which can be -imported with `import Granular` at the top of your script. *Note:* As per -Julia conventions, functions that contain an exclamation mark (!) modify the -values of the arguments. +imported with `import Granular`. *Note:* As per Julia conventions, functions +that contain an exclamation mark (!) modify the values of the arguments. -Any of the functions called below are documented in the source code, and this +Any of the functions called below are documented in the source code, and their documentation can be found in the [Public API Index](@ref main-index) in the online documentation, or simply from the Julia shell by typing `?<function name>`. An example: @@ -22,35 +21,33 @@ julia> ?Granular.fitGridToGrains! fitGridToGrains!(simulation, grid[, padding, verbose]) - Fit the ocean or atmosphere grid for a simulation to the current grains and their positions. + Fit the ocean or atmosphere grid for a simulation to the current grains and + their positions. Arguments ≡≡≡≡≡≡≡≡≡≡≡ • simulation::Simulation: simulation object to manipulate. - • grid::Any: Ocean or Atmosphere grid to manipulate. - • padding::Real: optional padding around edges [m]. - • verbose::Bool: show grid information when function completes. ``` ## Collision between two particles -For this simple example (`examples/two-grains.jl`), we will create two grains, -where one of the grains is bumping in to the other. +For the first example (`examples/two-grains.jl`), we will create two grains and +make the first grain bump in to the second grain. -As the first command, we import all the Granular.jl functionality: +As the first step, we import all the Granular.jl functionality: ```julia-repl julia> import Granular ``` ### Simulation setup -Next, we create our simulation object which holds all information on the -simulated grains. This object can be called whatever is appropriate. In this -documentation, we will use the name `sim`: +Next, we create our simulation object which holds all information related to +the simulated grains. In this documentation, we will use the name `sim` for +the simulation object: ```julia-repl julia> sim = Granular.createSimulation(id="two-grains") @@ -61,21 +58,21 @@ Granular.GrainCylindrical[], Granular.Ocean(false, [0.0], [0.0], [0.0], [0.0], [0.0], [0.0], Array{Int64,1}[#undef], 1, 1, 1, 1, false), 16) ``` -We will be presented with some output about the contents of the `sim` -simulation object. This is of minor importance as of now, and can safely be -ignored. +After creating the simulation object `sim`, we will be presented with some +output about the default contents of the `sim` simulation object. This is of +minor importance as of now, and can safely be ignored. -During the above `createSimulation` call, the `id` argument is optional, but is -used to name simulation output files that are written to the disk. It is good -practice to use the same name as used for the simulation script file. +In the above `createSimulation` call, the `id` argument is optional. It is used +to name simulation output files that are written to the disk. ### Adding grains one by one -We have now created a simulation object, which will be used during all of the -following. Next, we add grains to this object. The first grain is cylinder -shaped, placed at the x-y position (0,0) m, has a radius of 0.1 m, and a -thickness of 0.05 m (along z). As this call modifies the `sim` object, the -function contains an exclamation mark (!). For further information regarding -this call, see the reference in the [Public API Index](@ref main-index). +We have now created a simulation object `sim`, which will be used during all of +the following commands. Next, we can add grains to this object. The first +grain is cylinder shaped, placed at the x-y position (0,0) m. It has a radius +of 0.1 m, and has a thickness of 0.05 m. As this call modifies the `sim` +object, the function contains an exclamation mark (!). For further information +regarding this call, see the reference in the [Public API Index](@ref +main-index). ```julia-repl julia> Granular.addGrainCylindrical!(sim, [0.0, 0.0], 0.1, 0.05) @@ -90,7 +87,7 @@ INFO: Added Grain 2 ``` We now want to prescribe a linear (not rotational or angular) velocity to the -first grain, to make it bump into the second grain. +first grain to make it bump into the second grain. The simulation object `sim` contains an array of all grains that are added to it. We can directly inspect the grains and their values from the simulation @@ -104,8 +101,9 @@ julia> sim.grains[1].lin_vel 0.0 ``` -The default value is a 0,0 vector. Similarly, we can modify the properties of -the first grain directly with the following call: +The default value is a (0,0) vector, which means that it is not moving in +space. With a similar call, we can modify the properties of the first grain +directly and prescribe a velocity to it: ```julia-repl julia> sim.grains[1].lin_vel = [1.0, 0.0] @@ -118,25 +116,25 @@ The first grain (index 1 in `sim.grains`) now has a positive velocity along `x` with the value of 1.0 meter per second. ### Setting temporal parameters for the simulation -Before we can start the simulation, we need to tell the code vital information, -like what time step to use, how often to write output files to the disk, and -for how long to run the simulation. To set the computational time step, we -call the following: +Before we can start the simulation we need to set some more parameters vital to +the simulation, like what time step to use, how often to write output files to +the disk, and for how long to run the simulation. To set the computational +time step, we call the following: ```julia-repl julia> Granular.setTimeStep!(sim) INFO: Time step length t=8.478741928617433e-5 s ``` -Based on the elastic stiffness of the grains and their size, a suitable time -step is automatically determined. +A suitable time step is automatically determined based on the size and elastic +properties of the grain. The two grains are 0.3 meters apart, as the centers are placed 0.5 meter from -each other and each grain has a diameter of 0.1 m. With a linear velocity of -1.0 m/s, the collision should occur after 0.3 seconds. For that reason it -seems reasonable to run the simulation for a total of 1.0 seconds, and produce -an output file every 0.05 seconds. We will later use the output files for -visualization purposes. +each other and each grain has a radius of 0.1 m. With a linear velocity of 1.0 +m/s, the collision should occur after 0.3 seconds. For that reason it seems +reasonable to run the simulation for a total of 1.0 seconds. We choose to +produce an output file every 0.05 seconds, but this can be tweaked to taste. +We will later use the produced output files for visualization purposes. ```julia-repl julia> Granular.setOutputFileInterval!(sim, 0.05) @@ -147,7 +145,7 @@ julia> Granular.setTotalTime!(sim, 1.0) ### Running the simulation We are now ready to run the simulation. For illustrative purposes, let us compare the kinetic energy in the granular system before and after the -collision. For now, we save the initial value using the following call: +collision. For now, we compute the initial value using the following call: ```julia-repl julia> Granular.totalGrainKineticTranslationalEnergy(sim) @@ -157,7 +155,7 @@ julia> Granular.totalGrainKineticTranslationalEnergy(sim) The above value is the total translational (not angular) kinetic energy in Joules before the simulation is started. -For running the simulation, we have two choices; we can either run the entire +We have two choices for running the simulation; we can either run the entire simulation length with a single call, which steps time until the total time is reached and generates output files along the way. Alternatively, we can run the simulation for a single time step a time, and inspect the progress or do @@ -185,13 +183,15 @@ INFO: wrote status to ./two-grains/two-grains.status.txt t = 1.0000676104805686/1.0 s ``` -The script informs us of the simulation progress, and notifies us as output -files are generated (this can be disabled by passing `verbose=false` to the -`run!()` command). Finally, it tells us that it has generated a ParaView -python file for visualization. +The code informs us of the simulation progress along the way. It also and +notifies us as output files are generated. This output can be disabled by +passing `verbose=false` to the `run!()` command. Finally, the code tells us +that it has generated a ParaView python file for visualization, called +`two-grains.py`, located in the `two-grains/` directory. -Before going further, we are interested in getting an immediate idea of how the -collision went. We print the new velocities with the following commands: +We are interested in getting an immediate idea of how the collision went, +before going further. We can print the new velocities with the following +commands: ```julia-repl julia> sim.grains[1].lin_vel @@ -214,8 +214,9 @@ julia> Granular.totalGrainKineticTranslationalEnergy(sim) 0.7334506347624973 ``` -The before and after values are reasonably close (to less than 0.1 percent), -which is what can be expected given the computation accuracy in the algorithm. +The before and after values for total kinetic energy are reasonably close (to +less than 0.1 percent), which is what can be expected given the computation +accuracy of the algorithm. ### Visualizing the output To visualize the output we open [ParaView](https://www.paraview.org). The @@ -229,24 +230,28 @@ simulation by Granular.jl. Open ParaView and open the *Python Shell*, found under the menu *Tools > Python Shell*. In the pop-up dialog we select *Run Script*, which opens yet another -dialog prompting us to locate the visualization script (`two-grains.py`, in our -example). We locate this file, which is placed under the directory from where -we launched the `julia` session with the commands above. +dialog prompting us to locate the visualization script +(`two-grains/two-grains.py` in our example). We locate this file, which is +placed under the directory from where we launched the `julia` session with the +commands above. If you are not sure what the current working directory for the +Julia session is, it can be displayed with the command `pwd()` in the Julia +shell. After selecting the `two-grains/two-grains.py` script, we can close the *Python -Shell* window to inspect our simulation. Press the *Play* symbol in the top -toolbar, and see what happens! - -Alternatively, you can color the grains using different parameters, such as -velocity, number of contacts, etc. These can be selected by changing the -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()`. The program -`pvpython` is included in the ParaView download, and is in the macOS +Shell* window to inspect our simulation visually. Press the *Play* symbol in +the top toolbar, and see what happens. + +By default, the grains are colored according to their size. Alternatively, you +can color the grains using different parameters, such as linear velocity, +number of contacts, etc. These parameters can be selected by changing the +chosen field under the *Glyph1* object in the *Pipeline Browser* on the left, +and by selecting a parameter for *Coloring*. Press the *Apply* button at the +top of the panel on the left to see the changes in effect. + +**Tip:** If you have the command `pvpython` (ParaView Python) is available from +the command line, you can visualize the simulation directly from the Julia +shell without entering ParaView by the command `Granular.render(sim)`. The +program `pvpython` is included in the ParaView download, and is in the macOS application bundle located in `/Applications/Paraview-5.4.0.app/Contents/bin/pvpython`. Furthermore, if you have the `convert` command from ImageMagick installed (`brew install @@ -259,7 +264,7 @@ with the following: - What effect does the grain size have on the time step? - Try to make an oblique collision by placing one of the grains at a different `y` position. -- What happens if the second grains is set to be fixed in space +- What happens if the second grain is set to be fixed in space (`sim.grains[2].fixed = true`)? - How is the relationship between total kinetic energy before and after affected by the choice of time step length? Try setting different time @@ -268,14 +273,13 @@ with the following: ## 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. - +according to the square root 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. +In this example (`examples/sedimentation.jl`) we will initialize grains with 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: @@ -290,7 +294,7 @@ Instead of manually adding grains one by one, we can use the the simulation. Below, we specify that we want the grid of grains to be 7 grains wide along x, and 25 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. +distribution by default. ```julia-repl julia> Granular.regularPacking!(sim, [7, 25], 0.02, 0.2) @@ -334,9 +338,9 @@ 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`): +If we start 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]; @@ -354,7 +358,7 @@ 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 +We also, as before, set the total simulation time as well as the output file interval: ```julia-repl @@ -387,7 +391,7 @@ INFO: wrote status to ./sedimentation/sedimentation.status.txt t = 10.00001593471549/10.0 s ``` -The output can be plotted in ParaView as discribed in the `two-grain` example +The output can be plotted in ParaView as described in the `two-grain` example above, or, if `pvpython` is available from the command line, directly from Julia with the following command: