Granular.jl

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

commit b189f7b4e3f1e021691cc3a766b66bf2f2557538
parent 87def518c59023ee58581b32919e0aa9cea9ed6c
Author: Anders Damsgaard <andersd@riseup.net>
Date:   Fri,  8 Sep 2017 12:03:06 -0400

Merge branch 'master' of github.com:anders-dc/SeaIce.jl

Diffstat:
MREQUIRE | 1+
Msrc/SeaIce.jl | 1+
Msrc/atmosphere.jl | 55+++++++++++++++++++++++++++++--------------------------
Msrc/contact_search.jl | 73+++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/datatypes.jl | 209+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/grid.jl | 193++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/icefloe.jl | 235+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/interaction.jl | 23++++++++---------------
Msrc/io.jl | 62++++++++++++++++++++++++++++++++++----------------------------
Msrc/ocean.jl | 46++++++++++++++++++++++++++--------------------
Msrc/simulation.jl | 55+++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/temporal.jl | 39++++++++++++++++++++++++---------------
Msrc/temporal_integration.jl | 3+++
Asrc/util.jl | 43+++++++++++++++++++++++++++++++++++++++++++
Mtest/contact-search-and-geometry.jl | 44++++++++++++++++++++++++++++++++++++++++++++
Mtest/grid.jl | 74++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mtest/icefloe.jl | 10++++++++++
Atest/memory-management.jl | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/profiling.jl | 55++++++++++++++++++++++++++++++++++++++++++++++++++-----
Atest/profiling.sh | 18++++++++++++++++++
Mtest/runtests.jl | 2++
Atest/util.jl | 28++++++++++++++++++++++++++++
22 files changed, 1031 insertions(+), 413 deletions(-)

