tMerge branch 'master' of github.com:anders-dc/SeaIce.jl - Granular.jl - Julia package for granular dynamics simulation
 (HTM) git clone git://src.adamsgaard.dk/Granular.jl
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit b189f7b4e3f1e021691cc3a766b66bf2f2557538
 (DIR) parent 87def518c59023ee58581b32919e0aa9cea9ed6c
 (HTM) 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:
         M REQUIRE                             |       1 +
         M src/SeaIce.jl                       |       1 +
         M src/atmosphere.jl                   |      55 ++++++++++++++++---------------
         M src/contact_search.jl               |      73 +++++++++++++++++++++----------
         M src/datatypes.jl                    |     209 +++++++++++++++----------------
         M src/grid.jl                         |     193 ++++++++++++++++++++-----------
         M src/icefloe.jl                      |     235 ++++++++++++++++++++-----------
         M src/interaction.jl                  |      23 ++++++++---------------
         M src/io.jl                           |      62 +++++++++++++++++--------------
         M src/ocean.jl                        |      46 +++++++++++++++++--------------
         M src/simulation.jl                   |      55 ++++++++++++++++++++++++++++---
         M src/temporal.jl                     |      39 +++++++++++++++++++------------
         M src/temporal_integration.jl         |       3 +++
         A src/util.jl                         |      43 ++++++++++++++++++++++++++++++
         M test/contact-search-and-geometry.jl |      44 +++++++++++++++++++++++++++++++
         M test/grid.jl                        |      74 ++++++++++++++++++++++---------
         M test/icefloe.jl                     |      10 ++++++++++
         A test/memory-management.jl           |     175 +++++++++++++++++++++++++++++++
         M test/profiling.jl                   |      55 ++++++++++++++++++++++++++++---
         A test/profiling.sh                   |      18 ++++++++++++++++++
         M test/runtests.jl                    |       2 ++
         A test/util.jl                        |      28 ++++++++++++++++++++++++++++
       
       22 files changed, 1031 insertions(+), 413 deletions(-)
       ---
 (DIR) diff --git a/REQUIRE b/REQUIRE
       t@@ -1,3 +1,4 @@
        julia 0.6
        WriteVTK
        NetCDF
       +PyPlot
 (DIR) diff --git a/src/SeaIce.jl b/src/SeaIce.jl
       t@@ -19,5 +19,6 @@ include("temporal_integration.jl")
        include("io.jl")
        include("ocean.jl")
        include("atmosphere.jl")
       +include("util.jl")
        
        end # module end
 (DIR) diff --git a/src/atmosphere.jl b/src/atmosphere.jl
       t@@ -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
       t@@ -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
       t@@ -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]
       t@@ -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)
       t@@ -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
        
       t@@ -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!
       t@@ -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!
       t@@ -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
       t@@ -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
 (DIR) diff --git a/src/contact_search.jl b/src/contact_search.jl
       t@@ -32,6 +32,7 @@ function findContacts!(simulation::Simulation;
            else
                error("Unknown contact search method '$method'")
            end
       +    nothing
        end
        
        export interIceFloePositionVector
       t@@ -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!
       t@@ -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!
       t@@ -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!
       t@@ -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
       t@@ -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
 (DIR) diff --git a/src/datatypes.jl b/src/datatypes.jl
       t@@ -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
       t@@ -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
        
        #=
       t@@ -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}
       t@@ -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}
       t@@ -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
        
        #=
       t@@ -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], 
       t@@ -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}
       t@@ -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
       t@@ -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
 (DIR) diff --git a/src/grid.jl b/src/grid.jl
       t@@ -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
        
        """
       t@@ -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
       t@@ -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) +
       t@@ -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
        
       t@@ -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
       t@@ -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
       t@@ -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
       t@@ -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)
       t@@ -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) ≈
       t@@ -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]]
       t@@ -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]) +
       t@@ -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
        
       t@@ -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")
       t@@ -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)
        
       t@@ -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
       t@@ -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
 (DIR) diff --git a/src/icefloe.jl b/src/icefloe.jl
       t@@ -1,4 +1,5 @@
        ## Manage icefloes in the model
       +import PyPlot
        
        export addIceFloeCylindrical!
        """
       t@@ -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,
       t@@ -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
       t@@ -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
       t@@ -151,6 +152,7 @@ function addIceFloeCylindrical!(simulation::Simulation,
        
            # Add to simulation object
            addIceFloe!(simulation, icefloe, verbose)
       +    nothing
        end
        
        export iceFloeCircumreference
       t@@ -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
       t@@ -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
       t@@ -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
 (DIR) diff --git a/src/interaction.jl b/src/interaction.jl
       t@@ -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!
       t@@ -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
 (DIR) diff --git a/src/io.jl b/src/io.jl
       t@@ -41,6 +41,7 @@ function writeSimulation(simulation::Simulation;
                    info("simulation written to $filename")
                end
            end
       +    nothing
        end
        
        export readSimulation
       t@@ -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")
       t@@ -85,6 +87,7 @@ function writeSimulationStatus(simulation::Simulation;
            if verbose
                info("wrote status to $filename")
            end
       +    nothing
        end
        
        export readSimulationStatus
       t@@ -197,6 +200,7 @@ function status(folder::String=".";
                    repeat = false
                end
            end
       +    nothing
        end
        
        export writeVTK
       t@@ -240,6 +244,7 @@ function writeVTK(simulation::Simulation;
                                simulation.file_number)
                writeGridVTK(simulation.atmosphere, filename, verbose=verbose)
            end
       +    nothing
        end
        
        export writeIceFloeVTK
       t@@ -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
       t@@ -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
       t@@ -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")
       t@@ -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")
       t@@ -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")
       t@@ -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")
       t@@ -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")
       t@@ -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")
       t@@ -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")
       t@@ -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")
       t@@ -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")
       t@@ -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")
       t@@ -580,6 +584,7 @@ function writeIceFloeInteractionVTK(simulation::Simulation,
                write(f, "  </PolyData>\n")
                write(f, "</VTKFile>\n")
            end
       +    nothing
        end
        
        export writeOceanVTK
       t@@ -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
        
       t@@ -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
       t@@ -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
       t@@ -18176,6 +18180,7 @@ function writeParaviewStateFile(simulation::Simulation;
            if verbose
                info("Output file: " * filename)
            end
       +    nothing
        end
        
        export removeSimulationFiles
       t@@ -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
 (DIR) diff --git a/src/ocean.jl b/src/ocean.jl
       t@@ -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)
        
       t@@ -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
       t@@ -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]
       t@@ -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)
       t@@ -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
        
       t@@ -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!
       t@@ -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
       t@@ -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!
       t@@ -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
       t@@ -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
       t@@ -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
 (DIR) diff --git a/src/simulation.jl b/src/simulation.jl
       t@@ -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
       t@@ -175,7 +175,7 @@ function run!(simulation::Simulation;
                incrementCurrentTime!(simulation, simulation.time_step)
        
                if single_step
       -            return
       +            return nothing
                end
            end
        
       t@@ -191,6 +191,8 @@ function run!(simulation::Simulation;
                reportSimulationTimeToStdout(simulation)
                println()
            end
       +    gc()
       +    nothing
        end
        
        export addIceFloe!
       t@@ -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!
       t@@ -230,6 +233,7 @@ function zeroForcesAndTorques!(simulation::Simulation)
                icefloe.torque = 0.
                icefloe.pressure = 0.
            end
       +    nothing
        end
        
        export reportSimulationTimeToStdout
       t@@ -237,6 +241,7 @@ export reportSimulationTimeToStdout
        function reportSimulationTimeToStdout(simulation::Simulation)
            print("\r  t = ", simulation.time, '/', simulation.time_total,
                  " s            ")
       +    nothing
        end
        
        export compareSimulations
       t@@ -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
 (DIR) diff --git a/src/temporal.jl b/src/temporal.jl
       t@@ -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")
       t@@ -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
       t@@ -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
       t@@ -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
       t@@ -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.")
       t@@ -147,4 +155,5 @@ function setTimeStep!(simulation::Simulation;
            if verbose
                info("Time step length t=",  simulation.time_step, " s")
            end
       +    nothing
        end
 (DIR) diff --git a/src/temporal_integration.jl b/src/temporal_integration.jl
       t@@ -36,6 +36,7 @@ function updateIceFloeKinematics!(simulation::Simulation;
            else
                error("Unknown integration method '$method'")
            end
       +    nothing
        end
        
        export updateIceFloeKinematicsTwoTermTaylor!
       t@@ -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!
       t@@ -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
 (DIR) diff --git a/src/util.jl b/src/util.jl
       t@@ -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
 (DIR) diff --git a/test/contact-search-and-geometry.jl b/test/contact-search-and-geometry.jl
       t@@ -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
 (DIR) diff --git a/test/grid.jl b/test/grid.jl
       t@@ -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.
        
       t@@ -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")
       t@@ -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)
       t@@ -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()
       t@@ -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
       t@@ -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")
 (DIR) diff --git a/test/icefloe.jl b/test/icefloe.jl
       t@@ -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")
 (DIR) diff --git a/test/memory-management.jl b/test/memory-management.jl
       t@@ -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)
 (DIR) diff --git a/test/profiling.jl b/test/profiling.jl
       t@@ -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]
       t@@ -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]
       t@@ -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")
 (DIR) diff --git a/test/profiling.sh b/test/profiling.sh
       t@@ -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
 (DIR) diff --git a/test/runtests.jl b/test/runtests.jl
       t@@ -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")
       t@@ -14,3 +15,4 @@ include("jld.jl")
        include("grid.jl")
        include("ocean.jl")
        include("atmosphere.jl")
       +include("memory-management.jl")
 (DIR) diff --git a/test/util.jl b/test/util.jl
       t@@ -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