Granular.jl

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

commit 76c46ba160aa80b209aa7e20e302b170c87ef454
parent 2379861ee82b96220b667e149c44ed11172f7dc4
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Mon,  9 Jul 2018 11:31:33 -0400

Merge pull request #6 from anders-dc/ridging

Implement compressive failure parameterization
Diffstat:
Msrc/atmosphere.jl | 12++++++------
Msrc/contact_search.jl | 12+++++++-----
Msrc/datatypes.jl | 14+++++++++-----
Msrc/grain.jl | 347++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/grid.jl | 22++++++++++++----------
Msrc/interaction.jl | 213++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/io.jl | 53+++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/ocean.jl | 11++++++-----
Msrc/packing.jl | 12++++++------
Msrc/simulation.jl | 2+-
Msrc/temporal_integration.jl | 22++++++++++++++--------
Msrc/util.jl | 28++++++++++++++++++++++++++++
Msrc/wall.jl | 28++++++++++++++--------------
Mtest/atmosphere.jl | 24+++++++++++-------------
Mtest/cohesion.jl | 49+++++++++++++++++++++++++++----------------------
Mtest/collision-2floes-normal.jl | 2--
Mtest/collision-2floes-oblique.jl | 129++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mtest/collision-5floes-normal.jl | 2--
Atest/compressive_failure.jl | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/contact-search-and-geometry.jl | 92++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mtest/grain.jl | 28+++++++++++++---------------
Mtest/grid-boundaries.jl | 18++++++++----------
Mtest/grid.jl | 2--
Mtest/jld.jl | 2--
Mtest/netcdf.jl | 2--
Mtest/ocean.jl | 24+++++++++++-------------
Mtest/packing.jl | 2--
Mtest/profiling.jl | 2--
Mtest/runtests.jl | 40+++++++++++++++++++++++-----------------
Mtest/util.jl | 13+++++++++++--
Mtest/vtk.jl | 6++----
Mtest/wall.jl | 8+++-----
32 files changed, 885 insertions(+), 520 deletions(-)