diff --git a/REQUIRE b/REQUIRE @@ -1,3 +1,4 @@ julia 0.6 WriteVTK NetCDF +PyPlot diff --git a/src/SeaIce.jl b/src/SeaIce.jl @@ -19,5 +19,6 @@ include("temporal_integration.jl") include("io.jl") include("ocean.jl") include("atmosphere.jl") +include("util.jl") end # module end diff --git a/src/atmosphere.jl b/src/atmosphere.jl @@ -15,7 +15,7 @@ function createEmptyAtmosphere() zeros(1,1,1,1), zeros(1,1,1,1), - Array{Array{Int, 1}}(1, 1), + Array{Vector{Int}}(1, 1), false) end @@ -25,16 +25,16 @@ export interpolateAtmosphereVelocitiesToCorners Convert gridded data from Arakawa-C type (decomposed velocities at faces) to Arakawa-B type (velocities at corners) through interpolation. """ -function interpolateAtmosphereVelocitiesToCorners(u_in::Array{float, 4}, - v_in::Array{float, 4}) +function interpolateAtmosphereVelocitiesToCorners(u_in::Array{Float64, 4}, + v_in::Array{Float64, 4}) if size(u_in) != size(v_in) error("size of u_in ($(size(u_in))) must match v_in ($(size(v_in)))") end nx, ny, nz, nt = size(u_in) - #u = Array{float}(nx+1, ny+1, nz, nt) - #v = Array{float}(nx+1, ny+1, nz, nt) + #u = Array{Float64}(nx+1, ny+1, nz, nt) + #v = Array{Float64}(nx+1, ny+1, nz, nt) u = zeros(nx+1, ny+1, nz, nt) v = zeros(nx+1, ny+1, nz, nt) for i=1:nx @@ -62,7 +62,7 @@ steps to get the approximate atmosphere state at any point in time. If the `Atmosphere` data set only contains a single time step, values from that time are returned. """ -function interpolateAtmosphereState(atmosphere::Atmosphere, t::float) +function interpolateAtmosphereState(atmosphere::Atmosphere, t::Float64) if length(atmosphere.time) == 1 return atmosphere.u, atmosphere.v elseif t < atmosphere.time[1] || t > atmosphere.time[end] @@ -102,10 +102,10 @@ one 4-th dimension matrix per `time` step. Sea surface will be at `z=0.` with the atmosphere spanning `z<0.`. Vertical indexing starts with `k=0` at the sea surface, and increases downwards. """ -function createRegularAtmosphereGrid(n::Array{Int, 1}, - L::Array{float, 1}; - origo::Array{float, 1} = zeros(2), - time::Array{float, 1} = zeros(1), +function createRegularAtmosphereGrid(n::Vector{Int}, + L::Vector{Float64}; + origo::Vector{Float64} = zeros(2), + time::Array{Float64, 1} = zeros(1), name::String = "unnamed") xq = repmat(linspace(origo[1], L[1], n[1] + 1), 1, n[2] + 1) @@ -141,6 +141,11 @@ function addAtmosphereDrag!(simulation::Simulation) end u, v = interpolateAtmosphereState(simulation.atmosphere, simulation.time) + uv_interp = Vector{Float64}(2) + sw = Vector{Float64}(2) + se = Vector{Float64}(2) + ne = Vector{Float64}(2) + nw = Vector{Float64}(2) for ice_floe in simulation.ice_floes @@ -163,17 +168,13 @@ function addAtmosphereDrag!(simulation::Simulation) """) end - applyAtmosphereDragToIceFloe!(ice_floe, - bilinearInterpolation(u, - x_tilde, y_tilde, - i, j, k, 1), - bilinearInterpolation(v, - x_tilde, y_tilde, - i, j, k, 1)) + bilinearInterpolation!(uv_interp, u, v, x_tilde, y_tilde, i, j, k, 1) + applyAtmosphereDragToIceFloe!(ice_floe, uv_interp[1], uv_interp[2]) applyAtmosphereVorticityToIceFloe!(ice_floe, - curl(simulation.atmosphere, - x_tilde, y_tilde, i, j, k, 1)) + curl(simulation.atmosphere, x_tilde, y_tilde, + i, j, k, 1, sw, se, ne, nw)) end + nothing end export applyAtmosphereDragToIceFloe! @@ -182,18 +183,19 @@ Add Stokes-type drag from velocity difference between atmosphere and a single ice floe. """ function applyAtmosphereDragToIceFloe!(ice_floe::IceFloeCylindrical, - u::float, v::float) - freeboard = .1*ice_floe.thickness # height above water + u::Float64, v::Float64) rho_a = 1.2754 # atmosphere density length = ice_floe.areal_radius*2. width = ice_floe.areal_radius*2. - drag_force = rho_a * (.5*ice_floe.ocean_drag_coeff_vert*width*freeboard + - ice_floe.atmosphere_drag_coeff_horiz*length*width) * + drag_force = rho_a * + (.5*ice_floe.ocean_drag_coeff_vert*width*.1*ice_floe.thickness + + ice_floe.atmosphere_drag_coeff_horiz*length*width) * ([u, v] - ice_floe.lin_vel)*norm([u, v] - ice_floe.lin_vel) ice_floe.force += drag_force ice_floe.atmosphere_stress = drag_force/ice_floe.horizontal_surface_area + nothing end export applyAtmosphereVorticityToIceFloe! @@ -203,16 +205,16 @@ single ice floe. See Eq. 9.28 in "Introduction to Fluid Mechanics" by Nakayama and Boucher, 1999. """ function applyAtmosphereVorticityToIceFloe!(ice_floe::IceFloeCylindrical, - atmosphere_curl::float) - freeboard = .1*ice_floe.thickness # height above water + atmosphere_curl::Float64) rho_a = 1.2754 # atmosphere density ice_floe.torque += pi*ice_floe.areal_radius^4.*rho_a* (ice_floe.areal_radius/5.*ice_floe.atmosphere_drag_coeff_horiz + - freeboard*ice_floe.atmosphere_drag_coeff_vert)* + .1*ice_floe.thickness*ice_floe.atmosphere_drag_coeff_vert)* abs(.5*atmosphere_curl - ice_floe.ang_vel)* (.5*atmosphere_curl - ice_floe.ang_vel) + nothing end export compareAtmospheres @@ -240,4 +242,5 @@ function compareAtmospheres(atmosphere1::Atmosphere, atmosphere2::Atmosphere) if isassigned(atmosphere1.ice_floe_list, 1) Base.Test.@test atmosphere1.ice_floe_list == atmosphere2.ice_floe_list end + nothing end diff --git a/src/contact_search.jl b/src/contact_search.jl @@ -32,6 +32,7 @@ function findContacts!(simulation::Simulation; else error("Unknown contact search method '$method'") end + nothing end export interIceFloePositionVector @@ -47,18 +48,19 @@ Returns a `vector` pointing from ice floe `i` to ice floe `j` in the * `j::Int`: index of the second ice floe. """ function interIceFloePositionVector(simulation::Simulation, - i::Integer, j::Integer) - return simulation.ice_floes[i].lin_pos - simulation.ice_floes[j].lin_pos + i::Int, j::Int) + @inbounds return simulation.ice_floes[i].lin_pos - + simulation.ice_floes[j].lin_pos end """ position_ij is the inter-grain position vector, and can be found with interIceFloePositionVector(). """ -function findOverlap(simulation::Simulation, i::Integer, j::Integer, - position_ij::vector) - return norm(position_ij) - (simulation.ice_floes[i].contact_radius + - simulation.ice_floes[j].contact_radius) +function findOverlap(simulation::Simulation, i::Int, j::Int, + position_ij::Vector{Float64}) + @inbounds return norm(position_ij) - (simulation.ice_floes[i].contact_radius + + simulation.ice_floes[j].contact_radius) end export findContactsAllToAll! @@ -70,13 +72,14 @@ Perform an O(n^2) all-to-all contact search between all ice floes in the """ function findContactsAllToAll!(simulation::Simulation) - for i = 1:length(simulation.ice_floes) + @inbounds for i = 1:length(simulation.ice_floes) # Check contacts with other grains for j = 1:length(simulation.ice_floes) checkAndAddContact!(simulation, i, j) end end + nothing end export findContactsInGrid! @@ -107,12 +110,13 @@ function findContactsInGrid!(simulation::Simulation, grid::Any) continue end - for idx_j in grid.ice_floe_list[i, j] + @inbounds for idx_j in grid.ice_floe_list[i, j] checkAndAddContact!(simulation, idx_i, idx_j) end end end end + nothing end export checkAndAddContact! @@ -134,7 +138,7 @@ written to `simulation.contact_parallel_displacement`. function checkAndAddContact!(sim::Simulation, i::Int, j::Int) if i < j - if (sim.ice_floes[i].fixed && sim.ice_floes[j].fixed) || + @inbounds if (sim.ice_floes[i].fixed && sim.ice_floes[j].fixed) || !sim.ice_floes[i].enabled || !sim.ice_floes[j].enabled return end @@ -143,28 +147,49 @@ function checkAndAddContact!(sim::Simulation, i::Int, j::Int) position_ij = interIceFloePositionVector(sim, i, j) overlap_ij = findOverlap(sim, i, j, position_ij) + contact_found = false + # Check if grains overlap (overlap when negative) if overlap_ij < 0. - for ic=1:(sim.Nc_max + 1) - if ic == (sim.Nc_max + 1) - error("contact $i-$j exceeds max. number of contacts " * - "(sim.Nc_max = $(sim.Nc_max)) for ice floe $i") - - else - if sim.ice_floes[i].contacts[ic] == j - break # contact already registered - - elseif sim.ice_floes[i].contacts[ic] == 0 # empty - sim.ice_floes[i].n_contacts += 1 # register new contact - sim.ice_floes[j].n_contacts += 1 - sim.ice_floes[i].contacts[ic] = j - fill!(sim.ice_floes[i]. + + # Check if contact is already registered + for ic=1:sim.Nc_max + @inbounds if sim.ice_floes[i].contacts[ic] == j + contact_found = true + break # contact already registered + end + end + + # Register as new contact in first empty position + if !contact_found + + for ic=1:(sim.Nc_max + 1) + + # Test if this contact exceeds the number of contacts + if ic == (sim.Nc_max + 1) + for ic=1:sim.Nc_max + warn("ice_floes[$i].contacts[$ic] = " * + "$(sim.ice_floes[i].contacts[ic])") + warn("ice_floes[$i].contact_age[$ic] = " * + "$(sim.ice_floes[i].contact_age[ic])") + end + error("contact $i-$j exceeds max. number of contacts " * + "(sim.Nc_max = $(sim.Nc_max)) for ice floe $i") + end + + # Register as new contact + @inbounds if sim.ice_floes[i].contacts[ic] == 0 # empty + @inbounds sim.ice_floes[i].n_contacts += 1 + @inbounds sim.ice_floes[j].n_contacts += 1 + @inbounds sim.ice_floes[i].contacts[ic] = j + @inbounds fill!(sim.ice_floes[i]. contact_parallel_displacement[ic] , 0.) - sim.ice_floes[i].contact_age[ic] = 0. + @inbounds sim.ice_floes[i].contact_age[ic] = 0. break end end end end end + nothing end diff --git a/src/datatypes.jl b/src/datatypes.jl @@ -1,35 +1,31 @@ -# Define floating point data types -const float = Float64 -const vector = Array{Float64, 1} - ## Particle composite types type IceFloeCylindrical # Material properties - density::float + density::Float64 # Geometrical parameters - thickness::float - contact_radius::float - areal_radius::float - circumreference::float - horizontal_surface_area::float - side_surface_area::float - volume::float - mass::float - moment_of_inertia::float + thickness::Float64 + contact_radius::Float64 + areal_radius::Float64 + circumreference::Float64 + horizontal_surface_area::Float64 + side_surface_area::Float64 + volume::Float64 + mass::Float64 + moment_of_inertia::Float64 # Linear kinematic degrees of freedom along horizontal plane - lin_pos::vector - lin_vel::vector - lin_acc::vector - force::vector + lin_pos::Vector{Float64} + lin_vel::Vector{Float64} + lin_acc::Vector{Float64} + force::Vector{Float64} # Angular kinematic degrees of freedom for vertical rotation around center - ang_pos::float - ang_vel::float - ang_acc::float - torque::float + ang_pos::Float64 + ang_vel::Float64 + ang_acc::Float64 + torque::Float64 # Kinematic constraint flags fixed::Bool @@ -37,97 +33,98 @@ type IceFloeCylindrical enabled::Bool # Rheological parameters - contact_stiffness_normal::float - contact_stiffness_tangential::float - contact_viscosity_normal::float - contact_viscosity_tangential::float - contact_static_friction::float - contact_dynamic_friction::float - - youngs_modulus::float - poissons_ratio::float - tensile_strength::float - tensile_heal_rate::float - compressive_strength_prefactor::float + contact_stiffness_normal::Float64 + contact_stiffness_tangential::Float64 + contact_viscosity_normal::Float64 + contact_viscosity_tangential::Float64 + contact_static_friction::Float64 + contact_dynamic_friction::Float64 + + youngs_modulus::Float64 + poissons_ratio::Float64 + tensile_strength::Float64 + tensile_heal_rate::Float64 + compressive_strength_prefactor::Float64 # Ocean/atmosphere interaction parameters - ocean_drag_coeff_vert::float - ocean_drag_coeff_horiz::float - atmosphere_drag_coeff_vert::float - atmosphere_drag_coeff_horiz::float + ocean_drag_coeff_vert::Float64 + ocean_drag_coeff_horiz::Float64 + atmosphere_drag_coeff_vert::Float64 + atmosphere_drag_coeff_horiz::Float64 # Interaction - pressure::float + pressure::Float64 n_contacts::Int - ocean_grid_pos::Array{Int, 1} - atmosphere_grid_pos::Array{Int, 1} - contacts::Array{Int, 1} - contact_parallel_displacement::Array{Array{Float64, 1}, 1} - contact_age::Array{Float64, 1} - - granular_stress::vector - ocean_stress::vector - atmosphere_stress::vector + ocean_grid_pos::Vector{Int} + atmosphere_grid_pos::Vector{Int} + contacts::Vector{Int} + contact_parallel_displacement::Vector{Vector{Float64}} + contact_age::Vector{Float64} + + granular_stress::Vector{Float64} + ocean_stress::Vector{Float64} + atmosphere_stress::Vector{Float64} end # Type for gathering data from ice floe objects into single arrays type IceFloeArrays # Material properties - density + density::Vector{Float64} # Geometrical parameters - thickness - contact_radius - areal_radius - circumreference - horizontal_surface_area - side_surface_area - volume - mass - moment_of_inertia + thickness::Vector{Float64} + contact_radius::Vector{Float64} + areal_radius::Vector{Float64} + circumreference::Vector{Float64} + horizontal_surface_area::Vector{Float64} + side_surface_area::Vector{Float64} + volume::Vector{Float64} + mass::Vector{Float64} + moment_of_inertia::Vector{Float64} # Linear kinematic degrees of freedom along horizontal plane - lin_pos - lin_vel - lin_acc - force + lin_pos::Array{Float64, 2} + lin_vel::Array{Float64, 2} + lin_acc::Array{Float64, 2} + force::Array{Float64, 2} # Angular kinematic degrees of freedom for vertical rotation around center - ang_pos - ang_vel - ang_acc - torque + ang_pos::Array{Float64, 2} + ang_vel::Array{Float64, 2} + ang_acc::Array{Float64, 2} + torque::Array{Float64, 2} # Kinematic constraint flags - fixed - rotating - enabled + fixed::Vector{Int} + rotating::Vector{Int} + enabled::Vector{Int} # Rheological parameters - contact_stiffness_normal - contact_stiffness_tangential - contact_viscosity_normal - contact_viscosity_tangential - contact_static_friction - contact_dynamic_friction - - youngs_modulus - poissons_ratio - tensile_strength - compressive_strength_prefactor - - ocean_drag_coeff_vert - ocean_drag_coeff_horiz - atmosphere_drag_coeff_vert - atmosphere_drag_coeff_horiz - - pressure - n_contacts - - granular_stress - ocean_stress - atmosphere_stress + contact_stiffness_normal::Vector{Float64} + contact_stiffness_tangential::Vector{Float64} + contact_viscosity_normal::Vector{Float64} + contact_viscosity_tangential::Vector{Float64} + contact_static_friction::Vector{Float64} + contact_dynamic_friction::Vector{Float64} + + youngs_modulus::Vector{Float64} + poissons_ratio::Vector{Float64} + tensile_strength::Vector{Float64} + #tensile_heal_rate::Vector{Float64} + compressive_strength_prefactor::Vector{Float64} + + ocean_drag_coeff_vert::Vector{Float64} + ocean_drag_coeff_horiz::Vector{Float64} + atmosphere_drag_coeff_vert::Vector{Float64} + atmosphere_drag_coeff_horiz::Vector{Float64} + + pressure::Vector{Float64} + n_contacts::Vector{Int} + + granular_stress::Array{Float64, 2} + ocean_stress::Array{Float64, 2} + atmosphere_stress::Array{Float64, 2} end #= @@ -171,7 +168,7 @@ h-points. During read, the velocities are interpolated to the cell corners type Ocean input_file::Any - time::Array{Float64, 1} + time::Vector{Float64} # q-point (cell corner) positions xq::Array{Float64, 2} @@ -182,8 +179,8 @@ type Ocean yh::Array{Float64, 2} # Vertical positions - zl::Array{Float64, 1} - zi::Array{Float64, 1} + zl::Vector{Float64} + zi::Vector{Float64} # Field values u::Array{Float64, 4} @@ -191,7 +188,7 @@ type Ocean h::Array{Float64, 4} e::Array{Float64, 4} - ice_floe_list::Array{Array{Int, 1}, 2} + ice_floe_list::Array{Vector{Int}, 2} end #= @@ -213,12 +210,12 @@ cell corners (q-points). # Fields * `input_file::String`: path to input NetCDF file -* `time::Array{Float64, 1}`: time in days -* `xq::Array{Float64, 1}`: nominal longitude of q-points [degrees_E] -* `yq::Array{Float64, 1}`: nominal latitude of q-points [degrees_N] -* `xh::Array{Float64, 1}`: nominal longitude of h-points [degrees_E] -* `yh::Array{Float64, 1}`: nominal latitude of h-points [degrees_N] -* `zl::Array{Float64, 1}`: vertical position [m] +* `time::Vector{Float64}`: time in days +* `xq::Array{Float64, 2}`: nominal longitude of q-points [degrees_E] +* `yq::Array{Float64, 2}`: nominal latitude of q-points [degrees_N] +* `xh::Array{Float64, 2}`: nominal longitude of h-points [degrees_E] +* `yh::Array{Float64, 2}`: nominal latitude of h-points [degrees_N] +* `zl::Vector{Float64}`: vertical position [m] * `u::Array{Float64, Int}`: zonal velocity (positive towards west) [m/s], dimensions correspond to placement in `[xq, yq, zl, time]`. * `v::Array{Float64, Int}`: meridional velocity (positive towards north) [m/s], @@ -229,7 +226,7 @@ cell corners (q-points). type Atmosphere input_file::Any - time::Array{Float64, 1} + time::Vector{Float64} # q-point (cell corner) positions xq::Array{Float64, 2} @@ -240,13 +237,13 @@ type Atmosphere yh::Array{Float64, 2} # Vertical positions - zl::Array{Float64, 1} + zl::Vector{Float64} # Field values u::Array{Float64, 4} v::Array{Float64, 4} - ice_floe_list::Array{Array{Int, 1}, 2} + ice_floe_list::Array{Vector{Int}, 2} # If true the grid positions are identical to the ocean grid collocated_with_ocean_grid::Bool @@ -264,7 +261,7 @@ type Simulation file_number::Int file_time_since_output_file::Float64 - ice_floes::Array{IceFloeCylindrical, 1} + ice_floes::Vector{IceFloeCylindrical} ocean::Ocean atmosphere::Atmosphere diff --git a/src/grid.jl b/src/grid.jl @@ -7,41 +7,65 @@ south-west (-x, -y)-facing corner. # Arguments * `field::Array{Float64, 4}`: a scalar field to interpolate from -* `x_tilde::float`: x point position [0;1] -* `y_tilde::float`: y point position [0;1] +* `x_tilde::Float64`: x point position [0;1] +* `y_tilde::Float64`: y point position [0;1] * `i::Int`: i-index of cell containing point * `j::Int`: j-index of scalar field to interpolate from * `it::Int`: time step from scalar field to interpolate from """ -function bilinearInterpolation(field::Array{Float64, 4}, - x_tilde::Float64, - y_tilde::Float64, - i::Int, - j::Int, - k::Int, - it::Int) +@inline function bilinearInterpolation!(interp_val::Vector{Float64}, + field_x::Array{Float64, 2}, + field_y::Array{Float64, 2}, + x_tilde::Float64, + y_tilde::Float64, + i::Int, + j::Int) #if x_tilde < 0. || x_tilde > 1. || y_tilde < 0. || y_tilde > 1. #error("relative coordinates outside bounds ($(x_tilde), $(y_tilde))") #end - return (field[i+1, j+1, k, it]*x_tilde + - field[i, j+1, k, it]*(1. - x_tilde))*y_tilde + - (field[i+1, j, k, it]*x_tilde + - field[i, j, k, it]*(1. - x_tilde))*(1. - y_tilde) + x_tilde_inv = 1. - x_tilde + + @views interp_val[1] = + (field_x[i+1, j+1]*x_tilde + field_x[i, j+1]*x_tilde_inv)*y_tilde + + (field_x[i+1, j]*x_tilde + field_x[i, j]*x_tilde_inv)*(1. - y_tilde) + + @views interp_val[2] = + (field_y[i+1, j+1]*x_tilde + field_y[i, j+1]*x_tilde_inv)*y_tilde + + (field_y[i+1, j]*x_tilde + field_y[i, j]*x_tilde_inv)*(1. - y_tilde) + + nothing end -function bilinearInterpolation(field::Array{Float64, 2}, - x_tilde::Float64, - y_tilde::Float64, - i::Int, - j::Int) - - if x_tilde < 0. || x_tilde > 1. || y_tilde < 0. || y_tilde > 1. - error("relative coordinates outside bounds ($(x_tilde), $(y_tilde))") - end +@inline function bilinearInterpolation!(interp_val::Vector{Float64}, + field_x::Array{Float64, 4}, + field_y::Array{Float64, 4}, + x_tilde::Float64, + y_tilde::Float64, + i::Int, + j::Int, + k::Int, + it::Int) + + #if x_tilde < 0. || x_tilde > 1. || y_tilde < 0. || y_tilde > 1. + #error("relative coordinates outside bounds ($(x_tilde), $(y_tilde))") + #end - return (field[i+1, j+1]*x_tilde + field[i, j+1]*(1. - x_tilde))*y_tilde + - (field[i+1, j]*x_tilde + field[i, j]*(1. - x_tilde))*(1. - y_tilde) + x_tilde_inv = 1. - x_tilde + + @views interp_val[1] = + (field_x[i+1, j+1, k, it]*x_tilde + + field_x[i, j+1, k, it]*x_tilde_inv)*y_tilde + + (field_x[i+1, j, k, it]*x_tilde + + field_x[i, j, k, it]*x_tilde_inv)*(1. - y_tilde) + + @views interp_val[2] = + (field_y[i+1, j+1, k, it]*x_tilde + + field_y[i, j+1, k, it]*x_tilde_inv)*y_tilde + + (field_y[i+1, j, k, it]*x_tilde + + field_y[i, j, k, it]*x_tilde_inv)*(1. - y_tilde) + + nothing end """ @@ -53,8 +77,8 @@ grid to an arbitrary position in a cell. Assumes south-west convention, i.e. # Arguments * `grid::Any`: grid for which to determine curl -* `x_tilde::float`: x point position [0;1] -* `y_tilde::float`: y point position [0;1] +* `x_tilde::Float64`: x point position [0;1] +* `y_tilde::Float64`: y point position [0;1] * `i::Int`: i-index of cell containing point * `j::Int`: j-index of scalar field to interpolate from * `it::Int`: time step from scalar field to interpolate from @@ -65,15 +89,27 @@ function curl(grid::Any, i::Int, j::Int, k::Int, - it::Int) - - sw, se, ne, nw = getCellCornerCoordinates(grid.xq, grid.yq, i, j) + it::Int, + sw::Vector{Float64} = Vector{Float64}(2), + se::Vector{Float64} = Vector{Float64}(2), + ne::Vector{Float64} = Vector{Float64}(2), + nw::Vector{Float64} = Vector{Float64}(2)) + + #sw, se, ne, nw = getCellCornerCoordinates(grid.xq, grid.yq, i, j) + sw[1] = grid.xq[ i, j] + sw[2] = grid.yq[ i, j] + se[1] = grid.xq[i+1, j] + se[2] = grid.yq[i+1, j] + ne[1] = grid.xq[i+1, j+1] + ne[2] = grid.yq[i+1, j+1] + nw[1] = grid.xq[ i, j+1] + nw[2] = grid.yq[ i, j+1] sw_se = norm(sw - se) se_ne = norm(se - ne) nw_ne = norm(nw - ne) sw_nw = norm(sw - nw) - return ( + @views @inbounds return ( ((grid.v[i+1, j , k,it] - grid.v[i , j , k,it])/sw_se*(1. - y_tilde) + ((grid.v[i+1, j+1, k,it] - grid.v[i , j+1, k,it])/nw_ne)*y_tilde) - ((grid.u[i , j+1, k,it] - grid.u[i , j , k,it])/sw_nw*(1. - x_tilde) + @@ -92,36 +128,42 @@ function sortIceFloesInGrid!(simulation::Simulation, grid::Any; verbose=false) for i=1:size(grid.xh, 1) for j=1:size(grid.xh, 2) - grid.ice_floe_list[i, j] = Int[] + @inbounds grid.ice_floe_list[i, j] = Int[] end end else for i=1:size(grid.xh, 1) for j=1:size(grid.xh, 2) - empty!(grid.ice_floe_list[i, j]) + @inbounds empty!(grid.ice_floe_list[i, j]) end end end - for idx in 1:length(simulation.ice_floes) + sw = Vector{Float64}(2) + se = Vector{Float64}(2) + ne = Vector{Float64}(2) + nw = Vector{Float64}(2) + + for idx=1:length(simulation.ice_floes) - if !simulation.ice_floes[idx].enabled + @inbounds if !simulation.ice_floes[idx].enabled continue end # After first iteration, check if ice floe is in same cell before # traversing entire grid if typeof(grid) == Ocean - i_old, j_old = simulation.ice_floes[idx].ocean_grid_pos + @inbounds i_old, j_old = simulation.ice_floes[idx].ocean_grid_pos elseif typeof(grid) == Atmosphere - i_old, j_old = simulation.ice_floes[idx].atmosphere_grid_pos + @inbounds i_old, j_old = + simulation.ice_floes[idx].atmosphere_grid_pos else error("grid type not understood.") end if simulation.time > 0. && i_old > 0 && j_old > 0 && isPointInCell(grid, i_old, j_old, - simulation.ice_floes[idx].lin_pos) + simulation.ice_floes[idx].lin_pos, sw, se, ne, nw) i = i_old j = j_old @@ -139,8 +181,9 @@ function sortIceFloesInGrid!(simulation::Simulation, grid::Any; verbose=false) i_t = max(min(i_old + i_rel, nx), 1) j_t = max(min(j_old + j_rel, ny), 1) - if isPointInCell(grid, i_t, j_t, - simulation.ice_floes[idx].lin_pos) + @inbounds if isPointInCell(grid, i_t, j_t, + simulation.ice_floes[idx].lin_pos, + sw, se, ne, nw) i = i_t j = j_t found = true @@ -166,17 +209,18 @@ function sortIceFloesInGrid!(simulation::Simulation, grid::Any; verbose=false) # add cell to ice floe if typeof(grid) == Ocean - simulation.ice_floes[idx].ocean_grid_pos = [i, j] + @inbounds simulation.ice_floes[idx].ocean_grid_pos = [i, j] elseif typeof(grid) == Atmosphere - simulation.ice_floes[idx].atmosphere_grid_pos = [i, j] + @inbounds simulation.ice_floes[idx].atmosphere_grid_pos = [i, j] else error("grid type not understood.") end end # add ice floe to cell - push!(grid.ice_floe_list[i, j], idx) + @inbounds push!(grid.ice_floe_list[i, j], idx) end + nothing end export findCellContainingPoint @@ -191,16 +235,23 @@ found the function returns `(0,0)`. # Arguments * `grid::Any`: grid object containing ocean or atmosphere data. -* `point::Array{float, 1}`: two-dimensional vector of point to check. +* `point::Vector{Float64}`: two-dimensional vector of point to check. * `method::String`: approach to use for determining if point is inside cell or not, can be "Conformal" (default) or "Areal". """ -function findCellContainingPoint(grid::Any, point::Array{float, 1}; +function findCellContainingPoint(grid::Any, point::Vector{Float64}; method::String="Conformal") + sw = Vector{Float64}(2) + se = Vector{Float64}(2) + ne = Vector{Float64}(2) + nw = Vector{Float64}(2) + for i=1:size(grid.xh, 1) for j=1:size(grid.yh, 2) - if isPointInCell(grid, i, j, point, method=method) + if isPointInCell(grid, i, j, point, + sw, se, ne, nw, + method=method) return i, j end end @@ -217,7 +268,7 @@ This function is a wrapper for `getCellCornerCoordinates()` and `conformalQuadrilateralCoordinates()`. """ function getNonDimensionalCellCoordinates(grid::Any, i::Int, j::Int, - point::Array{float, 1}) + point::Vector{Float64}) sw, se, ne, nw = getCellCornerCoordinates(grid.xq, grid.yq, i, j) return conformalQuadrilateralCoordinates(sw, se, ne, nw, point) @@ -230,10 +281,22 @@ The function uses either an area-based approach (`method = "Area"`), or a conformal mapping approach (`method = "Conformal"`). The area-based approach is more robust. This function returns `true` or `false`. """ -function isPointInCell(grid::Any, i::Int, j::Int, point::Array{float, 1}; +function isPointInCell(grid::Any, i::Int, j::Int, point::Vector{Float64}, + sw::Vector{Float64} = Vector{Float64}(2), + se::Vector{Float64} = Vector{Float64}(2), + ne::Vector{Float64} = Vector{Float64}(2), + nw::Vector{Float64} = Vector{Float64}(2); method::String="Conformal") - sw, se, ne, nw = getCellCornerCoordinates(grid.xq, grid.yq, i, j) + #sw, se, ne, nw = getCellCornerCoordinates(grid.xq, grid.yq, i, j) + @views sw[1] = grid.xq[ i, j] + @views sw[2] = grid.yq[ i, j] + @views se[1] = grid.xq[i+1, j] + @views se[2] = grid.yq[i+1, j] + @views ne[1] = grid.xq[i+1, j+1] + @views ne[2] = grid.yq[i+1, j+1] + @views nw[1] = grid.xq[ i, j+1] + @views nw[2] = grid.yq[ i, j+1] if method == "Area" if areaOfQuadrilateral(sw, se, ne, nw) ≈ @@ -275,7 +338,7 @@ south-east corner, north-east corner, north-west corner). @inline function getCellCornerCoordinates(xq::Array{Float64, 2}, yq::Array{Float64, 2}, i::Int, j::Int) - return Float64[xq[ i, j], yq[ i, j]], + @inbounds return Float64[xq[ i, j], yq[ i, j]], Float64[xq[i+1, j], yq[i+1, j]], Float64[xq[i+1, j+1], yq[i+1, j+1]], Float64[xq[ i, j+1], yq[ i, j+1]] @@ -300,9 +363,9 @@ end export areaOfTriangle "Returns the area of an triangle with corner coordinates `a`, `b`, and `c`." -function areaOfTriangle(a::Array{float, 1}, - b::Array{float, 1}, - c::Array{float, 1}) +function areaOfTriangle(a::Vector{Float64}, + b::Vector{Float64}, + c::Vector{Float64}) return abs( (a[1]*(b[2] - c[2]) + b[1]*(c[2] - a[2]) + @@ -317,10 +380,10 @@ Returns the area of a quadrilateral with corner coordinates `a`, `b`, `c`, and true for `b` and `d`. This is true if the four corners are passed as arguments in a "clockwise" or "counter-clockwise" manner. """ -function areaOfQuadrilateral(a::Array{float, 1}, - b::Array{float, 1}, - c::Array{float, 1}, - d::Array{float, 1}) +function areaOfQuadrilateral(a::Vector{Float64}, + b::Vector{Float64}, + c::Vector{Float64}, + d::Vector{Float64}) return areaOfTriangle(a, b, c) + areaOfTriangle(c, d, a) end @@ -331,11 +394,11 @@ within a quadrilateral with corner coordinates `A`, `B`, `C`, and `D`. Points must be ordered in counter-clockwise order, starting from south-west corner. """ -function conformalQuadrilateralCoordinates(A::Array{float, 1}, - B::Array{float, 1}, - C::Array{float, 1}, - D::Array{float, 1}, - p::Array{float, 1}) +function conformalQuadrilateralCoordinates(A::Vector{Float64}, + B::Vector{Float64}, + C::Vector{Float64}, + D::Vector{Float64}, + p::Vector{Float64}) if !(A[1] < B[1] && B[2] < C[2] && C[1] > D[1]) error("corner coordinates are not passed in the correct order") @@ -404,13 +467,13 @@ function findEmptyPositionInGridCell(simulation::Simulation, grid::Any, i::Int, j::Int, - r::float; + r::Float64; n_iter::Int = 10, seed::Int = 1, verbose::Bool = false) overlap_found = false i_iter = 0 - pos = [NaN NaN] + pos = [NaN, NaN] nx, ny = size(grid.xh) @@ -421,8 +484,7 @@ function findEmptyPositionInGridCell(simulation::Simulation, # generate random candidate position x_tilde = Base.Random.rand() y_tilde = Base.Random.rand() - pos = [bilinearInterpolation(grid.xq, x_tilde, y_tilde, i, j) - bilinearInterpolation(grid.yq, x_tilde, y_tilde, i, j)] + bilinearInterpolation!(pos, grid.xq, grid.yq, x_tilde, y_tilde, i, j) if verbose info("trying poisition $pos in cell $i,$j") end @@ -496,4 +558,5 @@ function copyGridSortingInfo!(ocean::Ocean, atmosphere::Atmosphere, icefloe.atmosphere_grid_pos = deepcopy(icefloe.ocean_grid_pos) end atmosphere.ice_floe_list = deepcopy(ocean.ice_floe_list) + nothing end diff --git a/src/icefloe.jl b/src/icefloe.jl @@ -1,4 +1,5 @@ ## Manage icefloes in the model +import PyPlot export addIceFloeCylindrical! """ @@ -7,39 +8,39 @@ Adds a grain to the simulation. Example: SeaIce.addIceFloeCylindrical([1.0, 2.0, 3.0], 1.0) """ function addIceFloeCylindrical!(simulation::Simulation, - lin_pos::vector, - contact_radius::float, - thickness::float; + lin_pos::Vector{Float64}, + contact_radius::Float64, + thickness::Float64; areal_radius = false, - lin_vel::vector = [0., 0.], - lin_acc::vector = [0., 0.], - force::vector = [0., 0.], - ang_pos::float = 0., - ang_vel::float = 0., - ang_acc::float = 0., - torque::float = 0., - density::float = 934., - contact_stiffness_normal::float = 1e7, - contact_stiffness_tangential::float = 0., - contact_viscosity_normal::float = 0., - contact_viscosity_tangential::float = 0., - contact_static_friction::float = 0.4, - contact_dynamic_friction::float = 0.4, - youngs_modulus::float = 2e7, - #youngs_modulus::float = 2e9, # Hopkins 2004 - poissons_ratio::float = 0.185, # Hopkins 2004 - #tensile_strength::float = 500e3, # Hopkins2004 - tensile_strength::float = 0., - tensile_heal_rate::float = 0., - compressive_strength_prefactor::float = 1285e3, + 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., + density::Float64 = 934., + contact_stiffness_normal::Float64 = 1e7, + contact_stiffness_tangential::Float64 = 0., + contact_viscosity_normal::Float64 = 0., + contact_viscosity_tangential::Float64 = 0., + contact_static_friction::Float64 = 0.4, + contact_dynamic_friction::Float64 = 0.4, + youngs_modulus::Float64 = 2e7, + #youngs_modulus::Float64 = 2e9, # Hopkins 2004 + poissons_ratio::Float64 = 0.185, # Hopkins 2004 + #tensile_strength::Float64 = 500e3, # Hopkins2004 + tensile_strength::Float64 = 0., + tensile_heal_rate::Float64 = 0., + compressive_strength_prefactor::Float64 = 1285e3, # Hopkins 2004 - ocean_drag_coeff_vert::float = 0.85, # H&C 2011 - ocean_drag_coeff_horiz::float = 5e-4, # H&C 2011 - atmosphere_drag_coeff_vert::float = 0.4, + ocean_drag_coeff_vert::Float64 = 0.85, # H&C 2011 + ocean_drag_coeff_horiz::Float64 = 5e-4, # H&C 2011 + atmosphere_drag_coeff_vert::Float64 = 0.4, # H&C 2011 - atmosphere_drag_coeff_horiz::float = 2.5e-4, + atmosphere_drag_coeff_horiz::Float64 = 2.5e-4, # H&C2011 - pressure::float = 0., + pressure::Float64 = 0., fixed::Bool = false, rotating::Bool = true, enabled::Bool = true, @@ -47,9 +48,9 @@ function addIceFloeCylindrical!(simulation::Simulation, ocean_grid_pos::Array{Int, 1} = [0, 0], atmosphere_grid_pos::Array{Int, 1} = [0, 0], n_contacts::Int = 0, - granular_stress::vector = [0., 0.], - ocean_stress::vector = [0., 0.], - atmosphere_stress::vector = [0., 0.]) + granular_stress::Vector{Float64} = [0., 0.], + ocean_stress::Vector{Float64} = [0., 0.], + atmosphere_stress::Vector{Float64} = [0., 0.]) # Check input values if length(lin_pos) != 2 @@ -77,8 +78,8 @@ function addIceFloeCylindrical!(simulation::Simulation, contacts::Array{Int, 1} = zeros(Int, simulation.Nc_max) contact_parallel_displacement = - Array{Array{Float64, 1}, 1}(simulation.Nc_max) - contact_age::Array{Float64, 1} = zeros(Float64, simulation.Nc_max) + Vector{Vector{Float64}}(simulation.Nc_max) + contact_age::Vector{Float64} = zeros(Float64, simulation.Nc_max) for i=1:simulation.Nc_max contact_parallel_displacement[i] = zeros(2) end @@ -151,6 +152,7 @@ function addIceFloeCylindrical!(simulation::Simulation, # Add to simulation object addIceFloe!(simulation, icefloe, verbose) + nothing end export iceFloeCircumreference @@ -318,56 +320,62 @@ function convertIceFloeDataToArrays(simulation::Simulation) end function deleteIceFloeArrays!(ifarr::IceFloeArrays) - ifarr.density = 0 - - ifarr.thickness = 0 - ifarr.contact_radius = 0 - ifarr.areal_radius = 0 - ifarr.circumreference = 0 - ifarr.horizontal_surface_area = 0 - ifarr.side_surface_area = 0 - ifarr.volume = 0 - ifarr.mass = 0 - ifarr.moment_of_inertia = 0 - - ifarr.lin_pos = 0 - ifarr.lin_vel = 0 - ifarr.lin_acc = 0 - ifarr.force = 0 - - ifarr.ang_pos = 0 - ifarr.ang_vel = 0 - ifarr.ang_acc = 0 - ifarr.torque = 0 - - ifarr.fixed = 0 - ifarr.rotating = 0 - ifarr.enabled = 0 - - ifarr.contact_stiffness_normal = 0 - ifarr.contact_stiffness_tangential = 0 - ifarr.contact_viscosity_normal = 0 - ifarr.contact_viscosity_tangential = 0 - ifarr.contact_static_friction = 0 - ifarr.contact_dynamic_friction = 0 - - ifarr.youngs_modulus = 0 - ifarr.poissons_ratio = 0 - ifarr.tensile_strength = 0 - ifarr.compressive_strength_prefactor = 0 - - ifarr.ocean_drag_coeff_vert = 0 - ifarr.ocean_drag_coeff_horiz = 0 - ifarr.atmosphere_drag_coeff_vert = 0 - ifarr.atmosphere_drag_coeff_horiz = 0 - - ifarr.pressure = 0 - ifarr.n_contacts = 0 - - ifarr.granular_stress = 0 - ifarr.ocean_stress = 0 - ifarr.atmosphere_stress = 0 + f1 = zeros(1) + f2 = zeros(1,1) + i1 = zeros(Int, 1) + + ifarr.density = f1 + + ifarr.thickness = f1 + ifarr.contact_radius = f1 + ifarr.areal_radius = f1 + ifarr.circumreference = f1 + ifarr.horizontal_surface_area = f1 + ifarr.side_surface_area = f1 + ifarr.volume = f1 + ifarr.mass = f1 + ifarr.moment_of_inertia = f1 + + ifarr.lin_pos = f2 + ifarr.lin_vel = f2 + ifarr.lin_acc = f2 + ifarr.force = f2 + + ifarr.ang_pos = f2 + ifarr.ang_vel = f2 + ifarr.ang_acc = f2 + ifarr.torque = f2 + + ifarr.fixed = i1 + ifarr.rotating = i1 + ifarr.enabled = i1 + + ifarr.contact_stiffness_normal = f1 + ifarr.contact_stiffness_tangential = f1 + ifarr.contact_viscosity_normal = f1 + ifarr.contact_viscosity_tangential = f1 + ifarr.contact_static_friction = f1 + ifarr.contact_dynamic_friction = f1 + + ifarr.youngs_modulus = f1 + ifarr.poissons_ratio = f1 + ifarr.tensile_strength = f1 + ifarr.compressive_strength_prefactor = f1 + + ifarr.ocean_drag_coeff_vert = f1 + ifarr.ocean_drag_coeff_horiz = f1 + ifarr.atmosphere_drag_coeff_vert = f1 + ifarr.atmosphere_drag_coeff_horiz = f1 + + ifarr.pressure = f1 + ifarr.n_contacts = i1 + + ifarr.granular_stress = f2 + ifarr.ocean_stress = f2 + ifarr.atmosphere_stress = f2 + gc() + nothing end export printIceFloeInfo @@ -427,6 +435,7 @@ function printIceFloeInfo(f::IceFloeCylindrical) println(" granular_stress: $(f.granular_stress) Pa") println(" ocean_stress: $(f.ocean_stress) Pa") println(" atmosphere_stress: $(f.atmosphere_stress) Pa") + nothing end export iceFloeKineticTranslationalEnergy @@ -535,4 +544,64 @@ function compareIceFloes(if1::IceFloeCylindrical, if2::IceFloeCylindrical) Base.Test.@test if1.granular_stress ≈ if2.granular_stress Base.Test.@test if1.ocean_stress ≈ if2.ocean_stress Base.Test.@test if1.atmosphere_stress ≈ if2.atmosphere_stress + nothing +end + +export plotIceFloeSizeDistribution +""" + plotIceFloeSizeDistribution(simulation, [filename_postfix], [nbins], + [size_type], [figsize], [filetype]) + +Plot the ice-floe size distribution as a histogram and save it to the disk. The +plot is saved accoring to the simulation id, the optional `filename_postfix` +string, and the `filetype`, and is written to the current folder. + +# Arguments +* `simulation::Simulation`: the simulation object containing the ice floes. +* `filename_postfix::String`: optional string for the output filename. +* `nbins::Int`: number of bins in the histogram (default = 12). +* `size_type::String`: specify whether to use the `contact` or `areal` radius + for the ice-floe size. The default is `contact`. +* `figsize::Tuple`: the output figure size in inches (default = (6,4). +* `filetype::String`: the output file type (default = "png"). +* `verbose::String`: show output file as info message in stdout (default = + true). +* `skip_fixed::Bool`: ommit ice floes that are fixed in space from the size + distribution (default = true). +* `logy::Bool`: plot y-axis in log scale. +""" +function plotIceFloeSizeDistribution(simulation::Simulation; + filename_postfix::String = "", + nbins::Int=12, + size_type::String = "contact", + figsize::Tuple = (6,4), + filetype::String = "png", + verbose::Bool = true, + skip_fixed::Bool = true, + log_y::Bool = true) + + diameters = Float64[] + for i=1:length(simulation.ice_floes) + if simulation.ice_floes[i].fixed && skip_fixed + continue + end + if size_type == "contact" + push!(diameters, simulation.ice_floes[i].contact_radius*2.) + elseif size_type == "areal" + push!(diameters, simulation.ice_floes[i].areal_radius*2.) + else + error("size_type '$size_type' not understood") + end + end + PyPlot.pygui(false) + PyPlot.figure(figsize=figsize) + PyPlot.plt[:hist](diameters, nbins, log=log_y) + PyPlot.xlabel("Diameter [m]") + PyPlot.ylabel("Count [-]") + filename = string(simulation.id * filename_postfix * + "-ice-floe-size-distribution." * filetype) + PyPlot.savefig(filename) + if verbose + info(filename) + end end diff --git a/src/interaction.jl b/src/interaction.jl @@ -16,26 +16,26 @@ function interact!(simulation::Simulation) continue end - """ - if norm(simulation.ice_floes[i].lin_pos - + #=if norm(simulation.ice_floes[i].lin_pos - simulation.ice_floes[j].lin_pos) - - (simulation.ice_floes[i].contact_radius + - simulation.ice_floes[j].contact_radius) > 0. + (simulation.ice_floes[i].contact_radius + + simulation.ice_floes[j].contact_radius) > 0. simulation.ice_floes[i].contacts[ic] = 0 # remove contact simulation.ice_floes[i].n_contacts -= 1 simulation.ice_floes[j].n_contacts -= 1 - else - """ + else=# interactIceFloes!(simulation, i, j, ic) #end end end for i=1:length(simulation.ice_floes) - simulation.ice_floes[i].granular_stress = simulation.ice_floes[i].force/ + @inbounds simulation.ice_floes[i].granular_stress = + simulation.ice_floes[i].force/ simulation.ice_floes[i].horizontal_surface_area end + nothing end export interactIceFloes! @@ -197,12 +197,5 @@ function interactIceFloes!(simulation::Simulation, i::Int, j::Int, ic::Int) force_n/simulation.ice_floes[i].side_surface_area; simulation.ice_floes[j].pressure += force_n/simulation.ice_floes[j].side_surface_area; -end - -function harmonicMean(a::Any, b::Any) - if a ≈ 0. && b ≈ 0 - return 0. - else - return 2.*a*b/(a + b) - end + nothing end diff --git a/src/io.jl b/src/io.jl @@ -41,6 +41,7 @@ function writeSimulation(simulation::Simulation; info("simulation written to $filename") end end + nothing end export readSimulation @@ -56,6 +57,7 @@ function readSimulation(filename::String=""; warn("Package JLD not found. Simulation save/read not supported. " * "Please install JLD and its " * "requirements with `Pkg.add(\"JLD\")`.") + nothing else if verbose info("reading simulation from $filename") @@ -85,6 +87,7 @@ function writeSimulationStatus(simulation::Simulation; if verbose info("wrote status to $filename") end + nothing end export readSimulationStatus @@ -197,6 +200,7 @@ function status(folder::String="."; repeat = false end end + nothing end export writeVTK @@ -240,6 +244,7 @@ function writeVTK(simulation::Simulation; simulation.file_number) writeGridVTK(simulation.atmosphere, filename, verbose=verbose) end + nothing end export writeIceFloeVTK @@ -343,9 +348,8 @@ function writeIceFloeVTK(simulation::Simulation, outfiles = WriteVTK.vtk_save(vtkfile) if verbose info("Output file: " * outfiles[1]) - else - return nothing end + nothing end export writeIceFloeInteractionVTK @@ -363,14 +367,14 @@ function writeIceFloeInteractionVTK(simulation::Simulation, i1 = Int64[] i2 = Int64[] - inter_particle_vector = Array{float, 1}[] - force = float[] - effective_radius = float[] - contact_area = float[] - contact_stiffness = float[] - tensile_stress = float[] - shear_displacement = Array{float, 1}[] - contact_age = float[] + inter_particle_vector = Vector{Float64}[] + force = Float64[] + effective_radius = Float64[] + contact_area = Float64[] + contact_stiffness = Float64[] + tensile_stress = Float64[] + shear_displacement = Vector{Float64}[] + contact_age = Float64[] for i=1:length(simulation.ice_floes) for ic=1:simulation.Nc_max if simulation.ice_floes[i].contacts[ic] > 0 @@ -457,8 +461,8 @@ function writeIceFloeInteractionVTK(simulation::Simulation, "Name=\"Shear displacement [m]\" " * "NumberOfComponents=\"3\" format=\"ascii\">\n") for i=1:length(i1) - write(f, "$(shear_displacement[i][1]) ") - write(f, "$(shear_displacement[i][2]) ") + @inbounds write(f, "$(shear_displacement[i][1]) ") + @inbounds write(f, "$(shear_displacement[i][2]) ") write(f, "0.0 ") end write(f, "\n") @@ -467,7 +471,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, write(f, " <DataArray type=\"Float32\" Name=\"Force [N]\" " * "NumberOfComponents=\"1\" format=\"ascii\">\n") for i=1:length(i1) - write(f, "$(force[i]) ") + @inbounds write(f, "$(force[i]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -476,7 +480,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, "Name=\"Effective radius [m]\" " * "NumberOfComponents=\"1\" format=\"ascii\">\n") for i=1:length(i1) - write(f, "$(effective_radius[i]) ") + @inbounds write(f, "$(effective_radius[i]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -485,7 +489,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, "Name=\"Contact area [m^2]\" " * "NumberOfComponents=\"1\" format=\"ascii\">\n") for i=1:length(i1) - write(f, "$(contact_area[i]) ") + @inbounds write(f, "$(contact_area[i]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -494,7 +498,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, "Name=\"Contact stiffness [N/m]\" " * "NumberOfComponents=\"1\" format=\"ascii\">\n") for i=1:length(i1) - write(f, "$(contact_stiffness[i]) ") + @inbounds write(f, "$(contact_stiffness[i]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -503,7 +507,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, "Name=\"Tensile stress [Pa]\" " * "NumberOfComponents=\"1\" format=\"ascii\">\n") for i=1:length(i1) - write(f, "$(tensile_stress[i]) ") + @inbounds write(f, "$(tensile_stress[i]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -512,7 +516,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, "Name=\"Contact age [s]\" NumberOfComponents=\"1\" format=\"ascii\">\n") for i=1:length(i1) - write(f, "$(contact_age[i]) ") + @inbounds write(f, "$(contact_age[i]) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -525,7 +529,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, write(f, " <DataArray type=\"Float32\" Name=\"Points\" " * "NumberOfComponents=\"3\" format=\"ascii\">\n") for i in simulation.ice_floes - write(f, "$(i.lin_pos[1]) $(i.lin_pos[2]) 0.0 ") + @inbounds write(f, "$(i.lin_pos[1]) $(i.lin_pos[2]) 0.0 ") end write(f, "\n") write(f, " </DataArray>\n") @@ -544,7 +548,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, write(f, " <DataArray type=\"Int64\" Name=\"connectivity\" " * "format=\"ascii\">\n") for i=1:length(i1) - write(f, "$(i1[i] - 1) $(i2[i] - 1) ") + @inbounds write(f, "$(i1[i] - 1) $(i2[i] - 1) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -554,7 +558,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, write(f, " <DataArray type=\"Int64\" Name=\"offsets\" " * "format=\"ascii\">\n") for i=1:length(i1) - write(f, "$((i - 1)*2 + 2) ") + @inbounds write(f, "$((i - 1)*2 + 2) ") end write(f, "\n") write(f, " </DataArray>\n") @@ -580,6 +584,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation, write(f, " </PolyData>\n") write(f, "</VTKFile>\n") end + nothing end export writeOceanVTK @@ -599,12 +604,12 @@ function writeGridVTK(grid::Any, zq = similar(grid.u[:,:,:,1]) for iz=1:size(xq, 3) - xq[:,:,iz] = grid.xq - yq[:,:,iz] = grid.yq + @inbounds xq[:,:,iz] = grid.xq + @inbounds yq[:,:,iz] = grid.yq end for ix=1:size(xq, 1) for iy=1:size(xq, 2) - zq[ix,iy,:] = grid.zl + @inbounds zq[ix,iy,:] = grid.zl end end @@ -620,8 +625,8 @@ function writeGridVTK(grid::Any, for ix=1:size(xq, 1) for iy=1:size(xq, 2) for iz=1:size(xq, 3) - vel[1, ix, iy, iz] = grid.u[ix, iy, iz, 1] - vel[2, ix, iy, iz] = grid.v[ix, iy, iz, 1] + @inbounds vel[1, ix, iy, iz] = grid.u[ix, iy, iz, 1] + @inbounds vel[2, ix, iy, iz] = grid.v[ix, iy, iz, 1] end end end @@ -638,9 +643,8 @@ function writeGridVTK(grid::Any, outfiles = WriteVTK.vtk_save(vtkfile) if verbose info("Output file: " * outfiles[1]) - else - return nothing end + nothing end export writeParaviewStateFile @@ -18176,6 +18180,7 @@ function writeParaviewStateFile(simulation::Simulation; if verbose info("Output file: " * filename) end + nothing end export removeSimulationFiles @@ -18192,4 +18197,5 @@ function removeSimulationFiles(simulation::Simulation; folder::String=".") run(`bash -c "rm -rf $(folder)/$(simulation.id).status.txt"`) run(`bash -c "rm -rf $(folder)/$(simulation.id).*.jld"`) run(`bash -c "rm -rf $(folder)"`) + nothing end diff --git a/src/ocean.jl b/src/ocean.jl @@ -71,17 +71,17 @@ or `########.ocean_month.nc`) from disk and return time stamps, velocity fields, layer thicknesses, interface heights, and vertical coordinates. # Returns -* `time::Array{Float, 1}`: Time [s] -* `u::Array{Float, 2}`: Cell corner zonal velocity [m/s], +* `time::Vector{Float64}`: Time [s] +* `u::Array{Float64, 2}`: Cell corner zonal velocity [m/s], dimensions correspond to placement in `[xq, yq, zl, time]` -* `v::Array{Float, 2}`: Cell corner meridional velocity [m/s], +* `v::Array{Float64, 2}`: Cell corner meridional velocity [m/s], dimensions correspond to placement in `[xq, yq, zl, time]` * `h::Array{Float64, 2}`: layer thickness [m], dimensions correspond to placement in `[xh, yh, zl, time]` * `e::Array{Float64, 2}`: interface height relative to mean sea level [m], dimensions correspond to placement in `[xh, yh, zi, time]` -* `zl::Array{Float64, 1}`: layer target potential density [kg m^-3] -* `zi::Array{Float64, 1}`: interface target potential density [kg m^-3] +* `zl::Vector{Float64}`: layer target potential density [kg m^-3] +* `zi::Vector{Float64}`: interface target potential density [kg m^-3] """ function readOceanStateNetCDF(filename::String) @@ -93,13 +93,13 @@ function readOceanStateNetCDF(filename::String) v_staggered = convert(Array{Float64, 4}, NetCDF.ncread(filename, "v")) u, v = interpolateOceanVelocitiesToCorners(u_staggered, v_staggered) - time = convert(Array{Float64, 1}, + time = convert(Vector{Float64}, NetCDF.ncread(filename, "time")*24.*60.*60.) h = convert(Array{Float64, 4}, NetCDF.ncread(filename, "h")) e = convert(Array{Float64, 4}, NetCDF.ncread(filename, "e")) - zl = convert(Array{Float64, 1}, NetCDF.ncread(filename, "zl")) - zi = convert(Array{Float64, 1}, NetCDF.ncread(filename, "zi")) + zl = convert(Vector{Float64}, NetCDF.ncread(filename, "zl")) + zi = convert(Vector{Float64}, NetCDF.ncread(filename, "zi")) return time, u, v, h, e, zl, zi end @@ -172,7 +172,7 @@ function performs linear interpolation between time steps to get the approximate ocean state at any point in time. If the `Ocean` data set only contains a single time step, values from that time are returned. """ -function interpolateOceanState(ocean::Ocean, t::float) +function interpolateOceanState(ocean::Ocean, t::Float64) if length(ocean.time) == 1 return ocean.u, ocean.v, ocean.h, ocean.e elseif t < ocean.time[1] || t > ocean.time[end] @@ -213,9 +213,9 @@ the ocean spanning `z<0.`. Vertical indexing starts with `k=0` at the sea surface, and increases downwards. """ function createRegularOceanGrid(n::Array{Int, 1}, - L::Array{float, 1}; - origo::Array{float, 1} = zeros(2), - time::Array{float, 1} = zeros(1), + L::Vector{Float64}; + origo::Vector{Float64} = zeros(2), + time::Vector{Float64} = zeros(1), name::String = "unnamed") xq = repmat(linspace(origo[1], L[1], n[1] + 1), 1, n[2] + 1) @@ -253,6 +253,11 @@ function addOceanDrag!(simulation::Simulation) end u, v, h, e = interpolateOceanState(simulation.ocean, simulation.time) + uv_interp = Vector{Float64}(2) + sw = Vector{Float64}(2) + se = Vector{Float64}(2) + ne = Vector{Float64}(2) + nw = Vector{Float64}(2) for ice_floe in simulation.ice_floes @@ -274,15 +279,13 @@ function addOceanDrag!(simulation::Simulation) """) end - applyOceanDragToIceFloe!(ice_floe, - bilinearInterpolation(u, x_tilde, y_tilde, - i, j, k, 1), - bilinearInterpolation(v, x_tilde, y_tilde, - i, j, k, 1)) + bilinearInterpolation!(uv_interp, u, v, x_tilde, y_tilde, i, j, k, 1) + applyOceanDragToIceFloe!(ice_floe, uv_interp[1], uv_interp[2]) applyOceanVorticityToIceFloe!(ice_floe, curl(simulation.ocean, x_tilde, y_tilde, - i, j, k, 1)) + i, j, k, 1, sw, se, ne, nw)) end + nothing end export applyOceanDragToIceFloe! @@ -291,7 +294,7 @@ Add Stokes-type drag from velocity difference between ocean and a single ice floe. """ function applyOceanDragToIceFloe!(ice_floe::IceFloeCylindrical, - u::float, v::float) + u::Float64, v::Float64) freeboard = .1*ice_floe.thickness # height above water rho_o = 1000. # ocean density draft = ice_floe.thickness - freeboard # height of submerged thickness @@ -304,6 +307,7 @@ function applyOceanDragToIceFloe!(ice_floe::IceFloeCylindrical, ice_floe.force += drag_force ice_floe.ocean_stress = drag_force/ice_floe.horizontal_surface_area + nothing end export applyOceanVorticityToIceFloe! @@ -313,7 +317,7 @@ single ice floe. See Eq. 9.28 in "Introduction to Fluid Mechanics" by Nakayama and Boucher, 1999. """ function applyOceanVorticityToIceFloe!(ice_floe::IceFloeCylindrical, - ocean_curl::float) + ocean_curl::Float64) freeboard = .1*ice_floe.thickness # height above water rho_o = 1000. # ocean density draft = ice_floe.thickness - freeboard # height of submerged thickness @@ -323,6 +327,7 @@ function applyOceanVorticityToIceFloe!(ice_floe::IceFloeCylindrical, (ice_floe.areal_radius/5.*ice_floe.ocean_drag_coeff_horiz + draft*ice_floe.ocean_drag_coeff_vert)* abs(.5*ocean_curl - ice_floe.ang_vel)*(.5*ocean_curl - ice_floe.ang_vel) + nothing end export compareOceans @@ -353,4 +358,5 @@ function compareOceans(ocean1::Ocean, ocean2::Ocean) if isassigned(ocean1.ice_floe_list, 1) Base.Test.@test ocean1.ice_floe_list == ocean2.ice_floe_list end + nothing end diff --git a/src/simulation.jl b/src/simulation.jl @@ -93,7 +93,7 @@ function run!(simulation::Simulation; simulation.time_total += simulation.time_step end - checkTimeParameters(simulation) + checkTimeParameters(simulation, single_step=single_step) # if both are enabled, check if the atmosphere grid spatial geometry is # identical to the ocean grid @@ -175,7 +175,7 @@ function run!(simulation::Simulation; incrementCurrentTime!(simulation, simulation.time_step) if single_step - return + return nothing end end @@ -191,6 +191,8 @@ function run!(simulation::Simulation; reportSimulationTimeToStdout(simulation) println() end + gc() + nothing end export addIceFloe! @@ -210,16 +212,17 @@ function addIceFloe!(simulation::Simulation, if verbose info("Added IceFloe $(length(simulation.ice_floes))") end + nothing end export disableIceFloe! "Disable ice floe with index `i` in the `simulation` object." -function disableIceFloe!(simulation::Simulation, i::Integer) +function disableIceFloe!(simulation::Simulation, i::Int) if i < 1 error("Index must be greater than 0 (i = $i)") end - simulation.ice_floes[i].enabled = false + nothing end export zeroForcesAndTorques! @@ -230,6 +233,7 @@ function zeroForcesAndTorques!(simulation::Simulation) icefloe.torque = 0. icefloe.pressure = 0. end + nothing end export reportSimulationTimeToStdout @@ -237,6 +241,7 @@ export reportSimulationTimeToStdout function reportSimulationTimeToStdout(simulation::Simulation) print("\r t = ", simulation.time, '/', simulation.time_total, " s ") + nothing end export compareSimulations @@ -265,4 +270,46 @@ function compareSimulations(sim1::Simulation, sim2::Simulation) compareAtmospheres(sim1.atmosphere, sim2.atmosphere) Base.Test.@test sim1.Nc_max == sim2.Nc_max + nothing +end + +export printMemoryUsage +""" + printMemoryUsage(sim::Simulation) + +Shows the memory footprint of the simulation object. +""" +function printMemoryUsage(sim::Simulation) + + reportMemory(sim, "sim") + println(" where:") + + reportMemory(sim.ice_floes, " sim.ice_floes", + "(N=$(length(sim.ice_floes)))") + + reportMemory(sim.ocean, " sim.ocean", + "($(size(sim.ocean.xh, 1))x" * + "$(size(sim.ocean.xh, 2))x" * + "$(size(sim.ocean.xh, 3)))") + + reportMemory(sim.atmosphere, " sim.atmosphere", + "($(size(sim.atmosphere.xh, 1))x" * + "$(size(sim.atmosphere.xh, 2))x" * + "$(size(sim.atmosphere.xh, 3)))") + nothing +end + +function reportMemory(variable, head::String, tail::String="") + bytes = Base.summarysize(variable) + if bytes < 10_000 + size_str = @sprintf "%5d bytes" bytes + elseif bytes < 10_000 * 1024 + size_str = @sprintf "%5d kb" bytes ÷ 1024 + elseif bytes < 10_000 * 1024 * 1024 + size_str = @sprintf "%5d Mb" bytes ÷ 1024 ÷ 1024 + else + size_str = @sprintf "%5d Gb" bytes ÷ 1024 ÷ 1024 ÷ 1024 + end + @printf("%-20s %s %s\n", head, size_str, tail) + nothing end diff --git a/src/temporal.jl b/src/temporal.jl @@ -1,70 +1,77 @@ export setTotalTime! """ - setTotalTime!(simulation::Simulation, t::float) + setTotalTime!(simulation::Simulation, t::Float64) Sets the total simulation time of the `simulation` object to `t`, with parameter value checks. """ -function setTotalTime!(simulation::Simulation, t::float) +function setTotalTime!(simulation::Simulation, t::Float64) if t <= 0.0 error("Total time should be a positive value (t = $t s)") end simulation.time_total = t + nothing end export setCurrentTime! """ - setCurrentTime!(simulation::Simulation, t::float) + setCurrentTime!(simulation::Simulation, t::Float64) Sets the current simulation time of the `simulation` object to `t`, with parameter value checks. """ -function setCurrentTime!(simulation::Simulation, t::float) +function setCurrentTime!(simulation::Simulation, t::Float64) if t <= 0.0 error("Current time should be a positive value (t = $t s)") end simulation.time = t + nothing end export incrementCurrentTime! """ - incrementCurrentTime!(simulation::Simulation, t::float) + incrementCurrentTime!(simulation::Simulation, t::Float64) Sets the current simulation time of the `simulation` object to `t`, with parameter value checks. """ -function incrementCurrentTime!(simulation::Simulation, t::float) +function incrementCurrentTime!(simulation::Simulation, t::Float64) if t <= 0.0 error("Current time increment should be a positive value (t = $t s)") end simulation.time += t simulation.file_time_since_output_file += t + nothing end export setOutputFileInterval! """ - setOutputFileInterval!(simulation::Simulation, t::float) + setOutputFileInterval!(simulation::Simulation, t::Float64) Sets the simulation-time interval between output files are written to disk. If this value is zero or negative, no output files will be written. """ -function setOutputFileInterval!(simulation::Simulation, t::float; verbose=true) +function setOutputFileInterval!(simulation::Simulation, t::Float64; + verbose=true) if t <= 0.0 && verbose info("No output files will be written") end simulation.file_time_step = t + nothing end export disableOutputFiles! "Disables the write of output files to disk during a simulation." function disableOutputFiles!(simulation::Simulation) simulation.file_time_step = 0.0 + nothing end export checkTimeParameters "Checks if simulation temporal parameters are of reasonable values." -function checkTimeParameters(simulation::Simulation) - if simulation.time_total <= 0.0 || simulation.time_total <= simulation.time +function checkTimeParameters(simulation::Simulation; single_step::Bool=false) + if !single_step && (simulation.time_total <= 0.0 || simulation.time_total <= + simulation.time) error("Total time should be positive and larger than current time.\n", " t_total = ", simulation.time_total, " s, t = ", simulation.time, " s") @@ -72,6 +79,7 @@ function checkTimeParameters(simulation::Simulation) if simulation.time_step <= 0.0 error("Time step should be positive (t = ", simulation.time_step, ")") end + nothing end export findSmallestIceFloeMass @@ -80,8 +88,8 @@ the optimal time step length." function findSmallestIceFloeMass(simulation::Simulation) m_min = 1e20 i_min = -1 - for i in length(simulation.ice_floes) - icefloe = simulation.ice_floes[i] + for i=1:length(simulation.ice_floes) + @inbounds icefloe = simulation.ice_floes[i] if icefloe.mass < m_min m_min = icefloe.mass i_min = i @@ -98,9 +106,9 @@ function findLargestIceFloeStiffness(simulation::Simulation) k_t_max = 0. i_n_max = -1 i_t_max = -1 - for i in length(simulation.ice_floes) + for i=1:length(simulation.ice_floes) - icefloe = simulation.ice_floes[i] + @inbounds icefloe = simulation.ice_floes[i] if icefloe.youngs_modulus > 0. k_n = icefloe.youngs_modulus*icefloe.thickness # A = h*r @@ -129,7 +137,7 @@ Find the computational time step length suitable given the grain radii, contact stiffnesses, and grain density. Uses the scheme by Radjaii et al. 2011. """ function setTimeStep!(simulation::Simulation; - epsilon::float=0.07, verbose::Bool=true) + epsilon::Float64=0.07, verbose::Bool=true) if length(simulation.ice_floes) < 1 error("At least 1 grain is needed to find the computational time step.") @@ -147,4 +155,5 @@ function setTimeStep!(simulation::Simulation; if verbose info("Time step length t=", simulation.time_step, " s") end + nothing end diff --git a/src/temporal_integration.jl b/src/temporal_integration.jl @@ -36,6 +36,7 @@ function updateIceFloeKinematics!(simulation::Simulation; else error("Unknown integration method '$method'") end + nothing end export updateIceFloeKinematicsTwoTermTaylor! @@ -64,6 +65,7 @@ function updateIceFloeKinematicsTwoTermTaylor!(icefloe::IceFloeCylindrical, icefloe.lin_vel += icefloe.lin_acc * simulation.time_step icefloe.ang_vel += icefloe.ang_acc * simulation.time_step + nothing end export updateIceFloeKinematicsThreeTermTaylor! @@ -109,4 +111,5 @@ function updateIceFloeKinematicsThreeTermTaylor!(icefloe::IceFloeCylindrical, 0.5 * d_lin_acc_dt * simulation.time_step^2. icefloe.ang_vel += icefloe.ang_acc * simulation.time_step + 0.5 * d_ang_acc_dt * simulation.time_step^2. + nothing end diff --git a/src/util.jl b/src/util.jl @@ -0,0 +1,43 @@ +#!/usr/bin/env julia + +export randpower +""" + randpower([nvals], [distribution_power], [min_val], [max_val]) + +Returns one or more random numbers from a power-law probability distribution. + +# Arguments +* `dims::Any`: the dimensions of random values (default = 1) +* `distribution_power::Number`: the distribution power (default = 1.) +* `min_val::Number`: the lower bound of the distribution range (default = 0.) +* `max_val::Number`: the upper bound of the distribution range (default = 1.) +""" +@inline function randpower(dims::Any = 1, + distribution_power::Number = 1., + min_val::Number = 0., + max_val::Number = 1.) + + val = ((max_val^(distribution_power + 1.) - + min_val^(distribution_power + 1.))*Base.Random.rand(dims) + + min_val^(distribution_power + 1.)).^(1./(distribution_power + 1.)) + + if dims == 1 + return val[1] + else + return val + end +end + +export harmonicMean +""" + harmonicMean(a, b) + +Returns the harmonic mean of two numbers `a::Number` and `b::Number`. +""" +function harmonicMean(a::Number, b::Number)::Number + if a ≈ 0. && b ≈ 0 + return 0. + else + return 2.*a*b/(a + b) + end +end diff --git a/test/contact-search-and-geometry.jl b/test/contact-search-and-geometry.jl @@ -197,3 +197,47 @@ end @test 1 == sim.ice_floes[2].n_contacts @test_throws ErrorException SeaIce.findContacts!(sim, method="") + +info("Testing contact registration with multiple contacts") +sim = SeaIce.createSimulation(id="test") +SeaIce.addIceFloeCylindrical!(sim, [2., 2.], 1.01, 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [4., 2.], 1.01, 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [6., 2.], 1.01, 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [2., 4.], 1.01, 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [4., 4.], 1.01, 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [6., 4.], 1.01, 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [2., 6.], 1.01, 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [4., 6.], 1.01, 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [6., 6.], 1.01, 1., verbose=false) +sim.ocean = SeaIce.createRegularOceanGrid([4, 4, 2], [8., 8., 2.]) +SeaIce.sortIceFloesInGrid!(sim, sim.ocean) +SeaIce.findContacts!(sim) +@test 2 == sim.ice_floes[1].n_contacts +@test 3 == sim.ice_floes[2].n_contacts +@test 2 == sim.ice_floes[3].n_contacts +@test 3 == sim.ice_floes[4].n_contacts +@test 4 == sim.ice_floes[5].n_contacts +@test 3 == sim.ice_floes[6].n_contacts +@test 2 == sim.ice_floes[7].n_contacts +@test 3 == sim.ice_floes[8].n_contacts +@test 2 == sim.ice_floes[9].n_contacts +SeaIce.interact!(sim) +SeaIce.interact!(sim) +SeaIce.interact!(sim) +SeaIce.interact!(sim) +@test 2 == sim.ice_floes[1].n_contacts +@test 3 == sim.ice_floes[2].n_contacts +@test 2 == sim.ice_floes[3].n_contacts +@test 3 == sim.ice_floes[4].n_contacts +@test 4 == sim.ice_floes[5].n_contacts +@test 3 == sim.ice_floes[6].n_contacts +@test 2 == sim.ice_floes[7].n_contacts +@test 3 == sim.ice_floes[8].n_contacts +@test 2 == sim.ice_floes[9].n_contacts +for i=1:9 + sim.ice_floes[i].contact_radius = 0.99 +end +SeaIce.interact!(sim) +for i=1:9 + @test sim.ice_floes[i].n_contacts == 0 +end diff --git a/test/grid.jl b/test/grid.jl @@ -22,22 +22,23 @@ info("Testing area-determination methods") @test SeaIce.areaOfQuadrilateral([1., 0.], [0., 1.], [0., 0.], [1., 1.]) ≈ 1. info("Testing area-based cell content determination") +@test SeaIce.isPointInCell(ocean, 1, 1, [6.5, 53.5], sw, se, ne, nw) == true @test SeaIce.isPointInCell(ocean, 1, 1, [6.5, 53.5]) == true @test SeaIce.getNonDimensionalCellCoordinates(ocean, 1, 1, [6.5, 53.5]) ≈ [.5, .5] -@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.5]) == true +@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.5], sw, se, ne, nw) == true @test SeaIce.getNonDimensionalCellCoordinates(ocean, 1, 1, [6.1, 53.5]) ≈ [.1, .5] -@test SeaIce.isPointInCell(ocean, 1, 1, [6.0, 53.5]) == true +@test SeaIce.isPointInCell(ocean, 1, 1, [6.0, 53.5], sw, se, ne, nw) == true @test SeaIce.getNonDimensionalCellCoordinates(ocean, 1, 1, [6.0, 53.5]) ≈ [.0, .5] -@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.7]) == true -@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.9]) == true -@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.99999]) == true +@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.7], sw, se, ne, nw) == true +@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.9], sw, se, ne, nw) == true +@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.99999], sw, se, ne, nw) == true @test SeaIce.getNonDimensionalCellCoordinates(ocean, 1, 1, [6.1, 53.99999]) ≈ [.1, .99999] -@test SeaIce.isPointInCell(ocean, 1, 1, [7.5, 53.5]) == false -@test SeaIce.isPointInCell(ocean, 1, 1, [0.0, 53.5]) == false +@test SeaIce.isPointInCell(ocean, 1, 1, [7.5, 53.5], sw, se, ne, nw) == false +@test SeaIce.isPointInCell(ocean, 1, 1, [0.0, 53.5], sw, se, ne, nw) == false x_tilde, _ = SeaIce.getNonDimensionalCellCoordinates(ocean, 1, 1, [0., 53.5]) @test x_tilde < 0. @@ -64,16 +65,21 @@ info("Testing conformal mapping methods") [7.5,-1.5]) info("Checking cell content using conformal mapping methods") -@test SeaIce.isPointInCell(ocean, 1, 1, [6.4, 53.4], method="Conformal") == true -@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.5], method="Conformal") == true -@test SeaIce.isPointInCell(ocean, 1, 1, [6.0, 53.5], method="Conformal") == true -@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.7], method="Conformal") == true -@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.9], method="Conformal") == true -@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.99999], +@test SeaIce.isPointInCell(ocean, 1, 1, [6.4, 53.4], sw, se, ne, nw, method="Conformal") == true -@test SeaIce.isPointInCell(ocean, 1, 1, [7.5, 53.5], +@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.5], sw, se, ne, nw, + method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 1, 1, [6.0, 53.5], sw, se, ne, nw, + method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.7], sw, se, ne, nw, + method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.9], sw, se, ne, nw, + method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 1, 1, [6.1, 53.99999], sw, se, ne, nw, + method="Conformal") == true +@test SeaIce.isPointInCell(ocean, 1, 1, [7.5, 53.5], sw, se, ne, nw, method="Conformal") == false -@test SeaIce.isPointInCell(ocean, 1, 1, [0.0, 53.5], +@test SeaIce.isPointInCell(ocean, 1, 1, [0.0, 53.5], sw, se, ne, nw, method="Conformal") == false info("Testing bilinear interpolation scheme on conformal mapping") @@ -81,11 +87,29 @@ ocean.u[1, 1, 1, 1] = 1.0 ocean.u[2, 1, 1, 1] = 1.0 ocean.u[2, 2, 1, 1] = 0.0 ocean.u[1, 2, 1, 1] = 0.0 -@test SeaIce.bilinearInterpolation(ocean.u, .5, .5, 1, 1, 1, 1) ≈ .5 -@test SeaIce.bilinearInterpolation(ocean.u, 1., 1., 1, 1, 1, 1) ≈ .0 -@test SeaIce.bilinearInterpolation(ocean.u, 0., 0., 1, 1, 1, 1) ≈ 1. -@test SeaIce.bilinearInterpolation(ocean.u, .25, .25, 1, 1, 1, 1) ≈ .75 -@test SeaIce.bilinearInterpolation(ocean.u, .75, .75, 1, 1, 1, 1) ≈ .25 +val = [NaN, NaN] +SeaIce.bilinearInterpolation!(val, ocean.u[:,:,1,1], ocean.u[:,:,1,1], + .5, .5, 1, 1) +@time SeaIce.bilinearInterpolation!(val, ocean.u[:,:,1,1], ocean.u[:,:,1,1], .5, + .5, 1, 1) +@test val[1] ≈ .5 +@test val[2] ≈ .5 +SeaIce.bilinearInterpolation!(val, ocean.u[:,:,1,1], ocean.u[:,:,1,1], 1., 1., +1, 1) +@test val[1] ≈ .0 +@test val[2] ≈ .0 +SeaIce.bilinearInterpolation!(val, ocean.u[:,:,1,1], ocean.u[:,:,1,1], 0., 0., +1, 1) +@test val[1] ≈ 1. +@test val[2] ≈ 1. +SeaIce.bilinearInterpolation!(val, ocean.u[:,:,1,1], ocean.u[:,:,1,1], .25, .25, +1, 1) +@test val[1] ≈ .75 +@test val[2] ≈ .75 +SeaIce.bilinearInterpolation!(val, ocean.u[:,:,1,1], ocean.u[:,:,1,1], .75, .75, +1, 1) +@test val[1] ≈ .25 +@test val[2] ≈ .25 info("Testing cell binning - Area-based approach") @test SeaIce.findCellContainingPoint(ocean, [6.2,53.4], method="Area") == (1, 1) @@ -144,14 +168,18 @@ ocean.u[2, 1, 1, 1] = 1.0 ocean.u[2, 2, 1, 1] = 0.0 ocean.u[1, 2, 1, 1] = 0.0 ocean.v[:, :, 1, 1] = 0.0 -@test SeaIce.curl(ocean, .5, .5, 1, 1, 1, 1) > 0. +sw = Vector{Float64}(2) +se = Vector{Float64}(2) +ne = Vector{Float64}(2) +nw = Vector{Float64}(2) +@test SeaIce.curl(ocean, .5, .5, 1, 1, 1, 1, sw, se, ne, nw) > 0. ocean.u[1, 1, 1, 1] = 0.0 ocean.u[2, 1, 1, 1] = 0.0 ocean.u[2, 2, 1, 1] = 1.0 ocean.u[1, 2, 1, 1] = 1.0 ocean.v[:, :, 1, 1] = 0.0 -@test SeaIce.curl(ocean, .5, .5, 1, 1, 1, 1) < 0. +@test SeaIce.curl(ocean, .5, .5, 1, 1, 1, 1, sw, se, ne, nw) < 0. info("Testing atmosphere drag") sim = SeaIce.createSimulation() @@ -186,6 +214,7 @@ atmosphere.u[2, 2, 1, 1] = 0.0 atmosphere.u[1, 2, 1, 1] = 0.0 atmosphere.v[:, :, 1, 1] = 0.0 @test SeaIce.curl(atmosphere, .5, .5, 1, 1, 1, 1) > 0. +@test SeaIce.curl(atmosphere, .5, .5, 1, 1, 1, 1, sw, se, ne, nw) > 0. atmosphere.u[1, 1, 1, 1] = 0.0 atmosphere.u[2, 1, 1, 1] = 0.0 @@ -193,6 +222,7 @@ atmosphere.u[2, 2, 1, 1] = 1.0 atmosphere.u[1, 2, 1, 1] = 1.0 atmosphere.v[:, :, 1, 1] = 0.0 @test SeaIce.curl(atmosphere, .5, .5, 1, 1, 1, 1) < 0. +@test SeaIce.curl(atmosphere, .5, .5, 1, 1, 1, 1, sw, se, ne, nw) < 0. info("Testing findEmptyPositionInGridCell") diff --git a/test/icefloe.jl b/test/icefloe.jl @@ -28,3 +28,13 @@ sim = SeaIce.createSimulation(id="test") SeaIce.addIceFloeCylindrical!(sim, [ 0., 0.], 10., 1., verbose=false) SeaIce.addIceFloeCylindrical!(sim, [ 0., 0.], 10., 1., verbose=false) SeaIce.compareIceFloes(sim.ice_floes[1], sim.ice_floes[2]) + +SeaIce.plotIceFloeSizeDistribution(sim) +rm("test-ice-floe-size-distribution.png") +SeaIce.plotIceFloeSizeDistribution(sim, skip_fixed=false) +rm("test-ice-floe-size-distribution.png") +SeaIce.plotIceFloeSizeDistribution(sim, log_y=false) +rm("test-ice-floe-size-distribution.png") +SeaIce.plotIceFloeSizeDistribution(sim, size_type="areal") +rm("test-ice-floe-size-distribution.png") +@test_throws ErrorException SeaIce.plotIceFloeSizeDistribution(sim, size_type="asdf") diff --git a/test/memory-management.jl b/test/memory-management.jl @@ -0,0 +1,175 @@ +#!/usr/bin/env julia +import SeaIce +using Base.Test + +info("#### $(basename(@__FILE__)) ####") + +info("Testing memory footprint of SeaIce types") + +sim = SeaIce.createSimulation() +empty_sim_size = 96 +empty_sim_size_recursive = 472 + +@test sizeof(sim) == empty_sim_size +@test Base.summarysize(sim) == empty_sim_size_recursive + +size_per_icefloe = 352 +size_per_icefloe_recursive = 1136 + +info("Testing memory usage when adding ice floes") +for i=1:100 + SeaIce.addIceFloeCylindrical!(sim, [1., 1.], 1., 1., verbose=false) + + @test sizeof(sim) == empty_sim_size + + @test sizeof(sim.ice_floes) == sizeof(Int)*i + @test sizeof(sim.ice_floes[:]) == sizeof(Int)*i + @test Base.summarysize(sim.ice_floes) == size_per_icefloe_recursive*i + + sizeof(Int)*i + + @test Base.summarysize(sim) == empty_sim_size_recursive + sizeof(Int)*i + + size_per_icefloe_recursive*i + + @test Base.summarysize(sim.ice_floes[i]) == size_per_icefloe_recursive + + for j=1:i + @test sizeof(sim.ice_floes[j]) == size_per_icefloe + @test Base.summarysize(sim.ice_floes[j]) == size_per_icefloe_recursive + end + +end + +info("Checking memory footprint when overwriting simulation object") +sim = SeaIce.createSimulation() +@test sizeof(sim) == empty_sim_size +@test Base.summarysize(sim) == empty_sim_size_recursive + +info("Check memory usage when stepping time for empty simulation object") +sim = SeaIce.createSimulation() +sim.time_step = 1.0 +for i=1:10 + SeaIce.run!(sim, single_step=true, verbose=false) + @test sizeof(sim) == empty_sim_size + @test Base.summarysize(sim) == empty_sim_size_recursive +end + +info("Check memory when stepping time with single ice floe") +sim = SeaIce.createSimulation() +SeaIce.addIceFloeCylindrical!(sim, [1., 1.], 1., 1., verbose=false) +sim.time_step = 1.0 +for i=1:10 + SeaIce.run!(sim, single_step=true, verbose=false) + @test sizeof(sim) == empty_sim_size + @test Base.summarysize(sim) == empty_sim_size_recursive + + sizeof(Int)*length(sim.ice_floes) + + size_per_icefloe_recursive*length(sim.ice_floes) +end + +info("Check memory when stepping time with two separate ice floes") +sim = SeaIce.createSimulation() +SeaIce.addIceFloeCylindrical!(sim, [1., 1.], 1., 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [1., 1.], 3., 1., verbose=false) +sim.time_step = 1.0 +for i=1:10 + SeaIce.run!(sim, single_step=true, verbose=false) + @test sizeof(sim) == empty_sim_size + @test Base.summarysize(sim) == empty_sim_size_recursive + + sizeof(Int)*length(sim.ice_floes) + + size_per_icefloe_recursive*length(sim.ice_floes) +end + +info("Check memory when stepping time with two interacting ice floes (all to all)") +sim = SeaIce.createSimulation() +SeaIce.addIceFloeCylindrical!(sim, [1., 1.], 1., 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [1., 1.], 1.9, 1., verbose=false) +sim.time_step = 1.0 +for i=1:10 + SeaIce.run!(sim, single_step=true, verbose=false) + @test sizeof(sim) == empty_sim_size + @test Base.summarysize(sim) == empty_sim_size_recursive + + sizeof(Int)*length(sim.ice_floes) + + size_per_icefloe_recursive*length(sim.ice_floes) +end + +info("Check memory when stepping time with two interacting ice floes (cell sorting)") +sim = SeaIce.createSimulation() +SeaIce.addIceFloeCylindrical!(sim, [1., 1.], 1., 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [1., 1.], 1.9, 1., verbose=false) +nx, ny, nz = 5, 5, 1 +sim.ocean = SeaIce.createRegularOceanGrid([nx, ny, nz], [10., 10., 10.]) +sim.time_step = 1e-6 +SeaIce.run!(sim, single_step=true, verbose=false) +original_size_recursive = Base.summarysize(sim) +for i=1:10 + SeaIce.run!(sim, single_step=true, verbose=false) + @test Base.summarysize(sim) == original_size_recursive +end + +info("Checking if memory is freed after ended collision (all to all)") +sim = SeaIce.createSimulation(id="test") +SeaIce.addIceFloeCylindrical!(sim, [0., 0.], 10., 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [20.05, 0.], 10., 1., verbose=false) +sim.ice_floes[1].lin_vel[1] = 0.1 +SeaIce.setTotalTime!(sim, 10.0) +SeaIce.setTimeStep!(sim, epsilon=0.07, verbose=false) +original_size_recursive = Base.summarysize(sim) +SeaIce.run!(sim, verbose=false) +@test Base.summarysize(sim) == original_size_recursive + +info("Checking if memory is freed after ended collision (cell sorting)") +sim = SeaIce.createSimulation(id="test") +SeaIce.addIceFloeCylindrical!(sim, [0., 0.], 10., 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [20.05, 0.], 10., 1., verbose=false) +sim.ocean = SeaIce.createRegularOceanGrid([2, 2, 2], [40., 40., 10.]) +sim.ice_floes[1].lin_vel[1] = 0.1 +SeaIce.setTotalTime!(sim, 10.0) +SeaIce.setTimeStep!(sim, epsilon=0.07, verbose=false) +SeaIce.run!(sim, single_step=true, verbose=false) +original_sim_size_recursive = Base.summarysize(sim) +original_icefloes_size_recursive = Base.summarysize(sim.ice_floes) +original_ocean_size_recursive = Base.summarysize(sim.ocean) +original_atmosphere_size_recursive = Base.summarysize(sim.atmosphere) +SeaIce.run!(sim, verbose=false) +@test Base.summarysize(sim.ice_floes) == original_icefloes_size_recursive +@test Base.summarysize(sim.ocean) == original_ocean_size_recursive +@test Base.summarysize(sim.atmosphere) == original_atmosphere_size_recursive +@test Base.summarysize(sim) == original_sim_size_recursive + +sim = SeaIce.createSimulation(id="test") +SeaIce.addIceFloeCylindrical!(sim, [0., 0.], 10., 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [20.05, 0.], 10., 1., verbose=false) +sim.atmosphere = SeaIce.createRegularAtmosphereGrid([2, 2, 2], [40., 40., 10.]) +sim.ice_floes[1].lin_vel[1] = 0.1 +SeaIce.setTotalTime!(sim, 10.0) +SeaIce.setTimeStep!(sim, epsilon=0.07, verbose=false) +SeaIce.run!(sim, single_step=true, verbose=false) +original_sim_size_recursive = Base.summarysize(sim) +original_icefloes_size_recursive = Base.summarysize(sim.ice_floes) +original_ocean_size_recursive = Base.summarysize(sim.ocean) +original_atmosphere_size_recursive = Base.summarysize(sim.atmosphere) +SeaIce.run!(sim, verbose=false) +@test Base.summarysize(sim.ice_floes) == original_icefloes_size_recursive +@test Base.summarysize(sim.ocean) == original_ocean_size_recursive +@test Base.summarysize(sim.atmosphere) == original_atmosphere_size_recursive +@test Base.summarysize(sim) == original_sim_size_recursive + +sim = SeaIce.createSimulation(id="test") +SeaIce.addIceFloeCylindrical!(sim, [0., 0.], 10., 1., verbose=false) +SeaIce.addIceFloeCylindrical!(sim, [20.05, 0.], 10., 1., verbose=false) +sim.atmosphere = SeaIce.createRegularAtmosphereGrid([2, 2, 2], [40., 40., 10.]) +sim.ocean = SeaIce.createRegularOceanGrid([2, 2, 2], [40., 40., 10.]) +sim.ice_floes[1].lin_vel[1] = 0.1 +SeaIce.setTotalTime!(sim, 10.0) +SeaIce.setTimeStep!(sim, epsilon=0.07, verbose=false) +SeaIce.run!(sim, single_step=true, verbose=false) +original_sim_size_recursive = Base.summarysize(sim) +original_icefloes_size_recursive = Base.summarysize(sim.ice_floes) +original_ocean_size_recursive = Base.summarysize(sim.ocean) +original_atmosphere_size_recursive = Base.summarysize(sim.atmosphere) +SeaIce.run!(sim, verbose=false) +@test Base.summarysize(sim.ice_floes) == original_icefloes_size_recursive +@test Base.summarysize(sim.ocean) == original_ocean_size_recursive +@test Base.summarysize(sim.atmosphere) == original_atmosphere_size_recursive +@test Base.summarysize(sim) == original_sim_size_recursive + +SeaIce.printMemoryUsage(sim) diff --git a/test/profiling.jl b/test/profiling.jl @@ -85,7 +85,7 @@ function timeSingleStepInDenseSimulation(nx::Int; verbose::Bool=true, @test sim.ice_floes[nx].n_contacts == 0 @test sim.ice_floes[nx + 1].n_contacts == 1 @test sim.ice_floes[nx + 2].n_contacts == 4 - return t_elapsed + return t_elapsed, Base.summarysize(sim) end #nx = Int[4 8 16 32 64 96] @@ -95,13 +95,16 @@ t_elapsed = zeros(length(nx)) t_elapsed_all_to_all = zeros(length(nx)) t_elapsed_cell_sorting = zeros(length(nx)) t_elapsed_cell_sorting2 = zeros(length(nx)) +memory_usage_all_to_all = zeros(length(nx)) +memory_usage_cell_sorting = zeros(length(nx)) +memory_usage_cell_sorting2 = zeros(length(nx)) for i=1:length(nx) info("nx = $(nx[i])") - t_elapsed_all_to_all[i] = + t_elapsed_all_to_all[i], memory_usage_all_to_all[i] = timeSingleStepInDenseSimulation(Int(nx[i]), grid_sorting=false) - t_elapsed_cell_sorting[i] = + t_elapsed_cell_sorting[i], memory_usage_cell_sorting[i] = timeSingleStepInDenseSimulation(Int(nx[i]), grid_sorting=true) - t_elapsed_cell_sorting2[i] = + t_elapsed_cell_sorting2[i], memory_usage_cell_sorting2[i] = timeSingleStepInDenseSimulation(Int(nx[i]), grid_sorting=true, include_atmosphere=true) elements[i] = nx[i]*nx[i] @@ -148,4 +151,46 @@ Plots.plot!(elements, fit_cell_sorting2(elements), Plots.title!("Dense granular system " * "(host: $(gethostname()))") Plots.xaxis!("Number of ice floes") Plots.yaxis!("Wall time per time step [s]") -Plots.savefig("profiling.pdf") +Plots.savefig("profiling-cpu.pdf") + +Plots.scatter(elements, memory_usage_all_to_all .÷ 1024, + xscale=:log10, + yscale=:log10, + label="All to all") +fit_all_to_all = CurveFit.curve_fit(CurveFit.PowerFit, + elements, memory_usage_all_to_all .÷ 1024) +label_all_to_all = @sprintf "%1.3g n^%3.2f" fit_all_to_all.coefs[1] fit_all_to_all.coefs[2] +Plots.plot!(elements, fit_all_to_all(elements), + xscale=:log10, + yscale=:log10, + label=label_all_to_all) + +Plots.scatter!(elements, memory_usage_cell_sorting .÷ 1024, + xscale=:log10, + yscale=:log10, + label="Cell-based spatial decomposition (ocean only)") +fit_cell_sorting = CurveFit.curve_fit(CurveFit.PowerFit, + elements, memory_usage_cell_sorting .÷ 1024) +label_cell_sorting = @sprintf "%1.3g n^%3.2f" fit_cell_sorting.coefs[1] fit_cell_sorting.coefs[2] +Plots.plot!(elements, fit_cell_sorting(elements), + xscale=:log10, + yscale=:log10, + label=label_cell_sorting) + +Plots.scatter!(elements, memory_usage_cell_sorting2 .÷ 1024, + xscale=:log10, + yscale=:log10, + label="Cell-based spatial decomposition (ocean + atmosphere)") +fit_cell_sorting2 = CurveFit.curve_fit(CurveFit.PowerFit, + elements, + memory_usage_cell_sorting2 .÷ 1024) +label_cell_sorting2 = @sprintf "%1.3g n^%3.2f" fit_cell_sorting2.coefs[1] fit_cell_sorting2.coefs[2] +Plots.plot!(elements, fit_cell_sorting2(elements), + xscale=:log10, + yscale=:log10, + label=label_cell_sorting2) + +Plots.title!("Dense granular system " * "(host: $(gethostname()))") +Plots.xaxis!("Number of ice floes") +Plots.yaxis!("Memory usage [kb]") +Plots.savefig("profiling-memory-usage.pdf") diff --git a/test/profiling.sh b/test/profiling.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Optionally use this script to launch profiling.jl with different Julia +# optimization levels. Defaults are --optimize=2, --math-mode=ieee. + +declare -a arr=(\ + "--procs 1 --optimize=1 --math-mode=ieee" \ + "--procs 1 --optimize=2 --math-mode=ieee" \ + "--procs 1 --optimize=3 --math-mode=ieee" \ + "--procs 1 --optimize=1 --math-mode=fast" \ + "--procs 1 --optimize=2 --math-mode=fast" \ + "--procs 1 --optimize=3 --math-mode=fast") + +for flags in "${arr[@]}"; do + julia --color=yes $flags profiling.jl + mv profiling-cpu.pdf "profiling-cpu.$flags.pdf" + mv profiling-memory-usage.pdf "profiling-memory-usage.$flags.pdf" +done diff --git a/test/runtests.jl b/test/runtests.jl @@ -2,6 +2,7 @@ import SeaIce using Base.Test include("icefloe.jl") +include("util.jl") include("temporal.jl") include("contact-search-and-geometry.jl") include("collision-2floes-normal.jl") @@ -14,3 +15,4 @@ include("jld.jl") include("grid.jl") include("ocean.jl") include("atmosphere.jl") +include("memory-management.jl") diff --git a/test/util.jl b/test/util.jl @@ -0,0 +1,28 @@ +#!/usr/bin/env julia +import SeaIce +using Base.Test + +info("#### $(basename(@__FILE__)) ####") + +info("Testing power-law RNG") + +@test 1 == length(SeaIce.randpower()) +@test () == size(SeaIce.randpower()) +@test 1 == length(SeaIce.randpower(1)) +@test () == size(SeaIce.randpower(1)) +@test 4 == length(SeaIce.randpower((2,2))) +@test (2,2) == size(SeaIce.randpower((2,2))) +@test 5 == length(SeaIce.randpower(5)) +@test (5,) == size(SeaIce.randpower(5)) + +Base.Random.srand(1) +for i=1:10^5 + @test 0. <= SeaIce.randpower() <= 1. + @test 0. <= SeaIce.randpower(1, 1., 0., 1.) <= 1. + @test 0. <= SeaIce.randpower(1, 1., 0., .1) <= .1 + @test 5. <= SeaIce.randpower(1, 1., 5., 6.) <= 6. + @test 0. <= minimum(SeaIce.randpower((2,2), 1., 0., 1.)) + @test 1. >= maximum(SeaIce.randpower((2,2), 1., 0., 1.)) + @test 0. <= minimum(SeaIce.randpower(5, 1., 0., 1.)) + @test 1. >= minimum(SeaIce.randpower(5, 1., 0., 1.)) +end