Granular.jl

Julia package for granular dynamics simulation
git clone git://src.adamsgaard.dk/Granular.jl # fast
git clone https://src.adamsgaard.dk/Granular.jl.git # slow
Log | Files | Refs | README | LICENSE Back to index

simulation.jl (13648B)


      1 using Printf
      2 
      3 ## General simulation functions
      4 
      5 export createSimulation
      6 """
      7     createSimulation([id])
      8 
      9 Create a simulation object to contain all relevant variables such as temporal 
     10 parameters, fluid grids, grains, and contacts.  The parameter `id` is used to
     11 uniquely identify the simulation when it is written to disk.
     12 
     13 The function returns a `Simulation` object, which you can add grains to, e.g.
     14 with [`addGrainCylindrical!`](@ref).
     15 
     16 # Optional argument
     17 * `id::String="unnamed"`: simulation identifying string.
     18 
     19 """
     20 function createSimulation(;id::String="unnamed")
     21 
     22     # default values for simulation parameters (not included as arguments as
     23     # they are very rarely chagned and make the docstring much more cluttered
     24     # and intimidating
     25     time_iteration::Int = 0
     26     time::Float64 = 0.0
     27     time_total::Float64 = -1.
     28     time_step::Float64 = -1.
     29     file_time_step::Float64 = -1.
     30     file_number::Int = 0
     31     file_time_since_output_file::Float64 = 0.
     32     grains = Array{GrainCylindrical, 1}[]
     33     ocean::Ocean = createEmptyOcean()
     34     atmosphere::Atmosphere = createEmptyAtmosphere()
     35     Nc_max::Int = 32
     36     walls = Array{WallLinearFrictionless, 1}[]
     37 
     38     return Simulation(id,
     39                       time_iteration,
     40                       time,
     41                       time_total,
     42                       time_step,
     43                       file_time_step,
     44                       file_number,
     45                       file_time_since_output_file,
     46                       grains,
     47                       ocean,
     48                       atmosphere,
     49                       Nc_max,
     50                       walls)
     51 end
     52 function createSimulation(id::String)
     53     createSimulation(id=id)
     54 end
     55 
     56 export run!
     57 """
     58     run!(simulation[,
     59          verbose::Bool = true,
     60          status_interval = 100.,
     61          show_file_output = true,
     62          single_step = false,
     63          temporal_integration_method = "Three-term Taylor"],
     64          write_jld2 = false)
     65 
     66 Run the `simulation` through time until `simulation.time` equals or exceeds 
     67 `simulatim.time_total`.  This function requires that all grains are added to 
     68 the simulation and that the length of the computational time step is adjusted 
     69 accordingly.
     70 
     71 The function will search for contacts, determine the force balance on each ice 
     72 floe, and integrate all kinematic degrees of freedom accordingly.  The temporal 
     73 integration is explicit and of length `simulation.time_step`.  This function 
     74 will write VTK files to disk in the intervals `simulation.file_time_step` by the 
     75 function `writeVTK`.  If this value is negative, no output files will be written 
     76 to disk.
     77 
     78 # Arguments
     79 * `simulation::Simulation`: the simulation to run (object is modified)
     80 * `verbose::Bool=true`: show verbose information during the time loop
     81 * `status_interval::Bool=true`: show verbose information during the time loop
     82 * `show_file_output::Bool=true`: report to stdout when output file is written
     83 * `single_step::Bool=false`: run simulation for a single time step only.  If 
     84     this causes `simulation.time` to exceed `simulation.time_total`, the latter 
     85     is increased accordingly.
     86 * `temporal_integration_method::String="Three-term Taylor"`: type of integration 
     87     method to use.  See `updateGrainKinematics` for details.
     88 * `write_jld2::Bool=false`: write simulation state to disk as JLD2 files (see 
     89     `Granular.writeSimulation(...)` whenever saving VTK output.
     90 """
     91 function run!(simulation::Simulation;
     92               verbose::Bool=true,
     93               status_interval::Int=100,
     94               show_file_output::Bool=true,
     95               single_step::Bool=false,
     96               temporal_integration_method::String="Three-term Taylor",
     97               write_jld2::Bool=false)
     98 
     99     if single_step && simulation.time >= simulation.time_total
    100         simulation.time_total += simulation.time_step
    101     end
    102 
    103     checkTimeParameters(simulation, single_step=single_step)
    104 
    105     # if both are enabled, check if the atmosphere grid spatial geometry is 
    106     # identical to the ocean grid
    107     if simulation.time_iteration == 0 &&
    108         typeof(simulation.atmosphere.input_file) != Bool &&  
    109         typeof(simulation.ocean.input_file) != Bool
    110 
    111         if simulation.ocean.xq ≈ simulation.atmosphere.xq &&
    112             simulation.ocean.yq ≈ simulation.atmosphere.yq
    113             if verbose
    114                 @info "identical ocean and atmosphere grids, " *
    115                     "turning on grid optimizations"
    116             end
    117             simulation.atmosphere.collocated_with_ocean_grid = true
    118         end
    119     end
    120 
    121 
    122     # number of time steps between output files
    123     n_file_time_step = Int(ceil(simulation.file_time_step/simulation.time_step))
    124 
    125     while simulation.time <= simulation.time_total
    126 
    127         if simulation.file_time_step > 0.0 &&
    128             simulation.time_iteration % n_file_time_step == 0
    129 
    130             if show_file_output
    131                 println()
    132             end
    133             if write_jld2
    134                 writeSimulation(simulation, verbose=show_file_output)
    135             end
    136             writeVTK(simulation, verbose=show_file_output)
    137             writeSimulationStatus(simulation, verbose=show_file_output)
    138             simulation.file_time_since_output_file = 0.0
    139         end
    140 
    141         if verbose && simulation.time_iteration % status_interval == 0
    142             reportSimulationTimeToStdout(simulation)
    143         end
    144 
    145         zeroForcesAndTorques!(simulation)
    146 
    147         if typeof(simulation.atmosphere.input_file) != Bool && 
    148             !simulation.atmosphere.collocated_with_ocean_grid
    149             sortGrainsInGrid!(simulation, simulation.atmosphere)
    150         end
    151 
    152         if typeof(simulation.ocean.input_file) != Bool
    153             sortGrainsInGrid!(simulation, simulation.ocean)
    154             findContacts!(simulation, method="ocean grid")
    155 
    156             if simulation.atmosphere.collocated_with_ocean_grid
    157                 copyGridSortingInfo!(simulation.ocean, simulation.atmosphere,
    158                                      simulation.grains)
    159             end
    160 
    161         elseif typeof(simulation.atmosphere.input_file) != Bool
    162             findContacts!(simulation, method="atmosphere grid")
    163 
    164         else
    165             findContacts!(simulation, method="all to all")
    166         end
    167 
    168         interact!(simulation)
    169         interactWalls!(simulation)
    170 
    171         if typeof(simulation.ocean.input_file) != Bool
    172             addOceanDrag!(simulation)
    173         end
    174 
    175         if typeof(simulation.atmosphere.input_file) != Bool
    176             addAtmosphereDrag!(simulation)
    177         end
    178 
    179         updateGrainKinematics!(simulation, method=temporal_integration_method)
    180         updateWallKinematics!(simulation, method=temporal_integration_method)
    181 
    182         # Update time variables
    183         simulation.time_iteration += 1
    184         incrementCurrentTime!(simulation, simulation.time_step)
    185 
    186         if single_step
    187             return nothing
    188         end
    189     end
    190 
    191     if simulation.file_time_step > 0.0
    192         if show_file_output
    193             println()
    194         end
    195         writeParaviewPythonScript(simulation, verbose=show_file_output)
    196         writeSimulationStatus(simulation, verbose=show_file_output)
    197     end
    198 
    199     if verbose
    200         reportSimulationTimeToStdout(simulation)
    201         println()
    202     end
    203     nothing
    204 end
    205 
    206 export addGrain!
    207 """
    208     addGrain!(simulation::Simulation,
    209               grain::GrainCylindrical,
    210               verbose::Bool = false)
    211 
    212 Add an `grain` to the `simulation` object.  If `verbose` is true, a short 
    213 confirmation message will be printed to stdout.
    214 """
    215 function addGrain!(simulation::Simulation,
    216                    grain::GrainCylindrical,
    217                    verbose::Bool = false)
    218     push!(simulation.grains, grain)
    219 
    220     if verbose
    221         @info "Added grain $(length(simulation.grains))"
    222     end
    223     nothing
    224 end
    225 
    226 export addWall!
    227 """
    228     addWall!(simulation::Simulation,
    229              wall::WallLinearFrictionless,
    230              verbose::Bool = false)
    231 
    232 Add an `wall` to the `simulation` object.  If `verbose` is true, a short 
    233 confirmation message will be printed to stdout.
    234 """
    235 function addWall!(simulation::Simulation,
    236                   wall::WallLinearFrictionless,
    237                   verbose::Bool = false)
    238     push!(simulation.walls, wall)
    239 
    240     if verbose
    241         @info "Added wall $(length(simulation.walls))"
    242     end
    243     nothing
    244 end
    245 
    246 export disableGrain!
    247 "Disable grain with index `i` in the `simulation` object."
    248 function disableGrain!(simulation::Simulation, i::Int)
    249     if i < 1
    250         error("Index must be greater than 0 (i = $i)")
    251     end
    252     simulation.grains[i].enabled = false
    253     nothing
    254 end
    255 
    256 export zeroForcesAndTorques!
    257 "Sets the `force` and `torque` values of all grains and dynamic walls to zero."
    258 function zeroForcesAndTorques!(simulation::Simulation)
    259     for grain in simulation.grains
    260         grain.force .= grain.external_body_force
    261         grain.torque = zeros(3)
    262         grain.pressure = 0.
    263     end
    264     for wall in simulation.walls
    265         wall.force = 0.
    266     end
    267     nothing
    268 end
    269 
    270 export reportSimulationTimeToStdout
    271 "Prints the current simulation time and total time to standard out"
    272 function reportSimulationTimeToStdout(simulation::Simulation)
    273     print("\r  t = ", simulation.time, '/', simulation.time_total,
    274           " s            ")
    275     nothing
    276 end
    277 
    278 export compareSimulations
    279 """
    280     compareSimulations(sim1::Simulation, sim2::Simulation)
    281 
    282 Compare values of two `Simulation` objects using the `Base.Test` framework.
    283 """
    284 function compareSimulations(sim1::Simulation, sim2::Simulation)
    285 
    286     Test.@test sim1.id == sim2.id
    287 
    288     Test.@test sim1.time_iteration == sim2.time_iteration
    289     Test.@test sim1.time ≈ sim2.time
    290     Test.@test sim1.time_total ≈ sim2.time_total
    291     Test.@test sim1.time_step ≈ sim2.time_step
    292     Test.@test sim1.file_time_step ≈ sim2.file_time_step
    293     Test.@test sim1.file_number == sim2.file_number
    294     Test.@test sim1.file_time_since_output_file ≈ sim2.file_time_since_output_file
    295 
    296     for i=1:length(sim1.grains)
    297         compareGrains(sim1.grains[i], sim2.grains[i])
    298     end
    299     compareOceans(sim1.ocean, sim2.ocean)
    300     compareAtmospheres(sim1.atmosphere, sim2.atmosphere)
    301 
    302     Test.@test sim1.Nc_max == sim2.Nc_max
    303     nothing
    304 end
    305 
    306 export printMemoryUsage
    307 """
    308     printMemoryUsage(sim::Simulation)
    309 
    310 Shows the memory footprint of the simulation object.
    311 """
    312 function printMemoryUsage(sim::Simulation)
    313     
    314     reportMemory(sim, "sim")
    315     println("  where:")
    316 
    317     reportMemory(sim.grains, "    sim.grains", 
    318                  "(N=$(length(sim.grains)))")
    319 
    320     reportMemory(sim.walls, "    sim.walls", 
    321                  "(N=$(length(sim.walls)))")
    322 
    323     reportMemory(sim.ocean, "    sim.ocean",
    324                  "($(size(sim.ocean.xh, 1))x" *
    325                  "$(size(sim.ocean.xh, 2))x" *
    326                  "$(size(sim.ocean.xh, 3)))")
    327 
    328     reportMemory(sim.atmosphere, "    sim.atmosphere",
    329                  "($(size(sim.atmosphere.xh, 1))x" *
    330                  "$(size(sim.atmosphere.xh, 2))x" *
    331                  "$(size(sim.atmosphere.xh, 3)))")
    332     nothing
    333 end
    334 
    335 function reportMemory(variable, head::String, tail::String="")
    336     bytes = Base.summarysize(variable)
    337     if bytes < 10_000
    338         size_str = @sprintf "%5d bytes" bytes
    339     elseif bytes < 10_000 * 1024
    340         size_str = @sprintf "%5d kb" bytes ÷ 1024
    341     elseif bytes < 10_000 * 1024 * 1024
    342         size_str = @sprintf "%5d Mb" bytes ÷ 1024 ÷ 1024
    343     else
    344         size_str = @sprintf "%5d Gb" bytes ÷ 1024 ÷ 1024 ÷ 1024
    345     end
    346     @printf("%-20s %s %s\n", head, size_str, tail)
    347     nothing
    348 end
    349 
    350 export setMaximumNumberOfContactsPerGrain!
    351 """
    352     setMaximumNumberOfContactsPerGrain!(simulation, number_of_contacts)
    353 
    354 Change the maximum number of contacts per grain, which changes simulation.Nc_max
    355 and reallocates memory for each grain. Larger values require more memory, but
    356 allow simulation of wider grain-size distributions. The default value is a
    357 maximum of 32 contacts per grain, which is sufficient for most practical
    358 purposes.
    359 
    360 # Arguments
    361 * `simulation::Simulation`: the Simulation object to modify
    362 * `number_of_contacts::Int`: the maximum number of contacts per grain to allow.
    363 """
    364 function setMaximumNumberOfContactsPerGrain!(sim::Simulation,
    365                                              number_of_contacts::Int)
    366 
    367     if number_of_contacts < 1
    368         error("the parameter number_of_contacts must be a positive integer, " *
    369               "but has the value '$number_of_contacts'")
    370     end
    371     if number_of_contacts == sim.Nc_max
    372         error("number_of_contacts equals the current number of contacts " *
    373               "sim.Nc_max = $(sim.Nc_max)")
    374     end
    375 
    376     Nc_max_orig = sim.Nc_max
    377     sim.Nc_max = number_of_contacts
    378     diff = sim.Nc_max - Nc_max_orig
    379 
    380     for grain in sim.grains
    381 
    382         if diff > 0
    383             # push values to the end of contact arrays if Nc_max > Nc_max_orig
    384             for i=1:diff
    385                 push!(grain.contacts, 0)
    386                 push!(grain.position_vector, zeros(Float64, 3))
    387                 push!(grain.contact_parallel_displacement, zeros(Float64, 3))
    388                 push!(grain.contact_rotation, zeros(Float64, 3))
    389                 push!(grain.contact_age, 0.0)
    390                 push!(grain.contact_area, 0.0)
    391                 push!(grain.compressive_failure, false)
    392             end
    393 
    394         else
    395             # pop values from the end of contact arrays if Nc_max < Nc_max_orig
    396             for i=1:abs(diff)
    397                 pop!(grain.contacts)
    398                 pop!(grain.position_vector)
    399                 pop!(grain.contact_parallel_displacement)
    400                 pop!(grain.contact_rotation)
    401                 pop!(grain.contact_age)
    402                 pop!(grain.contact_area)
    403                 pop!(grain.compressive_failure)
    404             end
    405         end
    406     end
    407 end