diff --git a/src/atmosphere.jl b/src/atmosphere.jl @@ -169,10 +169,10 @@ function applyAtmosphereDragToGrain!(grain::GrainCylindrical, drag_force = ρ_a * π * (2.0*grain.ocean_drag_coeff_vert*grain.areal_radius*.1*grain.thickness + grain.atmosphere_drag_coeff_horiz*grain.areal_radius^2.0) * - ([u, v] - grain.lin_vel)*norm([u, v] - grain.lin_vel) + ([u, v] - grain.lin_vel[1:2])*norm([u, v] - grain.lin_vel[1:2]) - grain.force += drag_force - grain.atmosphere_stress = drag_force/grain.horizontal_surface_area + grain.force += vecTo3d(drag_force) + grain.atmosphere_stress = vecTo3d(drag_force/grain.horizontal_surface_area) nothing end @@ -186,12 +186,12 @@ function applyAtmosphereVorticityToGrain!(grain::GrainCylindrical, atmosphere_curl::Float64) ρ_a = 1.2754 # atmosphere density - grain.torque += + grain.torque[3] += π * grain.areal_radius^4. * ρ_a * (grain.areal_radius / 5. * grain.atmosphere_drag_coeff_horiz + .1 * grain.thickness * grain.atmosphere_drag_coeff_vert) * - abs(.5 * atmosphere_curl - grain.ang_vel) * - (.5 * atmosphere_curl - grain.ang_vel) + norm(.5 * atmosphere_curl - grain.ang_vel[3]) * + (.5 * atmosphere_curl - grain.ang_vel[3]) nothing end diff --git a/src/contact_search.jl b/src/contact_search.jl @@ -109,7 +109,7 @@ Perform an O(n*log(n)) cell-based contact search between all grains in the """ function findContactsInGrid!(simulation::Simulation, grid::Any) - distance_modifier = [0., 0.] + distance_modifier = [0., 0., 0.] i_corrected = 0 j_corrected = 0 @@ -173,7 +173,7 @@ function checkForContacts(simulation::Simulation, r_candidate::Float64; return_when_overlap_found::Bool=false) - distance_modifier = zeros(2) + distance_modifier = zeros(3) overlaps_found::Integer = 0 # Inter-grain position vector and grain overlap @@ -225,7 +225,7 @@ function periodicBoundaryCorrection!(grid::Any, i::Integer, j::Integer) # vector for correcting inter-particle distance in case of # boundary periodicity - distance_modifier = zeros(2) + distance_modifier = zeros(3) # i and j are not corrected for periodic boundaries i_corrected = i @@ -275,7 +275,7 @@ written to `simulation.contact_parallel_displacement`. boundaries. """ function checkAndAddContact!(sim::Simulation, i::Int, j::Int, - distance_modifier::Vector{Float64} = [0., 0.]) + distance_modifier::Vector{Float64} = [0., 0., 0.]) if i < j @inbounds if !sim.grains[i].enabled || !sim.grains[j].enabled @@ -327,8 +327,10 @@ function checkAndAddContact!(sim::Simulation, i::Int, j::Int, position_ij @inbounds fill!(sim.grains[i]. contact_parallel_displacement[ic] , 0.) - @inbounds sim.grains[i].contact_rotation[ic] = 0. + @inbounds fill!(sim.grains[i].contact_rotation[ic] , 0.) @inbounds sim.grains[i].contact_age[ic] = 0. + @inbounds sim.grains[i].contact_area[ic] = 0. + @inbounds sim.grains[i].compressive_failure[ic] = false break end end diff --git a/src/datatypes.jl b/src/datatypes.jl @@ -24,15 +24,16 @@ mutable struct GrainCylindrical lin_disp::Vector{Float64} # Angular kinematic degrees of freedom for vertical rotation around center - ang_pos::Float64 - ang_vel::Float64 - ang_acc::Float64 - torque::Float64 + ang_pos::Vector{Float64} + ang_vel::Vector{Float64} + ang_acc::Vector{Float64} + torque::Vector{Float64} # Kinematic constraint flags fixed::Bool allow_x_acc::Bool allow_y_acc::Bool + allow_z_acc::Bool rotating::Bool enabled::Bool @@ -65,8 +66,10 @@ mutable struct GrainCylindrical contacts::Vector{Int} position_vector::Vector{Vector{Float64}} contact_parallel_displacement::Vector{Vector{Float64}} - contact_rotation::Vector{Float64} + contact_rotation::Vector{Vector{Float64}} contact_age::Vector{Float64} + contact_area::Vector{Float64} + compressive_failure::Vector{Bool} granular_stress::Vector{Float64} ocean_stress::Vector{Float64} @@ -128,6 +131,7 @@ mutable struct GrainArrays fixed::Vector{Int} allow_x_acc::Vector{Int} allow_y_acc::Vector{Int} + allow_z_acc::Vector{Int} rotating::Vector{Int} enabled::Vector{Int} diff --git a/src/grain.jl b/src/grain.jl @@ -24,7 +24,7 @@ export addGrainCylindrical! atmosphere_drag_coeff_vert, atmosphere_drag_coeff_horiz, pressure, fixed, - allow_x_acc, allow_y_acc, + allow_x_acc, allow_y_acc, allow_z_acc, rotating, enabled, verbose, ocean_grid_pos, atmosphere_grid_pos, n_contact, granular_stress, ocean_stress, @@ -39,21 +39,34 @@ are optional, and come with default values. The only required arguments are # Arguments * `simulation::Simulation`: the simulation object where the grain should be added to. -* `lin_pos::Vector{Float64}`: linear position of grain center [m]. +* `lin_pos::Vector{Float64}`: linear position of grain center [m]. If a + two-component vector is used, the values will be mapped to *x* and *y*, and + the *z* component will be set to zero. * `contact_radius::Float64`: grain radius for granular interaction [m]. * `thickness::Float64`: grain thickness [m]. * `areal_radius = false`: grain radius for determining sea-ice concentration [m]. -* `lin_vel::Vector{Float64} = [0., 0.]`: linear velocity [m/s]. -* `lin_acc::Vector{Float64} = [0., 0.]`: linear acceleration [m/s^2]. -* `force::Vector{Float64} = [0., 0.]`: linear force balance [N]. -* `ang_pos::Float64 = 0.`: angular position around its center vertical axis - [rad]. -* `ang_vel::Float64 = 0.`: angular velocity around its center vertical axis - [rad/s]. -* `ang_acc::Float64 = 0.`: angular acceleration around its center vertical axis - [rad/s^2]. -* `torque::Float64 = 0.`: torque around its center vertical axis [N*m] +* `lin_vel::Vector{Float64} = [0., 0., 0.]`: linear velocity [m/s]. If a + two-component vector is used, the values will be mapped to *x* and *y*, and + the *z* component will be set to zero. +* `lin_acc::Vector{Float64} = [0., 0., 0.]`: linear acceleration [m/s^2]. If a + two-component vector is used, the values will be mapped to *x* and *y*, and + the *z* component will be set to zero. +* `force::Vector{Float64} = [0., 0., 0.]`: linear force balance [N]. If a + two-component vector is used, the values will be mapped to *x* and *y*, and + the *z* component will be set to zero. +* `ang_pos::Float64 = [0., 0., 0.]`: angular position around its center vertical + axis [rad]. If a scalar is used, the value will be mapped to *z*, and the + *x* and *y* components will be set to zero. +* `ang_vel::Float64 = [0., 0., 0.]`: angular velocity around its center vertical + axis [rad/s]. If a scalar is used, the value will be mapped to *z*, and the + *x* and *y* components will be set to zero. +* `ang_acc::Float64 = [0., 0., 0.]`: angular acceleration around its center + vertical axis [rad/s^2]. If a scalar is used, the value will be mapped to + *z*, and the *x* and *y* components will be set to zero. +* `torque::Float64 = [0., 0., 0.]`: torque around its center vertical axis + [N*m]. If a scalar is used, the value will be mapped to *z*, and the *x* and + *y* components will be set to zero. * `density::Float64 = 934.`: grain mean density [kg/m^3]. * `contact_stiffness_normal::Float64 = 1e7`: contact-normal stiffness [N/m]; overridden if `youngs_modulus` is set to a positive value. @@ -75,8 +88,8 @@ are optional, and come with default values. The only required arguments are * `shear_strength::Float64 = 0.`: shear strength of bonded contacts [Pa]. * `strength_heal_rate::Float64 = 0.`: rate at which contact bond strength is obtained [Pa/s]. -* `fracture_toughness::Float64 = 0.`: maximum compressive - strength on granular contact (not currently enforced) [m^{1/2}*Pa]. A value +* `fracture_toughness::Float64 = 0.`: fracture toughness which influences the + maximum compressive strength on granular contact [m^{1/2}*Pa]. A value of 1.285e3 m^{1/2}*Pa is used for sea ice by Hopkins 2004. * `ocean_drag_coeff_vert::Float64 = 0.85`: vertical drag coefficient for ocean against grain sides [-]. @@ -90,6 +103,7 @@ are optional, and come with default values. The only required arguments are * `fixed::Bool = false`: grain is fixed to a constant velocity (e.g. zero). * `allow_x_acc::Bool = false`: override `fixed` along `x`. * `allow_y_acc::Bool = false`: override `fixed` along `y`. +* `allow_z_acc::Bool = false`: override `fixed` along `z`. * `rotating::Bool = true`: grain is allowed to rotate. * `enabled::Bool = true`: grain interacts with other grains. * `verbose::Bool = true`: display diagnostic information during the function @@ -99,11 +113,11 @@ are optional, and come with default values. The only required arguments are * `atmosphere_grid_pos::Array{Int, 1} = [0, 0]`: position of grain in the atmosphere grid. * `n_contacts::Int = 0`: number of contacts with other grains. -* `granular_stress::Vector{Float64} = [0., 0.]`: resultant stress on grain +* `granular_stress::Vector{Float64} = [0., 0., 0.]`: resultant stress on grain from granular interactions [Pa]. -* `ocean_stress::Vector{Float64} = [0., 0.]`: resultant stress on grain from +* `ocean_stress::Vector{Float64} = [0., 0., 0.]`: resultant stress on grain from ocean drag [Pa]. -* `atmosphere_stress::Vector{Float64} = [0., 0.]`: resultant stress on grain +* `atmosphere_stress::Vector{Float64} = [0., 0., 0.]`: resultant stress on grain from atmosphere drag [Pa]. * `thermal_energy::Float64 = 0.0`: thermal energy of grain [J]. * `color::Int=0`: type number, usually used for associating a color to the grain @@ -111,12 +125,13 @@ are optional, and come with default values. The only required arguments are # Examples The most basic example adds a new grain to the simulation `sim`, with a -center at `[1., 2.]`, a radius of `1.` meter, and a thickness of `0.5` +center at `[1., 2., 0.]`, a radius of `1.` meter, and a thickness of `0.5` meter: ```julia Granular.addGrainCylindrical!(sim, [1., 2.], 1., .5) ``` +Note that the *z* component is set to zero if a two-component vector is passed. The following example will create a grain with tensile and shear strength, and a velocity of 0.5 m/s towards -x: @@ -142,13 +157,13 @@ function addGrainCylindrical!(simulation::Simulation, contact_radius::Float64, thickness::Float64; areal_radius = false, - lin_vel::Vector{Float64} = [0., 0.], - lin_acc::Vector{Float64} = [0., 0.], - force::Vector{Float64} = [0., 0.], - ang_pos::Float64 = 0., - ang_vel::Float64 = 0., - ang_acc::Float64 = 0., - torque::Float64 = 0., + lin_vel::Vector{Float64} = [0., 0., 0.], + lin_acc::Vector{Float64} = [0., 0., 0.], + force::Vector{Float64} = [0., 0., 0.], + ang_pos::Vector{Float64} = [0., 0., 0.], + ang_vel::Vector{Float64} = [0., 0., 0.], + ang_acc::Vector{Float64} = [0., 0., 0.], + torque::Vector{Float64} = [0., 0., 0.], density::Float64 = 934., contact_stiffness_normal::Float64 = 1e7, contact_stiffness_tangential::Float64 = 0., @@ -170,30 +185,52 @@ function addGrainCylindrical!(simulation::Simulation, fixed::Bool = false, allow_x_acc::Bool = false, allow_y_acc::Bool = false, + allow_z_acc::Bool = false, rotating::Bool = true, enabled::Bool = true, verbose::Bool = true, ocean_grid_pos::Array{Int, 1} = [0, 0], atmosphere_grid_pos::Array{Int, 1} = [0, 0], n_contacts::Int = 0, - granular_stress::Vector{Float64} = [0., 0.], - ocean_stress::Vector{Float64} = [0., 0.], - atmosphere_stress::Vector{Float64} = [0., 0.], + granular_stress::Vector{Float64} = [0., 0., 0.], + ocean_stress::Vector{Float64} = [0., 0., 0.], + atmosphere_stress::Vector{Float64} = [0., 0., 0.], thermal_energy::Float64 = 0., color::Int = 0) # Check input values - if length(lin_pos) != 2 - error("Linear position must be a two-element array " * - "(lin_pos = $lin_pos)") + if length(lin_pos) != 3 + lin_pos = vecTo3d(lin_pos) end - if length(lin_vel) != 2 - error("Linear velocity must be a two-element array " * - "(lin_vel = $lin_vel)") + if length(lin_vel) != 3 + lin_vel = vecTo3d(lin_vel) end - if length(lin_acc) != 2 - error("Linear acceleration must be a two-element array " * - "(lin_acc = $lin_acc)") + if length(lin_acc) != 3 + lin_acc = vecTo3d(lin_acc) + end + if length(force) != 3 + force = vecTo3d(force) + end + if length(ang_pos) != 3 + ang_pos = vecTo3d(ang_pos) + end + if length(ang_vel) != 3 + ang_vel = vecTo3d(ang_vel) + end + if length(ang_acc) != 3 + ang_acc = vecTo3d(ang_acc) + end + if length(torque) != 3 + torque = vecTo3d(torque) + end + if length(granular_stress) != 3 + granular_stress = vecTo3d(granular_stress) + end + if length(ocean_stress) != 3 + ocean_stress = vecTo3d(ocean_stress) + end + if length(atmosphere_stress) != 3 + atmosphere_stress = vecTo3d(atmosphere_stress) end if contact_radius <= 0.0 error("Radius must be greater than 0.0 " * @@ -212,81 +249,87 @@ function addGrainCylindrical!(simulation::Simulation, position_vector = Vector{Vector{Float64}}(undef, simulation.Nc_max) contact_parallel_displacement = Vector{Vector{Float64}}(undef, simulation.Nc_max) - contact_rotation::Vector{Float64} = zeros(Float64, simulation.Nc_max) + contact_rotation = Vector{Vector{Float64}}(undef, simulation.Nc_max) contact_age::Vector{Float64} = zeros(Float64, simulation.Nc_max) + contact_area::Vector{Float64} = zeros(Float64, simulation.Nc_max) + compressive_failure::Vector{Bool} = zeros(Bool, simulation.Nc_max) for i=1:simulation.Nc_max - position_vector[i] = zeros(2) - contact_parallel_displacement[i] = zeros(2) + position_vector[i] = zeros(3) + contact_rotation[i] = zeros(3) + contact_parallel_displacement[i] = zeros(3) end # Create grain object with placeholder values for surface area, volume, # mass, and moment of inertia. grain = GrainCylindrical(density, - thickness, - contact_radius, - areal_radius, - 1.0, # circumreference - 1.0, # horizontal_surface_area - 1.0, # side_surface_area - 1.0, # volume - 1.0, # mass - 1.0, # moment_of_inertia - lin_pos, - lin_vel, - lin_acc, - force, - [0., 0.], # external_body_force - [0., 0.], # lin_disp - - ang_pos, - ang_vel, - ang_acc, - torque, - - fixed, - allow_x_acc, - allow_y_acc, - rotating, - enabled, - - contact_stiffness_normal, - contact_stiffness_tangential, - contact_viscosity_normal, - contact_viscosity_tangential, - contact_static_friction, - contact_dynamic_friction, - - youngs_modulus, - poissons_ratio, - tensile_strength, - shear_strength, - strength_heal_rate, - fracture_toughness, - - ocean_drag_coeff_vert, - ocean_drag_coeff_horiz, - atmosphere_drag_coeff_vert, - atmosphere_drag_coeff_horiz, - - pressure, - n_contacts, - ocean_grid_pos, - atmosphere_grid_pos, - contacts, - position_vector, - contact_parallel_displacement, - contact_rotation, - contact_age, - - granular_stress, - ocean_stress, - atmosphere_stress, - - thermal_energy, - - color - ) + thickness, + contact_radius, + areal_radius, + 1.0, # circumreference + 1.0, # horizontal_surface_area + 1.0, # side_surface_area + 1.0, # volume + 1.0, # mass + 1.0, # moment_of_inertia + lin_pos, + lin_vel, + lin_acc, + force, + [0., 0., 0.], # external_body_force + [0., 0., 0.], # lin_disp + + ang_pos, + ang_vel, + ang_acc, + torque, + + fixed, + allow_x_acc, + allow_y_acc, + allow_z_acc, + rotating, + enabled, + + contact_stiffness_normal, + contact_stiffness_tangential, + contact_viscosity_normal, + contact_viscosity_tangential, + contact_static_friction, + contact_dynamic_friction, + + youngs_modulus, + poissons_ratio, + tensile_strength, + shear_strength, + strength_heal_rate, + fracture_toughness, + + ocean_drag_coeff_vert, + ocean_drag_coeff_horiz, + atmosphere_drag_coeff_vert, + atmosphere_drag_coeff_horiz, + + pressure, + n_contacts, + ocean_grid_pos, + atmosphere_grid_pos, + contacts, + position_vector, + contact_parallel_displacement, + contact_rotation, + contact_age, + contact_area, + compressive_failure, + + granular_stress, + ocean_stress, + atmosphere_stress, + + thermal_energy, + + color + ) # Overwrite previous placeholder values grain.circumreference = grainCircumreference(grain) @@ -400,6 +443,8 @@ function convertGrainDataToArrays(simulation::Simulation) Array{Int}(undef, length(simulation.grains)), ## allow_y_acc Array{Int}(undef, length(simulation.grains)), + ## allow_z_acc + Array{Int}(undef, length(simulation.grains)), ## rotating Array{Int}(undef, length(simulation.grains)), ## enabled @@ -479,22 +524,23 @@ function convertGrainDataToArrays(simulation::Simulation) ifarr.mass[i] = simulation.grains[i].mass ifarr.moment_of_inertia[i] = simulation.grains[i].moment_of_inertia - ifarr.lin_pos[1:2, i] = simulation.grains[i].lin_pos - ifarr.lin_vel[1:2, i] = simulation.grains[i].lin_vel - ifarr.lin_acc[1:2, i] = simulation.grains[i].lin_acc - ifarr.force[1:2, i] = simulation.grains[i].force - ifarr.external_body_force[1:2, i] = + ifarr.lin_pos[1:3, i] = simulation.grains[i].lin_pos + ifarr.lin_vel[1:3, i] = simulation.grains[i].lin_vel + ifarr.lin_acc[1:3, i] = simulation.grains[i].lin_acc + ifarr.force[1:3, i] = simulation.grains[i].force + ifarr.external_body_force[1:3, i] = simulation.grains[i].external_body_force - ifarr.lin_disp[1:2, i] = simulation.grains[i].lin_disp + ifarr.lin_disp[1:3, i] = simulation.grains[i].lin_disp - ifarr.ang_pos[3, i] = simulation.grains[i].ang_pos - ifarr.ang_vel[3, i] = simulation.grains[i].ang_vel - ifarr.ang_acc[3, i] = simulation.grains[i].ang_acc - ifarr.torque[3, i] = simulation.grains[i].torque + ifarr.ang_pos[1:3, i] = simulation.grains[i].ang_pos + ifarr.ang_vel[1:3, i] = simulation.grains[i].ang_vel + ifarr.ang_acc[1:3, i] = simulation.grains[i].ang_acc + ifarr.torque[1:3, i] = simulation.grains[i].torque ifarr.fixed[i] = Int(simulation.grains[i].fixed) ifarr.allow_x_acc[i] = Int(simulation.grains[i].allow_x_acc) ifarr.allow_y_acc[i] = Int(simulation.grains[i].allow_y_acc) + ifarr.allow_z_acc[i] = Int(simulation.grains[i].allow_z_acc) ifarr.rotating[i] = Int(simulation.grains[i].rotating) ifarr.enabled[i] = Int(simulation.grains[i].enabled) @@ -531,10 +577,9 @@ function convertGrainDataToArrays(simulation::Simulation) ifarr.pressure[i] = simulation.grains[i].pressure ifarr.n_contacts[i] = simulation.grains[i].n_contacts - ifarr.granular_stress[1:2, i] = simulation.grains[i].granular_stress - ifarr.ocean_stress[1:2, i] = simulation.grains[i].ocean_stress - ifarr.atmosphere_stress[1:2, i] = - simulation.grains[i].atmosphere_stress + ifarr.granular_stress[1:3, i] = simulation.grains[i].granular_stress + ifarr.ocean_stress[1:3, i] = simulation.grains[i].ocean_stress + ifarr.atmosphere_stress[1:3, i] = simulation.grains[i].atmosphere_stress ifarr.thermal_energy[i] = simulation.grains[i].thermal_energy @@ -576,6 +621,7 @@ function deleteGrainArrays!(ifarr::GrainArrays) ifarr.fixed = i1 ifarr.allow_x_acc = i1 ifarr.allow_y_acc = i1 + ifarr.allow_z_acc = i1 ifarr.rotating = i1 ifarr.enabled = i1 @@ -643,22 +689,23 @@ function printGrainInfo(f::GrainCylindrical) println(" fixed: $(f.fixed)") println(" allow_x_acc: $(f.allow_x_acc)") println(" allow_y_acc: $(f.allow_y_acc)") + println(" allow_z_acc: $(f.allow_z_acc)") println(" rotating: $(f.rotating)") println(" enabled: $(f.enabled)\n") - println(" k_n: $(f.contact_stiffness_normal) N/m") - println(" k_t: $(f.contact_stiffness_tangential) N/m") + println(" k_n: $(f.contact_stiffness_normal) N/m") + println(" k_t: $(f.contact_stiffness_tangential) N/m") println(" γ_n: $(f.contact_viscosity_normal) N/(m/s)") println(" γ_t: $(f.contact_viscosity_tangential) N/(m/s)") - println(" μ_s: $(f.contact_static_friction)") - println(" μ_d: $(f.contact_dynamic_friction)\n") + println(" μ_s: $(f.contact_static_friction)") + println(" μ_d: $(f.contact_dynamic_friction)\n") - println(" E: $(f.youngs_modulus) Pa") - println(" ν: $(f.poissons_ratio)") - println(" tensile_strength: $(f.tensile_strength) Pa") - println(" shear_strength: $(f.shear_strength) Pa") - println(" strength_heal_rate: $(f.strength_heal_rate) Pa/s") - println(" fracture_toughness: $(f.fracture_toughness) m^0.5 Pa\n") + println(" E: $(f.youngs_modulus) Pa") + println(" ν: $(f.poissons_ratio)") + println(" tensile_strength: $(f.tensile_strength) Pa") + println(" shear_strength: $(f.shear_strength) Pa") + println(" strength_heal_rate: $(f.strength_heal_rate) Pa/s") + println(" fracture_toughness: $(f.fracture_toughness) m^0.5 Pa\n") println(" c_o_v: $(f.ocean_drag_coeff_vert)") println(" c_o_h: $(f.ocean_drag_coeff_horiz)") @@ -754,7 +801,7 @@ Add to the value of the external body force on a grain. * `force::Vector{Float64}`: a vector of force [N] """ function addBodyForce!(grain::GrainCylindrical, force::Vector{Float64}) - grain.external_body_force += force + grain.external_body_force += vecTo3d(force) end export setBodyForce! @@ -768,7 +815,7 @@ Set the value of the external body force on a grain. * `force::Vector{Float64}`: a vector of force [N] """ function setBodyForce!(grain::GrainCylindrical, force::Vector{Float64}) - grain.external_body_force = force + grain.external_body_force = vecTo3d(force) end export compareGrains @@ -781,11 +828,9 @@ function compareGrains(if1::GrainCylindrical, if2::GrainCylindrical) @test if1.density ≈ if2.density @test if1.thickness ≈ if2.thickness - @test if1.contact_radius ≈ - if2.contact_radius + @test if1.contact_radius ≈ if2.contact_radius @test if1.areal_radius ≈ if2.areal_radius - @test if1.circumreference ≈ - if2.circumreference + @test if1.circumreference ≈ if2.circumreference @test if1.horizontal_surface_area ≈ if2.horizontal_surface_area @test if1.side_surface_area ≈ if2.side_surface_area @test if1.volume ≈ if2.volume @@ -809,11 +854,9 @@ function compareGrains(if1::GrainCylindrical, if2::GrainCylindrical) @test if1.enabled == if2.enabled @test if1.contact_stiffness_normal ≈ if2.contact_stiffness_normal - @test if1.contact_stiffness_tangential ≈ - if2.contact_stiffness_tangential + @test if1.contact_stiffness_tangential ≈ if2.contact_stiffness_tangential @test if1.contact_viscosity_normal ≈ if2.contact_viscosity_normal - @test if1.contact_viscosity_tangential ≈ - if2.contact_viscosity_tangential + @test if1.contact_viscosity_tangential ≈ if2.contact_viscosity_tangential @test if1.contact_static_friction ≈ if2.contact_static_friction @test if1.contact_dynamic_friction ≈ if2.contact_dynamic_friction @@ -822,25 +865,23 @@ function compareGrains(if1::GrainCylindrical, if2::GrainCylindrical) @test if1.tensile_strength ≈ if2.tensile_strength @test if1.shear_strength ≈ if2.shear_strength @test if1.strength_heal_rate ≈ if2.strength_heal_rate - @test if1.fracture_toughness ≈ - if2.fracture_toughness + @test if1.fracture_toughness ≈ if2.fracture_toughness @test if1.ocean_drag_coeff_vert ≈ if2.ocean_drag_coeff_vert @test if1.ocean_drag_coeff_horiz ≈ if2.ocean_drag_coeff_horiz - @test if1.atmosphere_drag_coeff_vert ≈ - if2.atmosphere_drag_coeff_vert - @test if1.atmosphere_drag_coeff_horiz ≈ - if2.atmosphere_drag_coeff_horiz + @test if1.atmosphere_drag_coeff_vert ≈ if2.atmosphere_drag_coeff_vert + @test if1.atmosphere_drag_coeff_horiz ≈ if2.atmosphere_drag_coeff_horiz @test if1.pressure ≈ if2.pressure @test if1.n_contacts == if2.n_contacts @test if1.ocean_grid_pos == if2.ocean_grid_pos @test if1.contacts == if2.contacts @test if1.position_vector == if2.position_vector - @test if1.contact_parallel_displacement == - if2.contact_parallel_displacement + @test if1.contact_parallel_displacement == if2.contact_parallel_displacement @test if1.contact_rotation == if2.contact_rotation @test if1.contact_age ≈ if2.contact_age + @test if1.contact_area ≈ if2.contact_area + @test if1.compressive_failure ≈ if2.compressive_failure @test if1.granular_stress ≈ if2.granular_stress @test if1.ocean_stress ≈ if2.ocean_stress @@ -905,12 +946,12 @@ rotational) to zero in order to get rid of all kinetic energy. """ function zeroKinematics!(sim::Simulation) for grain in sim.grains - grain.lin_vel .= zeros(2) - grain.lin_acc .= zeros(2) - grain.force .= zeros(2) - grain.lin_disp .= zeros(2) - grain.ang_vel = 0. - grain.ang_acc = 0. - grain.torque = 0. + grain.lin_vel .= zeros(3) + grain.lin_acc .= zeros(3) + grain.force .= zeros(3) + grain.lin_disp .= zeros(3) + grain.ang_vel .= zeros(3) + grain.ang_acc .= zeros(3) + grain.torque .= zeros(3) end end diff --git a/src/grid.jl b/src/grid.jl @@ -161,14 +161,15 @@ function sortGrainsInGrid!(simulation::Simulation, grid::Any; verbose=true) !grid.regular_grid && i_old > 0 && j_old > 0 && isPointInCell(grid, i_old, j_old, - simulation.grains[idx].lin_pos, sw, se, ne, nw) + simulation.grains[idx].lin_pos[1:2], sw, se, ne, nw) i = i_old j = j_old else if grid.regular_grid - i, j = Int.(floor.((simulation.grains[idx].lin_pos - grid.origo) + i, j = Int.(floor.((simulation.grains[idx].lin_pos[1:2] + - grid.origo) ./ grid.dx[1:2])) + [1,1] else @@ -185,8 +186,9 @@ function sortGrainsInGrid!(simulation::Simulation, grid::Any; verbose=true) j_t = max(min(j_old + j_rel, ny), 1) @inbounds if isPointInCell(grid, i_t, j_t, - simulation.grains[idx].lin_pos, - sw, se, ne, nw) + simulation.grains[idx]. + lin_pos[1:2], + sw, se, ne, nw) i = i_t j = j_t found = true @@ -201,7 +203,7 @@ function sortGrainsInGrid!(simulation::Simulation, grid::Any; verbose=true) if !found i, j = findCellContainingPoint(grid, simulation.grains[idx]. - lin_pos, + lin_pos[1:2], sw, se, ne, nw) end end @@ -285,10 +287,10 @@ This function is a wrapper for `getCellCornerCoordinates()` and function getNonDimensionalCellCoordinates(grid::Any, i::Int, j::Int, point::Vector{Float64}) if grid.regular_grid - return (point - Float64.([i-1,j-1]).*grid.dx[1:2])./grid.dx[1:2] + return (point[1:2] - Float64.([i-1,j-1]).*grid.dx[1:2])./grid.dx[1:2] else sw, se, ne, nw = getCellCornerCoordinates(grid.xq, grid.yq, i, j) - return conformalQuadrilateralCoordinates(sw, se, ne, nw, point) + return conformalQuadrilateralCoordinates(sw, se, ne, nw, point[1:2]) end end @@ -308,7 +310,7 @@ function isPointInCell(grid::Any, i::Int, j::Int, method::String="Conformal") if grid.regular_grid - if [i,j] == Int.(floor.((point - grid.origo) ./ grid.dx[1:2])) + [1,1] + if [i,j] == Int.(floor.((point[1:2] - grid.origo) ./ grid.dx[1:2])) + [1,1] return true else return false @@ -620,8 +622,8 @@ function findEmptyPositionInGridCell(simulation::Simulation, # traverse list of grains in the target cell and check # for overlaps for grain_idx in grid.grain_list[it, jt] - overlap = norm(simulation.grains[grain_idx].lin_pos - - pos) - + overlap = norm(simulation.grains[grain_idx]. + lin_pos[1:2] - pos) - (simulation.grains[grain_idx].contact_radius + r) if overlap < 0. diff --git a/src/interaction.jl b/src/interaction.jl @@ -63,10 +63,11 @@ function interactWalls!(sim::Simulation) # get overlap distance by projecting grain position onto wall-normal # vector. Overlap when δ_n < 0.0 - δ_n = abs(dot(sim.walls[iw].normal, sim.grains[i].lin_pos) - - sim.walls[iw].pos) - sim.grains[i].contact_radius + δ_n = norm(dot(sim.walls[iw].normal[1:2], + sim.grains[i].lin_pos[1:2]) - + sim.walls[iw].pos) - sim.grains[i].contact_radius - vel_n = dot(sim.walls[iw].normal, sim.grains[i].lin_vel) + vel_n = dot(sim.walls[iw].normal[1:2], sim.grains[i].lin_vel[1:2]) if δ_n < 0. if sim.grains[i].youngs_modulus > 0. @@ -98,7 +99,7 @@ function interactWalls!(sim::Simulation) end sim.grains[i].force += -force_n .* sim.walls[iw].normal .* - orientation + orientation sim.walls[iw].force += force_n * orientation end end @@ -127,7 +128,7 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) # Inter-position vector, use stored value which is corrected for boundary # periodicity - p = simulation.grains[i].position_vector[ic] + p = simulation.grains[i].position_vector[ic][1:2] dist = norm(p) r_i = simulation.grains[i].contact_radius @@ -137,11 +138,12 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) δ_n = dist - (r_i + r_j) # Local axes - n = p/dist + n = p/max(dist, 1e-12) t = [-n[2], n[1]] - # Contact kinematics - vel_lin = simulation.grains[i].lin_vel - simulation.grains[j].lin_vel + # Contact kinematics (2d) + vel_lin = simulation.grains[i].lin_vel[1:2] - + simulation.grains[j].lin_vel[1:2] vel_n = dot(vel_lin, n) if !simulation.grains[i].rotating && !simulation.grains[j].rotating @@ -152,25 +154,38 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) if rotation vel_t = dot(vel_lin, t) - - harmonicMean(r_i, r_j)*(simulation.grains[i].ang_vel + - simulation.grains[j].ang_vel) + harmonicMean(r_i, r_j)*(simulation.grains[i].ang_vel[3] + + simulation.grains[j].ang_vel[3]) # Correct old tangential displacement for contact rotation and add new - δ_t0 = simulation.grains[i].contact_parallel_displacement[ic] - δ_t = dot(t, δ_t0 - (n*dot(n, δ_t0))) + vel_t*simulation.time_step + δ_t0 = simulation.grains[i].contact_parallel_displacement[ic][1:2] + if !simulation.grains[i].compressive_failure[ic] + δ_t = dot(t, δ_t0 - (n*dot(n, δ_t0))) + vel_t*simulation.time_step + else + # Use δ_t as total slip-distance vector + δ_t = δ_t0 .+ (vel_lin .+ vel_t.*t) .* simulation.time_step + end - # Determine the contact rotation - θ_t = simulation.grains[i].contact_rotation[ic] + - (simulation.grains[j].ang_vel - simulation.grains[i].ang_vel) * - simulation.time_step + # Determine the contact rotation (2d) + θ_t = simulation.grains[i].contact_rotation[ic][3] + + (simulation.grains[j].ang_vel[3] - + simulation.grains[i].ang_vel[3]) * simulation.time_step end # Effective radius R_ij = harmonicMean(r_i, r_j) + # Determine which ice floe is the thinnest + h_min, idx_thinnest = findmin([simulation.grains[i].thickness, + simulation.grains[j].thickness]) + idx_thinnest == 1 ? idx_thinnest = i : idx_thinnest = j + + # Contact length along z + Lz_ij = h_min + # Contact area - A_ij = R_ij*min(simulation.grains[i].thickness, - simulation.grains[j].thickness) + A_ij = R_ij*Lz_ij + simulation.grains[i].contact_area[ic] = A_ij # Contact mechanical parameters if simulation.grains[i].youngs_modulus > 0. && @@ -190,7 +205,7 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) else # Micromechanical parameterization: k_n and k_t set explicitly k_n = harmonicMean(simulation.grains[i].contact_stiffness_normal, - simulation.grains[j].contact_stiffness_normal) + simulation.grains[j].contact_stiffness_normal) if rotation k_t = harmonicMean(simulation.grains[i].contact_stiffness_tangential, @@ -223,26 +238,20 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) end else - error("unknown contact_normal_rheology (k_n = $k_n, γ_n = $γ_n") + error("unknown contact_normal_rheology (k_n = $k_n, γ_n = $γ_n, + E = $E, A_ij = $A_ij, R_ij = $R_ij) + ") end - # Determine which grain is the weakest - compressive_strength = simulation.grains[i].fracture_toughness * - sqrt(simulation.grains[i].thickness) - compressive_strength_j = simulation.grains[j].fracture_toughness * - sqrt(simulation.grains[j].thickness) - idx_weakest = i - if compressive_strength_j < compressive_strength - compressive_strength = compressive_strength_j - idx_weakest = j - end + # Determine the compressive strength in Pa by the contact thickness and the + # minimum compressive strength [N] + compressive_strength = min(simulation.grains[i].fracture_toughness, + simulation.grains[j].fracture_toughness) * + Lz_ij^1.5 # Grain-pair moment of inertia [m^4] - if rotation - I_ij = 2.0/3.0*R_ij^3*min(simulation.grains[i].thickness, - simulation.grains[j].thickness) - end - + I_ij = 2.0/3.0*R_ij^3*min(simulation.grains[i].thickness, + simulation.grains[j].thickness) # Contact tensile strength increases linearly with contact age until # tensile stress exceeds tensile strength. @@ -264,7 +273,7 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) # Reset contact age (breaking bond) if bond strength is exceeded if rotation - if δ_n >= 0.0 && abs(force_n)/A_ij + abs(M_t)*R_ij/I_ij > tensile_strength + if δ_n >= 0.0 && norm(force_n)/A_ij + norm(M_t)*R_ij/I_ij > tensile_strength force_n = 0. force_t = 0. simulation.grains[i].contacts[ic] = 0 # remove contact @@ -272,7 +281,7 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) simulation.grains[j].n_contacts -= 1 end else - if δ_n >= 0.0 && abs(force_n)/A_ij > tensile_strength + if δ_n >= 0.0 && norm(force_n)/A_ij > tensile_strength force_n = 0. force_t = 0. simulation.grains[i].contacts[ic] = 0 # remove contact @@ -281,36 +290,20 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) end end - # Limit compressive stress if the prefactor is set to a positive value - if δ_n <= 0.0 && compressive_strength > 0. && - abs(force_n) >= compressive_strength - - # Determine the overlap distance where yeild stress is reached - δ_n_yield = -compressive_strength*A_ij/k_n - - # Determine the excess overlap distance relative to yield - Δr = abs(δ_n) - abs(δ_n_yield) - - # Adjust radius and thickness of the weakest grain - simulation.grains[idx_weakest].contact_radius -= Δr - simulation.grains[idx_weakest].areal_radius -= Δr - simulation.grains[idx_weakest].thickness += 1.0/(π*Δr) - end - - if rotation + if rotation && !simulation.grains[i].compressive_failure[ic] if k_t ≈ 0. && γ_t ≈ 0. # do nothing elseif k_t ≈ 0. && γ_t > 0. - force_t = abs(γ_t * vel_t) + force_t = norm(γ_t * vel_t) # Coulomb slip - if force_t > μ_d_minimum*abs(force_n) - force_t = μ_d_minimum*abs(force_n) + if force_t > μ_d_minimum*norm(force_n) + force_t = μ_d_minimum*norm(force_n) # Nguyen et al 2009 "Thermomechanical modelling of friction effects # in granular flows using the discrete element method" - E_shear = abs(force_t)*abs(vel_t)*simulation.time_step + E_shear = norm(force_t)*norm(vel_t)*simulation.time_step # Assume equal thermal properties simulation.grains[i].thermal_energy += 0.5*E_shear @@ -325,13 +318,13 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) force_t = -k_t*δ_t - γ_t*vel_t # Coulomb slip - if abs(force_t) > μ_d_minimum*abs(force_n) - force_t = μ_d_minimum*abs(force_n)*force_t/abs(force_t) + if norm(force_t) > μ_d_minimum*norm(force_n) + force_t = μ_d_minimum*norm(force_n)*force_t/norm(force_t) δ_t = (-force_t - γ_t*vel_t)/k_t - # Nguyen et al 2009 "Thermomechanical modelling of friction effects - # in granular flows using the discrete element method" - E_shear = abs(force_t)*abs(vel_t)*simulation.time_step + # Nguyen et al 2009 "Thermomechanical modelling of friction + # effects in granular flows using the discrete element method" + E_shear = norm(force_t)*norm(vel_t)*simulation.time_step # Assume equal thermal properties simulation.grains[i].thermal_energy += 0.5*E_shear @@ -343,28 +336,104 @@ function interactGrains!(simulation::Simulation, i::Int, j::Int, ic::Int) end end + just_failed = false + # Detect compressive failure if compressive force exceeds compressive + # strength + if δ_n <= 0.0 && compressive_strength > 0. && + !simulation.grains[i].compressive_failure[ic] && + norm(force_n.*n .+ force_t.*t) >= compressive_strength + + # Register that compressive failure has occurred for this contact + simulation.grains[i].compressive_failure[ic] = true + just_failed = true + end + + # Overwrite previous force results if compressive failure occurred + if simulation.grains[i].compressive_failure[ic] && δ_n < 0.0 + + # Normal stress on horizontal interface, assuming bending stresses are + # negligible (ocean density and gravity are hardcoded for now) + σ_n = (1e3 - simulation.grains[i].density) * 9.81 * + (simulation.grains[i].thickness + simulation.grains[j].thickness) + + # Horizontal overlap area (two overlapping circles) + if 2.0*min(r_i, r_j) <= abs(δ_n) + + # Complete overlap + A_h = π*min(r_i, r_j)^2 + + else + # Partial overlap, eq 14 from + # http://mathworld.wolfram.com/Circle-CircleIntersection.html + A_h = r_i^2.0*acos((dist^2.0 + r_i^2.0 - r_j^2.0)/(2.0*dist*r_i)) + + r_j^2.0*acos((dist^2.0 + r_j^2.0 - r_i^2.0)/(2.0*dist*r_j)) - + 0.5*sqrt((-dist + r_i + r_j)* + ( dist + r_i - r_j)* + ( dist - r_i + r_j)* + ( dist + r_i + r_j)) + end + simulation.grains[i].contact_area[ic] = A_h + + if just_failed + # Use δ_t as travel distance on horizontal slip surface, and set the + # original displacement to Coulomb-frictional limit in the direction + # of the pre-failure force + F = force_n.*n .+ force_t.*t + σ_t = μ_d_minimum*norm(σ_n).*F/norm(F) + δ_t = σ_t*A_h/(-k_t) + + # Save energy loss in E_shear + E_shear = norm(F)*norm(vel_lin)*simulation.time_step + simulation.grains[i].thermal_energy += 0.5*E_shear + simulation.grains[j].thermal_energy += 0.5*E_shear + + else + + # Find horizontal stress on slip interface + σ_t = -k_t*δ_t/A_h + + # Check for Coulomb-failure on the slip interface + if norm(σ_t) > μ_d_minimum*norm(σ_n) + + σ_t = μ_d_minimum*norm(σ_n)*σ_t/norm(σ_t) + δ_t = σ_t*A_h/(-k_t) + + E_shear = norm(σ_t*A_h)*norm(vel_lin)*simulation.time_step + simulation.grains[i].thermal_energy += 0.5*E_shear + simulation.grains[j].thermal_energy += 0.5*E_shear + end + end + + force_n = dot(σ_t, n)*A_h + force_t = dot(σ_t, t)*A_h + end + # Break bond under extension through bending failure if δ_n < 0.0 && tensile_strength > 0.0 && rotation && - shear_strength > 0.0 && abs(force_t)/A_ij > shear_strength + shear_strength > 0.0 && norm(force_t)/A_ij > shear_strength force_n = 0. force_t = 0. simulation.grains[i].contacts[ic] = 0 # remove contact simulation.grains[i].n_contacts -= 1 simulation.grains[j].n_contacts -= 1 + else + simulation.grains[i].contact_age[ic] += simulation.time_step end - simulation.grains[i].contact_age[ic] += simulation.time_step - if rotation - simulation.grains[i].contact_parallel_displacement[ic] = δ_t*t - simulation.grains[i].contact_rotation[ic] = θ_t + if simulation.grains[i].compressive_failure[ic] + simulation.grains[i].contact_parallel_displacement[ic] = vecTo3d(δ_t) + else + simulation.grains[i].contact_parallel_displacement[ic] = vecTo3d(δ_t.*t) + end + simulation.grains[i].contact_rotation[ic] = [0., 0., θ_t] - simulation.grains[i].force += force_n*n + force_t*t; - simulation.grains[j].force -= force_n*n + force_t*t; + simulation.grains[i].force += vecTo3d(force_n.*n + force_t.*t); + simulation.grains[j].force -= vecTo3d(force_n.*n + force_t.*t); - simulation.grains[i].torque += -force_t*R_ij + M_t - simulation.grains[j].torque += -force_t*R_ij - M_t + simulation.grains[i].torque[3] += -force_t*R_ij + M_t + simulation.grains[j].torque[3] += -force_t*R_ij - M_t else simulation.grains[i].force += force_n*n; simulation.grains[j].force -= force_n*n; diff --git a/src/io.jl b/src/io.jl @@ -393,6 +393,8 @@ function writeGrainVTK(simulation::Simulation, "Fixed but allow (x) acceleration [-]") WriteVTK.vtk_point_data(vtkfile, ifarr.allow_y_acc, "Fixed but allow (y) acceleration [-]") + WriteVTK.vtk_point_data(vtkfile, ifarr.allow_z_acc, + "Fixed but allow (z) acceleration [-]") WriteVTK.vtk_point_data(vtkfile, ifarr.rotating, "Free to rotate [-]") WriteVTK.vtk_point_data(vtkfile, ifarr.enabled, "Enabled [-]") @@ -481,8 +483,10 @@ function writeGrainInteractionVTK(simulation::Simulation, contact_stiffness = Float64[] tensile_stress = Float64[] shear_displacement = Vector{Float64}[] - contact_rotation = Float64[] + contact_rotation = Vector{Float64}[] contact_age = Float64[] + contact_area = Float64[] + compressive_failure = Int[] for i=1:length(simulation.grains) for ic=1:simulation.Nc_max @@ -549,6 +553,9 @@ function writeGrainInteractionVTK(simulation::Simulation, simulation.grains[i].contact_rotation[ic]) push!(contact_age, simulation.grains[i].contact_age[ic]) + push!(contact_area, simulation.grains[i].contact_area[ic]) + push!(compressive_failure, + Int(simulation.grains[i].compressive_failure[ic])) end end end @@ -577,7 +584,7 @@ function writeGrainInteractionVTK(simulation::Simulation, for i=1:length(i1) write(f, "$(inter_particle_vector[i][1]) ") write(f, "$(inter_particle_vector[i][2]) ") - write(f, "0.0 ") + write(f, "$(inter_particle_vector[i][3]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -587,7 +594,7 @@ function writeGrainInteractionVTK(simulation::Simulation, for i=1:length(i1) @inbounds write(f, "$(shear_displacement[i][1]) ") @inbounds write(f, "$(shear_displacement[i][2]) ") - write(f, "0.0 ") + @inbounds write(f, "$(shear_displacement[i][3]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -637,10 +644,12 @@ function writeGrainInteractionVTK(simulation::Simulation, write(f, " </DataArray>\n") write(f, " <DataArray type=\"Float32\" " * - "Name=\"Contact rotation [rad]\" NumberOfComponents=\"1\" + "Name=\"Contact rotation [rad]\" NumberOfComponents=\"3\" format=\"ascii\">\n") for i=1:length(i1) - @inbounds write(f, "$(contact_rotation[i]) ") + @inbounds write(f, "$(contact_rotation[i][1]) ") + @inbounds write(f, "$(contact_rotation[i][2]) ") + @inbounds write(f, "$(contact_rotation[i][3]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -654,6 +663,24 @@ function writeGrainInteractionVTK(simulation::Simulation, write(f, "\n") write(f, " </DataArray>\n") + write(f, " <DataArray type=\"Float32\" " * + "Name=\"Contact area [m*m]\" NumberOfComponents=\"1\" + format=\"ascii\">\n") + for i=1:length(i1) + @inbounds write(f, "$(contact_area[i]) ") + end + write(f, "\n") + write(f, " </DataArray>\n") + + write(f, " <DataArray type=\"Float32\" " * + "Name=\"Compressive failure [-]\" NumberOfComponents=\"1\" + format=\"ascii\">\n") + for i=1:length(i1) + @inbounds write(f, "$(Int(compressive_failure[i])) ") + end + write(f, "\n") + write(f, " </DataArray>\n") + write(f, " </CellData>\n") write(f, " <Points>\n") @@ -662,7 +689,7 @@ function writeGrainInteractionVTK(simulation::Simulation, write(f, " <DataArray type=\"Float32\" Name=\"Points\" " * "NumberOfComponents=\"3\" format=\"ascii\">\n") for i in simulation.grains - @inbounds write(f, "$(i.lin_pos[1]) $(i.lin_pos[2]) 0.0 ") + @inbounds write(f, "$(i.lin_pos[1]) $(i.lin_pos[2]) $(i.lin_pos[3]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -868,6 +895,7 @@ imagegrains.PointArrayStatus = [ 'Fixed in space [-]', 'Fixed but allow (x) acceleration [-]', 'Fixed but allow (y) acceleration [-]', +'Fixed but allow (z) acceleration [-]', 'Free to rotate [-]', 'Enabled [-]', 'Contact stiffness (normal) [N m^-1]', @@ -1103,6 +1131,12 @@ function render(simulation::Simulation; pvpython::String="pvpython", if isfile("$(simulation.id)/$(simulation.id).mp4") rm("$(simulation.id)/$(simulation.id).avi") end + catch return_signal + if isa(return_signal, Base.UVError) + Compat.@warn "Could not run external ffmpeg command, " * + "skipping conversion from " * + "$(simulation.id)/$(simulation.id).avi to mp4." + end end end @@ -1131,7 +1165,7 @@ function render(simulation::Simulation; pvpython::String="pvpython", end catch return_signal if isa(return_signal, Base.UVError) - Compat.@info "Skipping gif merge since `$convert` " * + Compat.@warn "Skipping gif merge since `$convert` " * "was not found." end end @@ -1321,8 +1355,9 @@ function plotGrains(sim::Simulation; contact_stiffness = Float64[] tensile_stress = Float64[] shear_displacement = Vector{Float64}[] - contact_rotation = Float64[] + contact_rotation = Vector{Float64}[] contact_age = Float64[] + compressive_failure = Int[] for i=1:length(sim.grains) for ic=1:sim.Nc_max if sim.grains[i].contacts[ic] > 0 @@ -1374,6 +1409,8 @@ function plotGrains(sim::Simulation; sim.grains[i].contact_rotation[ic]) push!(contact_age, sim.grains[i].contact_age[ic]) + push!(compressive_failure, + sim.grains[i].compressive_failure[ic]) end end end diff --git a/src/ocean.jl b/src/ocean.jl @@ -388,10 +388,10 @@ function applyOceanDragToGrain!(grain::GrainCylindrical, drag_force = ρ_o * π * (2.0*grain.ocean_drag_coeff_vert*grain.areal_radius*draft + grain.ocean_drag_coeff_horiz*grain.areal_radius^2.0) * - ([u, v] - grain.lin_vel)*norm([u, v] - grain.lin_vel) + ([u, v] - grain.lin_vel[1:2])*norm([u, v] - grain.lin_vel[1:2]) - grain.force += drag_force - grain.ocean_stress = drag_force/grain.horizontal_surface_area + grain.force += vecTo3d(drag_force) + grain.ocean_stress = vecTo3d(drag_force/grain.horizontal_surface_area) nothing end @@ -407,11 +407,12 @@ function applyOceanVorticityToGrain!(grain::GrainCylindrical, ρ_o = 1000. # ocean density draft = grain.thickness - freeboard # height of submerged thickness - grain.torque += + grain.torque[3] += π * grain.areal_radius^4. * ρ_o * (grain.areal_radius/5. * grain.ocean_drag_coeff_horiz + draft * grain.ocean_drag_coeff_vert) * - abs(.5 * ocean_curl - grain.ang_vel) * (.5 * ocean_curl - grain.ang_vel) + norm(.5 * ocean_curl - grain.ang_vel[3]) * + (.5 * ocean_curl - grain.ang_vel[3]) nothing end diff --git a/src/packing.jl b/src/packing.jl @@ -124,7 +124,7 @@ function generateNeighboringPoint(x_i::Vector, r_i::Real, #R = rand() * (r_i + r_j) * max_padding_factor + 2. * (r_i + r_j) R = r_i + r_j + padding T = rand() * 2. * pi - return [x_i[1] + R * sin(T), x_i[2] + R * cos(T)], r_j + return [x_i[1] + R * sin(T), x_i[2] + R * cos(T), x_i[3]], r_j end function generateRandomDirection() @@ -132,7 +132,7 @@ function generateRandomDirection() end function getPositionDistancedFromPoint(T::Real, x_i::Vector, dist::Real) - return [x_i[1] + dist * sin(T), x_i[2] + dist * cos(T)] + return [x_i[1] + dist * sin(T), x_i[2] + dist * cos(T), x_i[3]] end export irregularPacking! @@ -224,7 +224,7 @@ function irregularPacking!(simulation::Simulation; # and add it to the active list. If after `sample_limit` attempts no such # point is found, instead remove `i` from the active list. j = 0; - x_active = zeros(2); x_candidate = zeros(2); + x_active = zeros(3); x_candidate = zeros(3); r_active = 0.; r_candidate = 0.; T = 0. n = 0 neighbor_found = false @@ -456,9 +456,9 @@ function rasterMap(sim::Simulation, dx::Real) #i, j = Int.(floor.((grain.lin_pos - origo) ./ dx)) + [1,1] # Find corner indexes for box spanning the grain - min_i, min_j = Int.(floor.((grain.lin_pos - origo .- + min_i, min_j = Int.(floor.((grain.lin_pos[1:2] - origo .- grain.contact_radius) ./ dx)) .+ [1,1] - max_i, max_j = Int.(floor.((grain.lin_pos - origo .+ + max_i, max_j = Int.(floor.((grain.lin_pos[1:2] - origo .+ grain.contact_radius) ./ dx)) .+ [1,1] # For each cell in box, check if the grain is contained @@ -466,7 +466,7 @@ function rasterMap(sim::Simulation, dx::Real) for j = min_j:max_j cell_mid_point = dx .* Vector{Float64}([i,j]) .- 0.5 * dx - if (norm(cell_mid_point - grain.lin_pos) - + if (norm(cell_mid_point - grain.lin_pos[1:2]) - grain.contact_radius < dist) occupied[i,j] = true end diff --git a/src/simulation.jl b/src/simulation.jl @@ -259,7 +259,7 @@ export zeroForcesAndTorques! function zeroForcesAndTorques!(simulation::Simulation) for grain in simulation.grains grain.force .= grain.external_body_force - grain.torque = 0. + grain.torque = zeros(3) grain.pressure = 0. end for wall in simulation.walls diff --git a/src/temporal_integration.jl b/src/temporal_integration.jl @@ -51,7 +51,7 @@ Use a two-term Taylor expansion for integrating the kinematic degrees of freedom for a `grain`. """ function updateGrainKinematicsTwoTermTaylor!(grain::GrainCylindrical, - simulation::Simulation) + simulation::Simulation) grain.lin_acc = grain.force/grain.mass grain.ang_acc = grain.torque/grain.moment_of_inertia @@ -62,9 +62,12 @@ function updateGrainKinematicsTwoTermTaylor!(grain::GrainCylindrical, if !grain.allow_y_acc grain.lin_acc[2] = 0. end - grain.ang_acc = 0. + if !grain.allow_z_acc + grain.lin_acc[3] = 0. + end + grain.ang_acc = zeros(3) elseif !grain.rotating - grain.ang_acc = 0. + grain.ang_acc = zeros(3) end grain.lin_pos += @@ -90,11 +93,11 @@ Use a three-term Taylor expansion for integrating the kinematic degrees of freedom for a `grain`. """ function updateGrainKinematicsThreeTermTaylor!(grain::GrainCylindrical, - simulation::Simulation) + simulation::Simulation) if simulation.time_iteration == 0 - lin_acc_0 = zeros(2) - ang_acc_0 = 0. + lin_acc_0 = zeros(3) + ang_acc_0 = zeros(3) else lin_acc_0 = grain.lin_acc ang_acc_0 = grain.ang_acc @@ -110,9 +113,12 @@ function updateGrainKinematicsThreeTermTaylor!(grain::GrainCylindrical, if !grain.allow_y_acc grain.lin_acc[2] = 0. end - grain.ang_acc = 0. + if !grain.allow_z_acc + grain.lin_acc[3] = 0. + end + grain.ang_acc = zeros(3) elseif !grain.rotating - grain.ang_acc = 0. + grain.ang_acc = zeros(3) end # Temporal gradient in acceleration using backwards differences diff --git a/src/util.jl b/src/util.jl @@ -42,3 +42,31 @@ function harmonicMean(a::Number, b::Number)::Number return 2. * a * b / (a + b) end end + +export vecTo3d +""" + function vecTo3d(input, fill) + +Convert a scalar or 2d vector to 3d by filling the missing component with the +value `fill`. The returned 3-component vector is a Vector (or 1d Array) of +the same type as the input. + +# Arguments +* `input`: a scalar or two-component vector. +* `fill::Real`: value to use for third +""" +function vecTo3d(input::Any; fill::Real = 0.0) + if length(input) > 3 + error("vecTo3d requires a scalar or input vector to of length 3 or " * + "less, but input is $input") + elseif length(input) == 3 + return input + elseif length(input) == 2 + return [input[1], input[2], typeof(input[1])(fill)] + elseif length(input) == 1 + return [input[1], typeof(input[1])(fill), typeof(input[1])(fill)] + else + error("input not understood: $input") + end +end + diff --git a/src/wall.jl b/src/wall.jl @@ -16,9 +16,10 @@ The only required arguments are # 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]. The +* `normal::Vector{Float64}`: 3d vector denoting the normal to the wall [m]. The wall will only interact in the opposite direction of this vector, so the - normal vector should point in the direction of the grains. + normal vector should point in the direction of the grains. If a 2d vector is + passed, the third (z) component is set to zero. * `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"`. @@ -50,13 +51,13 @@ 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) +Granular.addWallLinearFrictionless!(sim, [1., 0., 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, +Granular.addWallLinearFrictionless!(sim, [0., 1., 0.], 1.5, bc="velocity", vel=-0.5) ``` @@ -65,7 +66,7 @@ To create a wall parallel to the *y* axis pushing downwards with a constant normal stress of 100 kPa, starting at a position of y = 3.5 m: ```julia -Granular.addWallLinearFrictionless!(sim, [0., 1.], 3.5, +Granular.addWallLinearFrictionless!(sim, [0., 1., 0.], 3.5, bc="normal stress", normal_stress=100e3) ``` @@ -85,20 +86,19 @@ function addWallLinearFrictionless!(simulation::Simulation, verbose::Bool=true) # Check input values - if length(normal) != 2 - error("Wall normal must be a two-element array (normal = " * - "$normal)") + if length(normal) != 3 + normal = vecTo3d(normal) end if bc != "fixed" && bc != "velocity" && bc != "normal stress" error("Wall BC must be 'fixed', 'velocity', or 'normal stress'.") end - if !(normal ≈ [1., 0.]) && !(normal ≈ [0., 1.]) + if !(normal ≈ [1., 0., 0.]) && !(normal ≈ [0., 1., 0.]) 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.]. The passed normal was $normal") + "are [1., 0., 0.] and [0., 1., 0.]. The passed normal was $normal") end # if not set, set wall mass to equal the mass of all grains. @@ -171,10 +171,10 @@ Returns the surface area of the wall given the grid size and its index. """ function getWallSurfaceArea(sim::Simulation, wall_index::Integer) - if sim.walls[wall_index].normal ≈ [1., 0.] + if sim.walls[wall_index].normal ≈ [1., 0., 0.] return (sim.ocean.yq[end,end] - sim.ocean.yq[1,1]) * sim.walls[wall_index].thickness - elseif sim.walls[wall_index].normal ≈ [0., 1.] + elseif sim.walls[wall_index].normal ≈ [0., 1., 0.] return (sim.ocean.xq[end,end] - sim.ocean.xq[1,1]) * sim.walls[wall_index].thickness else @@ -185,9 +185,9 @@ end function getWallSurfaceArea(sim::Simulation, normal::Vector{Float64}, thickness::Float64) - if normal ≈ [1., 0.] + if length(normal) == 3 && normal ≈ [1., 0., 0.] return (sim.ocean.yq[end,end] - sim.ocean.yq[1,1]) * thickness - elseif normal ≈ [0., 1.] + elseif length(normal) == 3 && normal ≈ [0., 1., 0.] return (sim.ocean.xq[end,end] - sim.ocean.xq[1,1]) * thickness else error("Wall normal not understood") diff --git a/test/atmosphere.jl b/test/atmosphere.jl @@ -3,8 +3,6 @@ # Check if atmosphere-specific functions and grid operations are functioning # correctly -Compat.@info "#### $(basename(@__FILE__)) ####" - Compat.@info "Testing regular grid generation" sim = Granular.createSimulation() sim.atmosphere = Granular.createRegularAtmosphereGrid([6, 6, 6], [1., 1., 1.]) @@ -54,15 +52,15 @@ E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) Compat.@info "Testing vortex interaction (static atmosphere)" sim = deepcopy(sim_init) -sim.grains[1].ang_vel = 0.1 +sim.grains[1].ang_vel[3] = 0.1 E_kin_lin_init = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_rot_init > E_kin_rot_final # energy lost to atmosphere -@test sim.grains[1].ang_vel > 0. # check angular velocity orientation -@test sim.grains[1].ang_pos > 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] > 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] > 0. # check angular position orientation @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained Compat.@info "Testing vortex interaction (static ice floe)" @@ -77,8 +75,8 @@ E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) -@test sim.grains[1].ang_vel > 0. # check angular velocity orientation -@test sim.grains[1].ang_pos > 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] > 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] > 0. # check angular position orientation @test E_kin_rot_init < E_kin_rot_final # rotation after due to atm vortex @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained @@ -93,8 +91,8 @@ E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) -@test sim.grains[1].ang_vel < 0. # check angular velocity orientation -@test sim.grains[1].ang_pos < 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] < 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] < 0. # check angular position orientation @test E_kin_rot_init < E_kin_rot_final # rotation after due to atm vortex @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained @@ -109,8 +107,8 @@ E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) -@test sim.grains[1].ang_vel < 0. # check angular velocity orientation -@test sim.grains[1].ang_pos < 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] < 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] < 0. # check angular position orientation @test E_kin_rot_init < E_kin_rot_final # rotation after due to atm vortex @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained @@ -125,8 +123,8 @@ E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) -@test sim.grains[1].ang_vel > 0. # check angular velocity orientation -@test sim.grains[1].ang_pos > 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] > 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] > 0. # check angular position orientation @test E_kin_rot_init < E_kin_rot_final # rotation after due to atm vortex @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained diff --git a/test/cohesion.jl b/test/cohesion.jl @@ -5,8 +5,6 @@ import Granular # Check for conservation of kinetic energy (=momentum) during a normal collision # between two ice cylindrical grains -Compat.@info "#### $(basename(@__FILE__)) ####" - verbose=false sim_init = Granular.createSimulation() @@ -21,6 +19,7 @@ sim = deepcopy(sim_init) Granular.setTotalTime!(sim, 10.) sim.time_step = 1. Granular.run!(sim, verbose=verbose) +Granular.removeSimulationFiles(sim) @test sim.grains[1].contact_age[1] ≈ sim.time Compat.@info "# Check if bonds add tensile strength" @@ -31,12 +30,13 @@ sim.grains[1].lin_vel[1] = 0.1 Granular.setTimeStep!(sim) Granular.setTotalTime!(sim, 10.) Granular.run!(sim, verbose=verbose) +Granular.removeSimulationFiles(sim) @test sim.grains[1].lin_vel[1] > 0. @test sim.grains[1].lin_vel[2] ≈ 0. @test sim.grains[2].lin_vel[1] > 0. @test sim.grains[2].lin_vel[2] ≈ 0. -@test sim.grains[1].ang_vel ≈ 0. -@test sim.grains[2].ang_vel ≈ 0. +@test sim.grains[1].ang_vel ≈ zeros(3) +@test sim.grains[2].ang_vel ≈ zeros(3) Compat.@info "# Add shear strength and test bending resistance (one grain rotating)" sim = Granular.createSimulation(id="cohesion") @@ -44,7 +44,7 @@ Granular.addGrainCylindrical!(sim, [0., 0.], 10.1, 1., tensile_strength=500e3, shear_strength=500e3) Granular.addGrainCylindrical!(sim, [20., 0.], 10., 1., tensile_strength=500e3, shear_strength=500e3) -sim.grains[1].ang_vel = 0.1 +sim.grains[1].ang_vel[3] = 0.1 Granular.findContacts!(sim) # make sure contact is registered sim.grains[1].contact_radius=10.0 # decrease radius so there isn't compression E_kin_lin_init = Granular.totalGrainKineticTranslationalEnergy(sim) @@ -53,12 +53,13 @@ Granular.setTimeStep!(sim) Granular.setTotalTime!(sim, 5.) #Granular.setOutputFileInterval!(sim, 0.1) Granular.run!(sim, verbose=verbose) +Granular.removeSimulationFiles(sim) @test sim.grains[1].lin_vel[1] ≈ 0. @test sim.grains[1].lin_vel[2] ≈ 0. @test sim.grains[2].lin_vel[1] ≈ 0. @test sim.grains[2].lin_vel[2] ≈ 0. -@test sim.grains[1].ang_vel != 0. -@test sim.grains[2].ang_vel != 0. +@test sim.grains[1].ang_vel[3] != 0. +@test sim.grains[2].ang_vel[3] != 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) E_therm_final = Granular.totalGrainThermalEnergy(sim) @@ -71,7 +72,7 @@ Granular.addGrainCylindrical!(sim, [0., 0.], 10.1, 1., tensile_strength=500e3, shear_strength=500e3) Granular.addGrainCylindrical!(sim, [20., 0.], 10., 1., tensile_strength=500e3, shear_strength=500e3) -sim.grains[2].ang_vel = 0.1 +sim.grains[2].ang_vel[3] = 0.1 Granular.findContacts!(sim) # make sure contact is registered sim.grains[1].contact_radius=10.0 # decrease radius so there isn't compression E_kin_lin_init = Granular.totalGrainKineticTranslationalEnergy(sim) @@ -80,12 +81,13 @@ Granular.setTimeStep!(sim) Granular.setTotalTime!(sim, 5.) #Granular.setOutputFileInterval!(sim, 0.1) Granular.run!(sim, verbose=verbose) +Granular.removeSimulationFiles(sim) @test sim.grains[1].lin_vel[1] ≈ 0. @test sim.grains[1].lin_vel[2] ≈ 0. @test sim.grains[2].lin_vel[1] ≈ 0. @test sim.grains[2].lin_vel[2] ≈ 0. -@test sim.grains[1].ang_vel != 0. -@test sim.grains[2].ang_vel != 0. +@test sim.grains[1].ang_vel[3] != 0. +@test sim.grains[2].ang_vel[3] != 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) E_therm_final = Granular.totalGrainThermalEnergy(sim) @@ -98,8 +100,8 @@ Granular.addGrainCylindrical!(sim, [0., 0.], 10.0000001, 1., tensile_strength=50 shear_strength=500e3) Granular.addGrainCylindrical!(sim, [20., 0.], 10., 1., tensile_strength=500e3, shear_strength=500e3) -sim.grains[1].ang_vel = 0.1 -sim.grains[2].ang_vel = -0.1 +sim.grains[1].ang_vel[3] = 0.1 +sim.grains[2].ang_vel[3] = -0.1 Granular.findContacts!(sim) # make sure contact is registered sim.grains[1].contact_radius=10.0 # decrease radius so there isn't compression E_kin_lin_init = Granular.totalGrainKineticTranslationalEnergy(sim) @@ -108,12 +110,13 @@ Granular.setTimeStep!(sim) Granular.setTotalTime!(sim, 5.) #Granular.setOutputFileInterval!(sim, 0.1) Granular.run!(sim, verbose=verbose) +Granular.removeSimulationFiles(sim) @test sim.grains[1].lin_vel[1] ≈ 0. @test sim.grains[1].lin_vel[2] ≈ 0. @test sim.grains[2].lin_vel[1] ≈ 0. @test sim.grains[2].lin_vel[2] ≈ 0. -@test sim.grains[1].ang_vel != 0. -@test sim.grains[2].ang_vel != 0. +@test sim.grains[1].ang_vel[3] != 0. +@test sim.grains[2].ang_vel[3] != 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) E_therm_final = Granular.totalGrainThermalEnergy(sim) @@ -126,8 +129,8 @@ Granular.addGrainCylindrical!(sim, [0., 0.], 10.0000001, 1., tensile_strength=50 shear_strength=500e3) Granular.addGrainCylindrical!(sim, [20., 0.], 10., 1., tensile_strength=500e3, shear_strength=500e3) -sim.grains[1].ang_vel = 100 -sim.grains[2].ang_vel = -100 +sim.grains[1].ang_vel[3] = 100 +sim.grains[2].ang_vel[3] = -100 Granular.findContacts!(sim) # make sure contact is registered sim.grains[1].contact_radius=10.0 # decrease radius so there isn't compression E_kin_lin_init = Granular.totalGrainKineticTranslationalEnergy(sim) @@ -136,12 +139,13 @@ Granular.setTimeStep!(sim) Granular.setTotalTime!(sim, 5.) #Granular.setOutputFileInterval!(sim, 0.1) Granular.run!(sim, verbose=verbose) +Granular.removeSimulationFiles(sim) @test sim.grains[1].lin_vel[1] ≈ 0. @test sim.grains[1].lin_vel[2] ≈ 0. @test sim.grains[2].lin_vel[1] ≈ 0. @test sim.grains[2].lin_vel[2] ≈ 0. -@test sim.grains[1].ang_vel != 0. -@test sim.grains[2].ang_vel != 0. +@test sim.grains[1].ang_vel[3] != 0. +@test sim.grains[2].ang_vel[3] != 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) E_therm_final = Granular.totalGrainThermalEnergy(sim) @@ -155,8 +159,8 @@ Granular.addGrainCylindrical!(sim, [0., 0.], 10.1, 1., tensile_strength=500e3, shear_strength=50e3) Granular.addGrainCylindrical!(sim, [20., 0.], 10., 1., tensile_strength=500e3, shear_strength=50e3) -sim.grains[1].ang_vel = 100 -sim.grains[2].ang_vel = 0.0 +sim.grains[1].ang_vel[3] = 100 +sim.grains[2].ang_vel[3] = 0.0 Granular.findContacts!(sim) # make sure contact is registered sim.grains[1].contact_radius=10.0 # decrease radius so there isn't compression E_kin_lin_init = Granular.totalGrainKineticTranslationalEnergy(sim) @@ -165,12 +169,13 @@ Granular.setTimeStep!(sim) Granular.setTotalTime!(sim, 5.) #Granular.setOutputFileInterval!(sim, 0.1) Granular.run!(sim, verbose=verbose) +Granular.removeSimulationFiles(sim) @test sim.grains[1].lin_vel[1] ≈ 0. @test sim.grains[1].lin_vel[2] ≈ 0. @test sim.grains[2].lin_vel[1] ≈ 0. @test sim.grains[2].lin_vel[2] ≈ 0. -@test sim.grains[1].ang_vel != 0. -@test sim.grains[2].ang_vel != 0. +@test sim.grains[1].ang_vel[3] != 0. +@test sim.grains[2].ang_vel[3] != 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) E_therm_final = Granular.totalGrainThermalEnergy(sim) diff --git a/test/collision-2floes-normal.jl b/test/collision-2floes-normal.jl @@ -3,8 +3,6 @@ # Check for conservation of kinetic energy (=momentum) during a normal collision # between two ice cylindrical grains -Compat.@info "#### $(basename(@__FILE__)) ####" - verbose=false Compat.@info "# One ice floe fixed" diff --git a/test/collision-2floes-oblique.jl b/test/collision-2floes-oblique.jl @@ -3,8 +3,6 @@ # Check for conservation of kinetic energy (=momentum) during a normal collision # between two ice cylindrical grains -Compat.@info "#### $(basename(@__FILE__)) ####" - verbose=false Compat.@info "## Contact-normal elasticity only" @@ -36,9 +34,6 @@ Granular.run!(sim, temporal_integration_method="Two-term Taylor", verbose=verbos E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) E_thermal_final = Granular.totalGrainThermalEnergy(sim) -println(E_kin_lin_init) -println(E_kin_lin_final) -println(E_thermal_final) @test E_kin_lin_init ≈ E_kin_lin_final+E_thermal_final atol=E_kin_lin_init*tol @test E_kin_rot_init ≈ E_kin_rot_final @@ -136,19 +131,25 @@ sim_init.grains[2].contact_dynamic_friction = 1e2 # no Coulomb sliding sim_init.grains[2].fixed = true sim = deepcopy(sim_init) + Compat.@info "Testing kinetic energy conservation with Two-term Taylor scheme" Granular.setTimeStep!(sim, epsilon=0.07) tol = 0.1 Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" +Granular.setOutputFileInterval!(sim, 1.0) Granular.run!(sim, temporal_integration_method="Two-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos ≈ 0. -@test sim.grains[2].ang_vel ≈ 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] ≈ 0. +@test sim.grains[2].ang_vel[3] ≈ 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) +println(E_kin_lin_init) +println(E_kin_lin_final) +println(E_kin_rot_init) +println(E_kin_rot_final) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol Compat.@info "mu_d = 0." @@ -162,9 +163,9 @@ E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, temporal_integration_method="Three-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos ≈ 0. -@test sim.grains[1].ang_vel ≈ 0. -@test sim.grains[2].ang_pos ≈ 0. +@test sim.grains[1].ang_pos[3] ≈ 0. +@test sim.grains[1].ang_vel[3] ≈ 0. +@test sim.grains[2].ang_pos[3] ≈ 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init ≈ E_kin_lin_final atol=E_kin_lin_init*tol @@ -178,10 +179,10 @@ Compat.@info "Relative tolerance: $(tol*100.)%" Granular.run!(sim, temporal_integration_method="Two-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos ≈ 0. -@test sim.grains[2].ang_vel ≈ 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] ≈ 0. +@test sim.grains[2].ang_vel[3] ≈ 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -195,10 +196,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Three-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos ≈ 0. -@test sim.grains[2].ang_vel ≈ 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] ≈ 0. +@test sim.grains[2].ang_vel[3] ≈ 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -228,10 +229,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Two-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos < 0. -@test sim.grains[2].ang_vel < 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] < 0. +@test sim.grains[2].ang_vel[3] < 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -257,10 +258,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Three-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos < 0. -@test sim.grains[2].ang_vel < 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] < 0. +@test sim.grains[2].ang_vel[3] < 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -291,10 +292,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Two-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos > 0. -@test sim.grains[1].ang_vel > 0. -@test sim.grains[2].ang_pos > 0. -@test sim.grains[2].ang_vel > 0. +@test sim.grains[1].ang_pos[3] > 0. +@test sim.grains[1].ang_vel[3] > 0. +@test sim.grains[2].ang_pos[3] > 0. +@test sim.grains[2].ang_vel[3] > 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -320,10 +321,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Three-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos > 0. -@test sim.grains[1].ang_vel > 0. -@test sim.grains[2].ang_pos > 0. -@test sim.grains[2].ang_vel > 0. +@test sim.grains[1].ang_pos[3] > 0. +@test sim.grains[1].ang_vel[3] > 0. +@test sim.grains[2].ang_pos[3] > 0. +@test sim.grains[2].ang_vel[3] > 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -352,10 +353,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Two-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos < 0. -@test sim.grains[2].ang_vel < 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] < 0. +@test sim.grains[2].ang_vel[3] < 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -381,10 +382,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Three-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos < 0. -@test sim.grains[2].ang_vel < 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] < 0. +@test sim.grains[2].ang_vel[3] < 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -421,10 +422,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Two-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos < 0. -@test sim.grains[2].ang_vel < 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] < 0. +@test sim.grains[2].ang_vel[3] < 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -450,10 +451,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Three-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos < 0. -@test sim.grains[2].ang_vel < 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] < 0. +@test sim.grains[2].ang_vel[3] < 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init ≈ E_kin_lin_final+E_kin_rot_final atol=E_kin_lin_init*tol @@ -503,10 +504,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Three-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos < 0. -@test sim.grains[2].ang_vel < 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] < 0. +@test sim.grains[2].ang_vel[3] < 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init > E_kin_lin_final+E_kin_rot_final @@ -556,10 +557,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Three-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos < 0. -@test sim.grains[2].ang_vel < 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] < 0. +@test sim.grains[2].ang_vel[3] < 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init > E_kin_lin_final+E_kin_rot_final @@ -609,10 +610,10 @@ Compat.@info "Relative tolerance: $(tol*100.)% with time step: $(sim.time_step)" Granular.run!(sim, temporal_integration_method="Three-term Taylor", verbose=verbose) -@test sim.grains[1].ang_pos < 0. -@test sim.grains[1].ang_vel < 0. -@test sim.grains[2].ang_pos < 0. -@test sim.grains[2].ang_vel < 0. +@test sim.grains[1].ang_pos[3] < 0. +@test sim.grains[1].ang_vel[3] < 0. +@test sim.grains[2].ang_pos[3] < 0. +@test sim.grains[2].ang_vel[3] < 0. E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_lin_init+E_kin_rot_init > E_kin_lin_final+E_kin_rot_final diff --git a/test/collision-5floes-normal.jl b/test/collision-5floes-normal.jl @@ -4,8 +4,6 @@ using Compat.LinearAlgebra # Check for conservation of kinetic energy (=momentum) during a normal collision # between two ice cylindrical grains -Compat.@info "#### $(basename(@__FILE__)) ####" - verbose=false Compat.@info "# One ice floe fixed" diff --git a/test/compressive_failure.jl b/test/compressive_failure.jl @@ -0,0 +1,184 @@ +#!/usr/bin/env julia +using Compat.Test +import Granular + +verbose = false +debug = false +if debug + import PyPlot +end + +function plot_interaction(sim::Granular.Simulation, output::String) + time = Float64[] + force_n_1 = Float64[] + force_n_2 = Float64[] + force_t_1 = Float64[] + force_t_2 = Float64[] + torque_1 = Float64[] + torque_2 = Float64[] + compressive_failure = Bool[] + while sim.time < sim.time_total + Granular.run!(sim, verbose=verbose, single_step=true) + append!(time, sim.time) + append!(compressive_failure, sim.grains[1].compressive_failure[1]) + append!(force_n_1, sim.grains[1].force[1]) + append!(force_n_2, sim.grains[2].force[1]) + append!(force_t_1, sim.grains[1].force[2]) + append!(force_t_2, sim.grains[2].force[2]) + append!(torque_1, sim.grains[1].torque[3]) + append!(torque_2, sim.grains[2].torque[3]) + end + PyPlot.clf() + PyPlot.subplot(3,1,1) + PyPlot.plot(time, force_n_1, "-b", label="1") + PyPlot.plot(time, force_n_2, "--y", label="2") + PyPlot.legend(loc="upper right") + PyPlot.ylabel("\$f_x\$ [N]") + PyPlot.subplot(3,1,2) + PyPlot.plot(time, force_t_1, "-b", label="1") + PyPlot.plot(time, force_t_2, "--y", label="2") + PyPlot.legend(loc="upper right") + PyPlot.ylabel("\$f_y\$ [N]") + PyPlot.subplot(3,1,3) + PyPlot.plot(time, torque_1, "-b", label="1") + PyPlot.plot(time, torque_2, "--y", label="2") + PyPlot.legend(loc="upper right") + PyPlot.ylabel("Torque [Nm]") + PyPlot.xlabel("Time [s]") + PyPlot.tight_layout() + PyPlot.savefig(output) +end + +Compat.@info "Testing compressive failure: uniaxial compression" +sim = Granular.createSimulation("compressive_failure_uniaxial") +Granular.addGrainCylindrical!(sim, [0.,0.], 1., 0.5, + fracture_toughness=1285e3, + lin_vel=[1., 0.], fixed=true, verbose=verbose) +Granular.addGrainCylindrical!(sim, [2.,0.], 1., 0.5, + fracture_toughness=1285e3, + fixed=true, verbose=verbose) +@test count(x->x==true, sim.grains[1].compressive_failure) == 0 +Granular.setTimeStep!(sim, verbose=verbose) +Granular.setTotalTime!(sim, 1.0) + +if debug + Granular.removeSimulationFiles(sim) + Granular.setOutputFileInterval!(sim, 0.01) + plot_interaction(sim, sim.id * ".pdf") +else + Granular.run!(sim, verbose=verbose) +end + +@test sim.grains[1].compressive_failure[1] == true +@test count(x->x==true, sim.grains[1].compressive_failure) == 1 +@test sim.grains[1].force[1] < 0.0 +@test sim.grains[1].force[2] ≈ 0.0 +@test sim.grains[2].force[1] > 0.0 +@test sim.grains[2].force[2] ≈ 0.0 +@test sim.grains[1].torque ≈ zeros(3) +@test sim.grains[2].torque ≈ zeros(3) + +Compat.@info "Testing compressive failure: shear" +sim = Granular.createSimulation("compressive_failure_shear") +Granular.addGrainCylindrical!(sim, [0.,0.], 1., 0.5, + fracture_toughness=1285e3, + lin_vel=[0., 1.], fixed=true, verbose=verbose) +Granular.addGrainCylindrical!(sim, [1.5,1.5], 1., 0.5, + fracture_toughness=1285e3, + fixed=true, verbose=verbose) +@test count(x->x==true, sim.grains[1].compressive_failure) == 0 +Granular.setTimeStep!(sim, verbose=verbose) +Granular.setTotalTime!(sim, 1.0) + +if debug + Granular.removeSimulationFiles(sim) + Granular.setOutputFileInterval!(sim, 0.01) + plot_interaction(sim, sim.id * ".pdf") +else + Granular.run!(sim, verbose=verbose) +end + +@test sim.grains[1].compressive_failure[1] == true +@test count(x->x==true, sim.grains[1].compressive_failure) == 1 +@test sim.grains[1].force[1] > 0.0 +@test sim.grains[1].force[2] < 0.0 +@test abs(sim.grains[1].force[1]) < abs(sim.grains[1].force[2]) +@test sim.grains[2].force[1] < 0.0 +@test sim.grains[2].force[2] > 0.0 +@test abs(sim.grains[2].force[1]) < abs(sim.grains[2].force[2]) +@test sim.grains[1].torque[1:2] ≈ zeros(2) +@test sim.grains[1].torque[3] < 0.0 +@test sim.grains[2].torque[1:2] ≈ zeros(2) +@test sim.grains[2].torque[3] < 0.0 + +Compat.@info "Testing robustness of overlap calculations" +sim = Granular.createSimulation("overlap") +Granular.addGrainCylindrical!(sim, [0.,0.], 1., 0.5, + fracture_toughness=1285e3, + lin_vel=[0., 1.], fixed=true, verbose=verbose) +Granular.addGrainCylindrical!(sim, [2.,0.], 1., 0.5, + fracture_toughness=1285e3, + fixed=true, verbose=verbose) +@test count(x->x==true, sim.grains[1].compressive_failure) == 0 +Granular.setTimeStep!(sim, verbose=verbose) +Granular.setTotalTime!(sim, 1.0) +Granular.run!(sim, single_step=true, verbose=verbose) +@test sim.grains[1].compressive_failure[1] == false +@test sim.grains[1].contact_area[1] == 0.0 +@test count(x->x==true, sim.grains[1].compressive_failure) == 0 + +sim = Granular.createSimulation("overlap") +Granular.addGrainCylindrical!(sim, [0.,0.], 1., 0.5, + fracture_toughness=1., + fixed=true, verbose=verbose) +Granular.addGrainCylindrical!(sim, [0.0+1e-9,0.], 1., 0.5, + fracture_toughness=1., + fixed=true, verbose=verbose) +Granular.setTimeStep!(sim, verbose=verbose) +Granular.setTotalTime!(sim, 1.0) +Granular.run!(sim, single_step=true, verbose=verbose) +@test sim.grains[1].compressive_failure[1] == true +@test sim.grains[1].contact_area[1] ≈ π*1.0^2 + +sim = Granular.createSimulation("overlap") +Granular.addGrainCylindrical!(sim, [0.,0.], 1., 0.5, + fracture_toughness=1., + fixed=true, verbose=verbose) +Granular.addGrainCylindrical!(sim, [0.1,0.], 1., 0.5, + fracture_toughness=1., + fixed=true, verbose=verbose) +Granular.setTimeStep!(sim, verbose=verbose) +Granular.setTotalTime!(sim, 1.0) +Granular.run!(sim, single_step=true, verbose=verbose) +@test sim.grains[1].compressive_failure[1] == true +@test sim.grains[1].contact_area[1] < π*1.0^2 +@test sim.grains[1].contact_area[1] > 0. + +sim = Granular.createSimulation("overlap") +Granular.addGrainCylindrical!(sim, [0.,0.], 1., 0.5, + fracture_toughness=1., + fixed=true, verbose=verbose) +Granular.addGrainCylindrical!(sim, [0.+1e-9,0.], 0.1, 0.5, + fracture_toughness=1., + fixed=true, verbose=verbose) +@test count(x->x==true, sim.grains[1].compressive_failure) == 0 +Granular.setTimeStep!(sim, verbose=verbose) +Granular.setTotalTime!(sim, 1.0) +Granular.run!(sim, single_step=true, verbose=verbose) +@test sim.grains[1].position_vector[1] ≈ [-1e-9, 0., 0.] +@test sim.grains[1].compressive_failure[1] == true +@test sim.grains[1].contact_area[1] ≈ π*0.1^2 + +sim = Granular.createSimulation("overlap") +Granular.addGrainCylindrical!(sim, [0.,0.], 1., 0.5, + fracture_toughness=1., + fixed=true, verbose=verbose) +Granular.addGrainCylindrical!(sim, [0.3,0.4], 0.1, 0.5, + fracture_toughness=1., + fixed=true, verbose=verbose) +@test count(x->x==true, sim.grains[1].compressive_failure) == 0 +Granular.setTimeStep!(sim, verbose=verbose) +Granular.setTotalTime!(sim, 1.0) +Granular.run!(sim, single_step=true, verbose=verbose) +@test sim.grains[1].compressive_failure[1] == true +@test sim.grains[1].contact_area[1] ≈ π*0.1^2 diff --git a/test/contact-search-and-geometry.jl b/test/contact-search-and-geometry.jl @@ -4,8 +4,6 @@ import Granular # Check the contact search and geometry of a two-particle interaction -Compat.@info "#### $(basename(@__FILE__)) ####" - Compat.@info "Testing interGrainPositionVector(...) and findOverlap(...)" sim = Granular.createSimulation("test") sim = Granular.createSimulation(id="test") @@ -15,7 +13,7 @@ Granular.addGrainCylindrical!(sim, [18.01, 0.01], 10., 1., verbose=false) position_ij = Granular.interGrainPositionVector(sim, 1, 2) overlap_ij = Granular.findOverlap(sim, 1, 2, position_ij) -@test [-18., 0.] ≈ position_ij +@test [-18., 0., 0.] ≈ position_ij @test -2. ≈ overlap_ij @@ -31,16 +29,16 @@ Granular.findContacts!(sim) sim.grains[1].enabled = false # The contact should be registered in ice floe 1, but not ice floe 2 @test 2 == sim.grains[1].contacts[1] -@test [-18., 0.] ≈ sim.grains[1].position_vector[1] +@test [-18., 0., 0.] ≈ sim.grains[1].position_vector[1] for ic=2:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 1 == sim.grains[1].n_contacts @test 1 == sim.grains[2].n_contacts @@ -50,16 +48,16 @@ sim = deepcopy(sim_copy) Granular.findContacts!(sim) @test 2 == sim.grains[1].contacts[1] -@test [-18., 0.] ≈ sim.grains[1].position_vector[1] +@test [-18., 0., 0.] ≈ sim.grains[1].position_vector[1] for ic=2:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 1 == sim.grains[1].n_contacts @test 1 == sim.grains[2].n_contacts @@ -72,13 +70,13 @@ sim.grains[2].enabled = false Granular.findContacts!(sim) for ic=1:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 0 == sim.grains[1].n_contacts @test 0 == sim.grains[2].n_contacts @@ -89,13 +87,13 @@ Granular.disableGrain!(sim, 1) Granular.findContacts!(sim) for ic=1:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 0 == sim.grains[1].n_contacts @test 0 == sim.grains[2].n_contacts @@ -107,13 +105,13 @@ Granular.disableGrain!(sim, 2) Granular.findContacts!(sim) for ic=1:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 0 == sim.grains[1].n_contacts @test 0 == sim.grains[2].n_contacts @@ -125,16 +123,16 @@ Granular.interact!(sim) Granular.findContacts!(sim) @test 2 == sim.grains[1].contacts[1] -@test [-18., 0.] ≈ sim.grains[1].position_vector[1] +@test [-18., 0., 0.] ≈ sim.grains[1].position_vector[1] for ic=2:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 1 == sim.grains[1].n_contacts @test 1 == sim.grains[2].n_contacts @@ -149,13 +147,13 @@ Granular.findContactsInGrid!(sim, sim.ocean) @test 2 == sim.grains[1].contacts[1] for ic=2:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 1 == sim.grains[1].n_contacts @test 1 == sim.grains[2].n_contacts @@ -170,13 +168,13 @@ Granular.findContactsInGrid!(sim, sim.ocean) @test 2 == sim.grains[1].contacts[1] for ic=2:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 1 == sim.grains[1].n_contacts @test 1 == sim.grains[2].n_contacts @@ -191,13 +189,13 @@ Granular.findContactsInGrid!(sim, sim.ocean) for ic=1:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 0 == sim.grains[1].n_contacts @test 0 == sim.grains[2].n_contacts @@ -211,13 +209,13 @@ Granular.findContacts!(sim) @test 2 == sim.grains[1].contacts[1] for ic=2:sim.Nc_max @test 0 == sim.grains[1].contacts[ic] - @test [0., 0.] ≈ sim.grains[1].position_vector[ic] - @test [0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[1].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[1].contact_parallel_displacement[ic] end for ic=1:sim.Nc_max @test 0 == sim.grains[2].contacts[ic] - @test [0., 0.] ≈ sim.grains[2].position_vector[ic] - @test [0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] + @test [0., 0., 0.] ≈ sim.grains[2].position_vector[ic] + @test [0., 0., 0.] ≈ sim.grains[2].contact_parallel_displacement[ic] end @test 1 == sim.grains[1].n_contacts @test 1 == sim.grains[2].n_contacts @@ -302,5 +300,3 @@ for j=2:(ny-1) @test sim.grains[idx].n_contacts == 4 end end - - diff --git a/test/grain.jl b/test/grain.jl @@ -2,22 +2,20 @@ # Check the basic icefloe functionality -Compat.@info "#### $(basename(@__FILE__)) ####" - Compat.@info "Writing simple simulation to VTK file" sim = Granular.createSimulation(id="test") Granular.addGrainCylindrical!(sim, [ 0., 0.], 10., 1., verbose=false) Granular.printGrainInfo(sim.grains[1]) Compat.@info "Testing grain value checks " -@test_throws ErrorException Granular.addGrainCylindrical!(sim, [.1, .1, .1], +@test_throws ErrorException Granular.addGrainCylindrical!(sim, [.1, .1, .1, .1], 10., 1.) @test_throws ErrorException Granular.addGrainCylindrical!(sim, [.1, .1], 10., 1., - lin_vel=[.2,.2,.2]) + lin_vel=[.2,.2,.2,.2]) @test_throws ErrorException Granular.addGrainCylindrical!(sim, [.1, .1], 10., 1., - lin_acc=[.2,.2,.2]) + lin_acc=[.2,.2,.2,.2]) @test_throws ErrorException Granular.addGrainCylindrical!(sim, [.1, .1], 0., 1.) @test_throws ErrorException Granular.addGrainCylindrical!(sim, [.1, .1], @@ -82,18 +80,18 @@ end Compat.@info "Testing external body force routines" sim = Granular.createSimulation(id="test") Granular.addGrainCylindrical!(sim, [ 0., 0.], 10., 1., verbose=false) -Granular.setBodyForce!(sim.grains[1], [1., 2.]) -@test sim.grains[1].external_body_force ≈ [1., 2.] -Granular.addBodyForce!(sim.grains[1], [1., 2.]) -@test sim.grains[1].external_body_force ≈ [2., 4.] +Granular.setBodyForce!(sim.grains[1], [1., 2., 0.]) +@test sim.grains[1].external_body_force ≈ [1., 2., 0.] +Granular.addBodyForce!(sim.grains[1], [1., 2., 0.]) +@test sim.grains[1].external_body_force ≈ [2., 4., 0.] Compat.@info "Testing zeroKinematics!()" -sim.grains[1].force .= ones(2) -sim.grains[1].lin_acc .= ones(2) -sim.grains[1].lin_vel .= ones(2) -sim.grains[1].torque = 1. -sim.grains[1].ang_acc = 1. -sim.grains[1].ang_vel = 1. +sim.grains[1].force .= ones(3) +sim.grains[1].lin_acc .= ones(3) +sim.grains[1].lin_vel .= ones(3) +sim.grains[1].torque .= ones(3) +sim.grains[1].ang_acc .= ones(3) +sim.grains[1].ang_vel .= ones(3) Granular.zeroKinematics!(sim) @test Granular.totalGrainKineticTranslationalEnergy(sim) ≈ 0. @test Granular.totalGrainKineticRotationalEnergy(sim) ≈ 0. diff --git a/test/grid-boundaries.jl b/test/grid-boundaries.jl @@ -1,8 +1,6 @@ #!/usr/bin/env julia import Compat -Compat.@info "#### $(basename(@__FILE__)) ####" - verbose=false Compat.@info "## Inactive/Periodic BCs" @@ -185,7 +183,7 @@ sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.]) Granular.setGridBoundaryConditions!(sim.ocean, "inactive", verbose=false) Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false) Granular.moveGrainsAcrossPeriodicBoundaries!(sim) -@test [0.1, 0.5] ≈ sim.grains[1].lin_pos +@test [0.1, 0.5, 0.] ≈ sim.grains[1].lin_pos # do not readjust inside grid, periodic boundaries sim = Granular.createSimulation() @@ -193,7 +191,7 @@ sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.]) Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false) Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false) Granular.moveGrainsAcrossPeriodicBoundaries!(sim) -@test [0.1, 0.5] ≈ sim.grains[1].lin_pos +@test [0.1, 0.5, 0.] ≈ sim.grains[1].lin_pos # do not readjust outside grid, inactive boundaries sim = Granular.createSimulation() @@ -201,7 +199,7 @@ sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.]) Granular.setGridBoundaryConditions!(sim.ocean, "inactive", verbose=false) Granular.addGrainCylindrical!(sim, [-0.1, 0.5], 0.11, 0.1, verbose=false) Granular.moveGrainsAcrossPeriodicBoundaries!(sim) -@test [-0.1, 0.5] ≈ sim.grains[1].lin_pos +@test [-0.1, 0.5, 0.] ≈ sim.grains[1].lin_pos # readjust outside grid, periodic boundaries, -x sim = Granular.createSimulation() @@ -209,7 +207,7 @@ sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.]) Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false) Granular.addGrainCylindrical!(sim, [-0.1, 0.5], 0.11, 0.1, verbose=false) Granular.moveGrainsAcrossPeriodicBoundaries!(sim) -@test [0.9, 0.5] ≈ sim.grains[1].lin_pos +@test [0.9, 0.5, 0.] ≈ sim.grains[1].lin_pos # readjust outside grid, periodic boundaries, +x sim = Granular.createSimulation() @@ -217,7 +215,7 @@ sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.]) Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false) Granular.addGrainCylindrical!(sim, [1.1, 0.5], 0.11, 0.1, verbose=false) Granular.moveGrainsAcrossPeriodicBoundaries!(sim) -@test [0.1, 0.5] ≈ sim.grains[1].lin_pos +@test [0.1, 0.5, 0.] ≈ sim.grains[1].lin_pos # readjust outside grid, periodic boundaries, -y sim = Granular.createSimulation() @@ -225,7 +223,7 @@ sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.]) Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false) Granular.addGrainCylindrical!(sim, [0.3, -0.1], 0.11, 0.1, verbose=false) Granular.moveGrainsAcrossPeriodicBoundaries!(sim) -@test [0.3, 0.9] ≈ sim.grains[1].lin_pos +@test [0.3, 0.9, 0.] ≈ sim.grains[1].lin_pos # readjust outside grid, periodic boundaries, +y sim = Granular.createSimulation() @@ -233,7 +231,7 @@ sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.]) Granular.setGridBoundaryConditions!(sim.ocean, "periodic", verbose=false) Granular.addGrainCylindrical!(sim, [0.3, 1.1], 0.11, 0.1, verbose=false) Granular.moveGrainsAcrossPeriodicBoundaries!(sim) -@test [0.3, 0.1] ≈ sim.grains[1].lin_pos +@test [0.3, 0.1, 0.] ≈ sim.grains[1].lin_pos # throw error if atmosphere and ocean BCs differ sim = Granular.createSimulation() @@ -253,4 +251,4 @@ sim.ocean = Granular.createRegularOceanGrid([5, 5, 2], [1., 1., 1.]) Granular.setGridBoundaryConditions!(sim.ocean, "inactive", verbose=false) Granular.addGrainCylindrical!(sim, [0.1, 0.5], 0.11, 0.1, verbose=false) Granular.moveGrainsAcrossPeriodicBoundaries!(sim) -@test [0.1, 0.5] ≈ sim.grains[1].lin_pos +@test [0.1, 0.5, 0.] ≈ sim.grains[1].lin_pos diff --git a/test/grid.jl b/test/grid.jl @@ -5,8 +5,6 @@ import Granular # Check the grid interpolation and sorting functions verbose = false -Compat.@info "#### $(basename(@__FILE__)) ####" - if Granular.hasNetCDF ocean = Granular.readOceanNetCDF("Baltic/00010101.ocean_month.nc", "Baltic/ocean_hgrid.nc") diff --git a/test/jld.jl b/test/jld.jl @@ -1,8 +1,6 @@ #!/usr/bin/env julia import Compat -Compat.@info "#### $(basename(@__FILE__)) ####" - Compat.@info "Determining if JLD is installed" if Granular.hasJLD Compat.@info "JLD found, proceeding with JLD-specific tests" diff --git a/test/netcdf.jl b/test/netcdf.jl @@ -2,8 +2,6 @@ # Check if NetCDF files are read correctly from the disk. -Compat.@info "#### $(basename(@__FILE__)) ####" - @test_throws ErrorException Granular.readOceanStateNetCDF("nonexistentfile") @test_throws ErrorException Granular.readOceanGridNetCDF("nonexistentfile") diff --git a/test/ocean.jl b/test/ocean.jl @@ -3,8 +3,6 @@ # Check if ocean-specific functions and grid operations are functioning # correctly -Compat.@info "#### $(basename(@__FILE__)) ####" - Compat.@info "Testing regular grid generation" sim = Granular.createSimulation() sim.ocean = Granular.createRegularOceanGrid([6, 6, 6], [1., 1., 1.]) @@ -58,15 +56,15 @@ E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) Compat.@info "Testing vortex interaction (static ocean)" sim = deepcopy(sim_init) -sim.grains[1].ang_vel = 0.1 +sim.grains[1].ang_vel[3] = 0.1 E_kin_lin_init = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) @test E_kin_rot_init > E_kin_rot_final # energy lost to ocean -@test sim.grains[1].ang_vel > 0. # check angular velocity orientation -@test sim.grains[1].ang_pos > 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] > 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] > 0. # check angular position orientation @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained Compat.@info "Testing vortex interaction (static ice floe)" @@ -81,8 +79,8 @@ E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) -@test sim.grains[1].ang_vel > 0. # check angular velocity orientation -@test sim.grains[1].ang_pos > 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] > 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] > 0. # check angular position orientation @test E_kin_rot_init < E_kin_rot_final # rotation after due to ocean vortex @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained @@ -97,8 +95,8 @@ E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) -@test sim.grains[1].ang_vel < 0. # check angular velocity orientation -@test sim.grains[1].ang_pos < 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] < 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] < 0. # check angular position orientation @test E_kin_rot_init < E_kin_rot_final # rotation after due to ocean vortex @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained @@ -113,8 +111,8 @@ E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) -@test sim.grains[1].ang_vel < 0. # check angular velocity orientation -@test sim.grains[1].ang_pos < 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] < 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] < 0. # check angular position orientation @test E_kin_rot_init < E_kin_rot_final # rotation after due to ocean vortex @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained @@ -129,7 +127,7 @@ E_kin_rot_init = Granular.totalGrainKineticRotationalEnergy(sim) Granular.run!(sim, verbose=false) E_kin_lin_final = Granular.totalGrainKineticTranslationalEnergy(sim) E_kin_rot_final = Granular.totalGrainKineticRotationalEnergy(sim) -@test sim.grains[1].ang_vel > 0. # check angular velocity orientation -@test sim.grains[1].ang_pos > 0. # check angular position orientation +@test sim.grains[1].ang_vel[3] > 0. # check angular velocity orientation +@test sim.grains[1].ang_pos[3] > 0. # check angular position orientation @test E_kin_rot_init < E_kin_rot_final # rotation after due to ocean vortex @test E_kin_lin_init ≈ E_kin_lin_final # no linear velocity gained diff --git a/test/packing.jl b/test/packing.jl @@ -6,8 +6,6 @@ verbose = false plot = false plot_packings=false -Compat.@info "#### $(basename(@__FILE__)) ####" - Compat.@info "Testing regular packing generation (power law GSD)" sim = Granular.createSimulation() Granular.regularPacking!(sim, [2, 2], 1., 1., size_distribution="powerlaw") diff --git a/test/profiling.jl b/test/profiling.jl @@ -9,8 +9,6 @@ import Plots import Granular import CurveFit -Compat.@info "#### $(basename(@__FILE__)) ####" - verbose=false Compat.@info "Testing performance with many interacting grains" diff --git a/test/runtests.jl b/test/runtests.jl @@ -3,22 +3,28 @@ using Compat.Test using Compat.LinearAlgebra import Granular -include("collision-2floes-normal.jl") -include("collision-5floes-normal.jl") -include("collision-2floes-oblique.jl") -include("grid.jl") -include("contact-search-and-geometry.jl") -include("grid-boundaries.jl") -include("ocean.jl") -include("atmosphere.jl") -include("wall.jl") -include("grain.jl") -include("packing.jl") -include("util.jl") -include("temporal.jl") -include("cohesion.jl") +function run_test(filename::String) + Compat.printstyled("Info: #### $filename ####\n", color=:green) + include(filename) +end + +run_test("compressive_failure.jl") +run_test("cohesion.jl") +run_test("grain.jl") +run_test("vtk.jl") +run_test("collision-2floes-normal.jl") +run_test("collision-5floes-normal.jl") +run_test("collision-2floes-oblique.jl") +run_test("grid.jl") +run_test("contact-search-and-geometry.jl") +run_test("grid-boundaries.jl") +run_test("ocean.jl") +run_test("atmosphere.jl") +run_test("wall.jl") +run_test("packing.jl") +run_test("util.jl") +run_test("temporal.jl") if Granular.hasNetCDF - include("netcdf.jl") + run_test("netcdf.jl") end -include("vtk.jl") -include("jld.jl") +run_test("jld.jl") diff --git a/test/util.jl b/test/util.jl @@ -1,8 +1,8 @@ #!/usr/bin/env julia +import Granular import Compat using Compat.Random - -Compat.@info "#### $(basename(@__FILE__)) ####" +using Compat.Test Compat.@info "Testing power-law RNG" @@ -26,3 +26,12 @@ for i=1:10^5 @test 0. <= minimum(Granular.randpower(5, 1., 0., 1.)) @test 1. >= minimum(Granular.randpower(5, 1., 0., 1.)) end + +@test [1,2,0] == Granular.vecTo3d([1,2]) +@test [1,2,3] == Granular.vecTo3d([1,2], fill=3) +@test [1,3,3] == Granular.vecTo3d([1], fill=3) +@test [1,3,3] == Granular.vecTo3d(1, fill=3) +@test [1.,2.,3.] == Granular.vecTo3d([1.,2.], fill=3.) +@test [1.,3.,3.] == Granular.vecTo3d(1., fill=3.) +@test [1.,0.,0.] == Granular.vecTo3d(1.) +@test [1.,0.,0.] == Granular.vecTo3d([1.]) diff --git a/test/vtk.jl b/test/vtk.jl @@ -3,8 +3,6 @@ import Compat # Check the contact search and geometry of a two-particle interaction -Compat.@info "#### $(basename(@__FILE__)) ####" - Compat.@info "Writing simple simulation to VTK file" sim = Granular.createSimulation(id="test") Granular.addGrainCylindrical!(sim, [ 0., 0.], 10., 1., verbose=false) @@ -29,12 +27,12 @@ end grainpath = "test/test.grains.1.vtu" grainchecksum = -"eff130f14975dd5da06186c5b61c0c1a9d679f2188d03019c8144898ddc902b6 " * +"b1748cb82e8270d951d9c1acaea0d151f0a5c48a1255e75de350bf9bcbc15fe9 " * grainpath * "\n" graininteractionpath = "test/test.grain-interaction.1.vtp" graininteractionchecksum = -"c61f314a997c4405eaa98e6a4e3f81368ab2e1c60d06d9a0d998b69d7c8fb1bf " * +"b8e49252a0ac87c2fce05e68ffab46589853429dc9f75d89818e4a37b953b137 " * graininteractionpath * "\n" oceanpath = "test/test.ocean.1.vts" diff --git a/test/wall.jl b/test/wall.jl @@ -2,14 +2,12 @@ # Check the basic dynamic wall functionality -Compat.@info "#### $(basename(@__FILE__)) ####" - Compat.@info "# Test wall initialization" Compat.@info "Testing argument value checks" sim = Granular.createSimulation() Granular.addGrainCylindrical!(sim, [ 0., 0.], 10., 2., verbose=false) @test_throws ErrorException Granular.addWallLinearFrictionless!(sim, - [.1, .1, .1], + [.1, .1, .1, .1], 1.) @test_throws ErrorException Granular.addWallLinearFrictionless!(sim, [1., 1.], @@ -18,7 +16,7 @@ Granular.addGrainCylindrical!(sim, [ 0., 0.], 10., 2., verbose=false) [.1, .1, .1], 1.) @test_throws ErrorException Granular.addWallLinearFrictionless!(sim, - [0., 1.], 1., + [0., 1., 1.], 1., bc="asdf") sim = Granular.createSimulation() @test_throws ErrorException Granular.addWallLinearFrictionless!(sim, [1., 0.], @@ -56,7 +54,7 @@ sim.walls[1].force = 1.0 @test Granular.getWallNormalStress(sim, wall_index=1, stress_type="effective") ≈ 1.0/(20.0*2.0) @test_throws ErrorException Granular.getWallNormalStress(sim, wall_index=1, stress_type="nonexistent") -sim.walls[1].normal = [1.0, 1.0] +sim.walls[1].normal = [1.0, 1.0, 1.0] @test_throws ErrorException Granular.getWallSurfaceArea(sim, 1) @test_throws ErrorException Granular.getWallSurfaceArea(sim, [1.,1.], 0.5)