seaice-experiments

sea ice experiments using Granular.jl
git clone git://src.adamsgaard.dk/seaice-experiments # fast
git clone https://src.adamsgaard.dk/seaice-experiments.git # slow
Log | Files | Refs | README | LICENSE Back to index

jamming-ocean-atmosphere.jl (16831B)


      1 #!/usr/bin/env julia
      2 import Granular
      3 import ArgParse
      4 import Plots
      5 
      6 verbose = false
      7 
      8 function parse_command_line()
      9     s = ArgParse.ArgParseSettings()
     10     ArgParse.@add_arg_table s begin
     11         "--width"
     12             help = "strait width [m]"
     13             arg_type = Float64
     14             default = 5e3
     15         "--E"
     16             help = "Young's modulus [Pa]"
     17             arg_type = Float64
     18             default = 0.
     19         "--nu"
     20             help = "Poisson's ratio [-]"
     21             arg_type = Float64
     22             default = 0.
     23         "--k_n"
     24             help = "normal stiffness [N/m]"
     25             arg_type = Float64
     26             default = 1e7
     27         "--k_t"
     28             help = "tangential stiffness [N/m]"
     29             arg_type = Float64
     30             default = 1e7
     31         "--gamma_n"
     32             help = "normal viscosity [N/(m/s)]"
     33             arg_type = Float64
     34             default = 0.
     35         "--gamma_t"
     36             help = "tangential viscosity [N/(m/s)]"
     37             arg_type = Float64
     38             default = 0.
     39         "--mu_s"
     40             help = "static friction coefficient [-]"
     41             arg_type = Float64
     42             default = 0.5
     43         "--mu_d"
     44             help = "dynamic friction coefficient [-]"
     45             arg_type = Float64
     46             default = 0.5
     47         "--mu_s_wall"
     48             help = "static friction coefficient for wall particles [-]"
     49             arg_type = Float64
     50             default = 0.5
     51         "--mu_d_wall"
     52             help = "dynamic friction coefficient for wall particles [-]"
     53             arg_type = Float64
     54             default = 0.5
     55         "--tensile_strength"
     56             help = "the maximum tensile strength [Pa]"
     57             arg_type = Float64
     58             default = 0.
     59         "--r_min"
     60             help = "minimum grain radius [m]"
     61             arg_type = Float64
     62             default = 1e3
     63         "--r_max"
     64             help = "maximum grain radius [m]"
     65             arg_type = Float64
     66             default = 1e3
     67         "--rotating"
     68             help = "allow the grains to rotate"
     69             arg_type = Bool
     70             default = true
     71         "--ocean_vel_fac"
     72             help = "ocean velocity factor [-]"
     73             arg_type = Float64
     74             default = 1e4
     75         "--atmosphere_vel_fac"
     76             help = "atmosphere velocity [m/s]"
     77             arg_type = Float64
     78             default = 2.0
     79         "--total_hours"
     80             help = "hours of simulation time [h]"
     81             arg_type = Float64
     82             default = 6.
     83         "--nruns"
     84             help = "number of runs in ensemble"
     85             arg_type = Int
     86             default = 1
     87         "id"
     88             help = "simulation id"
     89             required = true
     90     end
     91     return ArgParse.parse_args(s)
     92 end
     93 
     94 function report_args(parsed_args)
     95     println("Parsed args:")
     96     for (arg,val) in parsed_args
     97         println("  $arg  =>  $val")
     98     end
     99 end
    100 
    101 function run_simulation(id::String,
    102                         width::Float64,
    103                         E::Float64,
    104                         nu::Float64,
    105                         k_n::Float64,
    106                         k_t::Float64,
    107                         gamma_n::Float64,
    108                         gamma_t::Float64,
    109                         mu_s::Float64,
    110                         mu_d::Float64,
    111                         mu_s_wall::Float64,
    112                         mu_d_wall::Float64,
    113                         tensile_strength::Float64,
    114                         r_min::Float64,
    115                         r_max::Float64,
    116                         rotating::Bool,
    117                         ocean_vel_fac::Float64,
    118                         atmosphere_vel_fac::Float64,
    119                         total_hours::Float64,
    120                         seed::Int)
    121 
    122     info("## EXPERIMENT: " * id * " ##")
    123     sim = Granular.createSimulation(id=id)
    124 
    125     Lx = 50.e3
    126     Lx_constriction = width
    127     L = [Lx, Lx*1.5, 1e3]
    128     Ly_constriction = 20e3
    129     dx = r_max*2.
    130 
    131     n = [Int(ceil(L[1]/dx/2.)), Int(ceil(L[2]/dx/2.)), 2]
    132 
    133     # Initialize confining walls, which are grains that are fixed in space
    134     r = r_max
    135     r_min = r/4.
    136     h = 1.
    137     r_walls = r_min
    138 
    139     ## N-S segments
    140     for y in linspace((L[2] - Ly_constriction)/2.,
    141                       Ly_constriction + (L[2] - Ly_constriction)/2., 
    142                       Int(round(Ly_constriction/(r_walls*2))))
    143         Granular.addGrainCylindrical!(sim, [(Lx - Lx_constriction)/2., y], 
    144                                      r_walls, h, fixed=true, verbose=false)
    145     end
    146     for y in linspace((L[2] - Ly_constriction)/2.,
    147                       Ly_constriction + (L[2] - Ly_constriction)/2., 
    148                       Int(round(Ly_constriction/(r_walls*2))))
    149         Granular.addGrainCylindrical!(sim, [Lx_constriction + (L[1] - 
    150                                       Lx_constriction)/2., y], r_walls, h, 
    151                                      fixed=true, verbose=false)
    152     end
    153 
    154     dx = 2.*r_walls*sin(atan((Lx - Lx_constriction)/(L[2] - Ly_constriction)))
    155 
    156     ## NW diagonal
    157     x = r_walls:dx:((Lx - Lx_constriction)/2.)
    158     y = linspace(L[2] - r_walls, (L[2] - Ly_constriction)/2. + Ly_constriction + 
    159                  r_walls, length(x))
    160     for i in 1:length(x)
    161         Granular.addGrainCylindrical!(sim, [x[i], y[i]], r_walls, h, fixed=true, 
    162                                      verbose=false)
    163     end
    164 
    165     ## NE diagonal
    166     x = (L[1] - r_walls):(-dx):((Lx - Lx_constriction)/2. + Lx_constriction)
    167     y = linspace(L[2] - r_walls, (L[2] - Ly_constriction)/2. + Ly_constriction + 
    168                  r_walls, length(x))
    169     for i in 1:length(x)
    170         Granular.addGrainCylindrical!(sim, [x[i], y[i]], r_walls, h, fixed=true, 
    171                                      verbose=false)
    172     end
    173 
    174     ## SW diagonal
    175     x = r_walls:dx:((Lx - Lx_constriction)/2.)
    176     y = linspace(r, (L[2] - Ly_constriction)/2. - r_walls, length(x))
    177     for i in 1:length(x)
    178         Granular.addGrainCylindrical!(sim, [x[i], y[i]], r_walls, h, fixed=true, 
    179                                      verbose=false)
    180     end
    181 
    182     ## SE diagonal
    183     x = (L[1] - r_walls):(-dx):((Lx - Lx_constriction)/2. + Lx_constriction)
    184     y = linspace(r_walls, (L[2] - Ly_constriction)/2. - r_walls, length(x))
    185     for i in 1:length(x)
    186         Granular.addGrainCylindrical!(sim, [x[i], y[i]], r_walls, h, fixed=true, 
    187                                      verbose=false)
    188     end
    189 
    190     n_walls = length(sim.grains)
    191     info("added $(n_walls) fixed grains as walls")
    192 
    193     # Initialize ocean
    194     sim.ocean = Granular.createRegularOceanGrid(n, L, name="poiseuille_flow")
    195 
    196     # Determine stream function value for all grid cells, row by row.
    197     # At y=0 and y=Ly:  psi = a*ocean_vel_fac*(x - Lx/2.).^3 with a = 1.
    198     # The value of a increases when strait is narrow, and psi values are 
    199     # constant outside domain>
    200     psi = similar(sim.ocean.v[:, :, 1, 1])
    201     Granular.sortGrainsInGrid!(sim, sim.ocean)
    202     for j=1:size(psi, 2)
    203 
    204         # Check width of domain in the current row
    205         if sim.ocean.yq[1, j] < (L[2] - Ly_constriction)/2.
    206             # lower triangle
    207             W = (Lx - width)*
    208                 (1. - sim.ocean.yq[1, j]/((L[2] - Ly_constriction)/2.)) + width
    209 
    210         elseif sim.ocean.yq[1, j] < Ly_constriction+(L[2] - Ly_constriction)/2.  
    211             # strait
    212             W = width
    213 
    214         else
    215             y_min = Ly_constriction + (L[2] - Ly_constriction)/2.
    216 
    217             # upper triangle
    218             W = (Lx - width)*(sim.ocean.yq[1, j] - y_min)/(L[2] - y_min) + width
    219         end
    220 
    221         # transform [Lx/2 - W/2; Lx/2 + W/2] to [-2;2]
    222         x_ = (sim.ocean.xq[:, j] - (Lx/2. - W/2.))/W*4. - 2.
    223 
    224         psi[:, j] = tanh(x_)
    225     end
    226 
    227     # determine ocean velocities (u and v) from stream function derivatives
    228     for i=1:size(psi, 1)
    229         if i == 1
    230             sim.ocean.v[i, :, 1, 1] = -(psi[i+1, :] - psi[i, :])./
    231                 (sim.ocean.xq[i+1, :] - sim.ocean.xq[i, :])
    232         elseif i == size(psi, 1)
    233             sim.ocean.v[i, :, 1, 1] = -(psi[i, :] - psi[i-1, :])./
    234                 (sim.ocean.xq[i, :] - sim.ocean.xq[i-1, :])
    235         else
    236             sim.ocean.v[i, :, 1, 1] = -(psi[i+1, :] - psi[i-1, :])./
    237                 (sim.ocean.xq[i+1, :] - sim.ocean.xq[i-1, :])
    238         end
    239     end
    240 
    241     for j=1:size(psi, 2)
    242         if j == 1
    243             sim.ocean.u[:, j, 1, 1] = (psi[:, j+1] - psi[:, j])./
    244                 (sim.ocean.yq[:, j+1] - sim.ocean.yq[:, j])
    245         elseif j == size(psi, 2)
    246             sim.ocean.u[:, j, 1, 1] = (psi[:, j] - psi[:, j-1])./
    247                 (sim.ocean.yq[:, j] - sim.ocean.yq[:, j-1])
    248         else
    249             sim.ocean.u[:, j, 1, 1] = (psi[:, j+1] - psi[:, j-1])./
    250                 (sim.ocean.yq[:, j+1] - sim.ocean.yq[:, j-1])
    251         end
    252     end
    253     sim.ocean.h[:,:,1,1] = psi  # this field is unused; use for stream function
    254     sim.ocean.u *= ocean_vel_fac
    255     sim.ocean.v *= ocean_vel_fac
    256 
    257     # Constant velocities along y:
    258     #sim.ocean.v[:, :, 1, 1] = ocean_vel_fac*((sim.ocean.xq - Lx/2.).^2 - 
    259     #                                        Lx^2./4.)
    260     sim.atmosphere = Granular.createRegularAtmosphereGrid(n, L, 
    261                                                         name="uniform_flow")
    262     sim.atmosphere.v[:, :, 1, 1] = -atmosphere_vel_fac
    263 
    264     # Initialize grains in wedge north of the constriction
    265     iy = 1
    266     dy = sqrt((2.*r_walls)^2. - dx^2.)
    267     spacing_to_boundaries = 2.*r
    268     floe_padding = .5*r
    269     noise_amplitude = floe_padding
    270     Base.Random.srand(seed)
    271     for y in (L[2] - r - noise_amplitude):(-(2.*r + floe_padding)):((L[2] - 
    272         Ly_constriction)/2. + Ly_constriction)
    273         for x in (r + noise_amplitude):(2.*r + floe_padding):(Lx - r - 
    274                                                               noise_amplitude)
    275             if iy % 2 == 0
    276                 x += 1.5*r
    277             end
    278 
    279             x_ = x + noise_amplitude*(0.5 - Base.Random.rand())
    280             y_ = y + noise_amplitude*(0.5 - Base.Random.rand())
    281 
    282             if y_ < -dy/dx*x_ + L[2] + spacing_to_boundaries
    283                 continue
    284             end
    285                 
    286             if y_ < dy/dx*x_ + (L[2] - dy/dx*Lx) + spacing_to_boundaries
    287                 continue
    288             end
    289                 
    290             r_rand = r_min + Base.Random.rand()*(r - r_min)
    291             Granular.addGrainCylindrical!(sim, [x_, y_], r_rand, h, 
    292                                          verbose=false)
    293         end
    294         iy += 1
    295     end
    296     n = length(sim.grains) - n_walls
    297     info("added $(n) grains")
    298 
    299     # Remove old simulation files
    300     Granular.removeSimulationFiles(sim)
    301 
    302     for i=1:length(sim.grains)
    303         sim.grains[i].youngs_modulus = E
    304         sim.grains[i].poissons_ratio = nu
    305         sim.grains[i].contact_stiffness_normal = k_n
    306         sim.grains[i].contact_stiffness_tangential = k_t
    307         sim.grains[i].contact_viscosity_normal = gamma_n
    308         sim.grains[i].contact_viscosity_tangential = gamma_t
    309         sim.grains[i].contact_static_friction = mu_s
    310         sim.grains[i].contact_dynamic_friction = mu_d
    311         sim.grains[i].tensile_strength = tensile_strength
    312         sim.grains[i].rotating = rotating
    313         if sim.grains[i].fixed == true
    314             sim.grains[i].contact_static_friction = mu_s_wall
    315             sim.grains[i].contact_dynamic_friction = mu_d_wall
    316         end
    317     end
    318 
    319     # Set temporal parameters
    320     Granular.setTotalTime!(sim, total_hours*60.*60.)
    321     #Granular.setOutputFileInterval!(sim, 60.*5.)  # output every 5 mins
    322     Granular.setOutputFileInterval!(sim, 60.)  # output every 1 mins
    323     Granular.setTimeStep!(sim, verbose=verbose)
    324     Granular.writeVTK(sim, verbose=verbose)
    325 
    326     profile = false
    327 
    328     ice_flux = Float64[]
    329     jammed = false
    330     it_before_eval = 10
    331     time_jammed = 0.
    332     time_jammed_criterion = 60.*60.  # define as jammed after this duration
    333 
    334     while sim.time < sim.time_total
    335 
    336         # run simulation for it_before_eval time steps
    337         for it=1:it_before_eval
    338             if sim.time >= sim.time_total*.75 && profile
    339                 @profile Granular.run!(sim, status_interval=1, single_step=true,
    340                                     verbose=verbose, show_file_output=verbose)
    341                 Profile.print()
    342                 profile = false
    343             else
    344                 Granular.run!(sim, status_interval=1, single_step=true,
    345                             verbose=verbose, show_file_output=verbose)
    346             end
    347         end
    348 
    349         # assert if the system is jammed by looking at ice-floe mass change in 
    350         # the number of jammed grains
    351         if jammed
    352             time_jammed += sim.time_dt*float(it_before_eval)
    353             if time_jammed >= 60.*60.  # 1 h
    354                 info("$t s: system jammed for more than " * 
    355                 "$time_jammed_criterion s, stopping simulation")
    356                 exit()
    357             end
    358         end
    359 
    360         ice_mass_outside_domain = 0.
    361         for icefloe in sim.grains
    362             if !icefloe.enabled
    363                 ice_mass_outside_domain += icefloe.mass
    364             end
    365         end
    366         append!(ice_flux, ice_mass_outside_domain)
    367 
    368         # add new grains from the top
    369         for i=1:size(sim.ocean.xh, 1)
    370             if sim.ocean.grain_list[i, end] == []
    371 
    372                 x, y = Granular.getCellCenterCoordinates(sim.ocean, i, 
    373                                                        size(sim.ocean.xh, 2))
    374 
    375                 x_ = x + noise_amplitude*(0.5 - Base.Random.rand())
    376                 y_ = y + noise_amplitude*(0.5 - Base.Random.rand())
    377                 r_rand = r_min + Base.Random.rand()*(r - r_min)
    378 
    379                 Granular.addGrainCylindrical!(sim, [x_, y_], r_rand, h, 
    380                         verbose=false,
    381                         youngs_modulus=E,
    382                         poissons_ratio=nu,
    383                         contact_stiffness_normal=k_n,
    384                         contact_stiffness_tangential=k_t,
    385                         contact_viscosity_normal=gamma_n,
    386                         contact_viscosity_tangential=gamma_t,
    387                         contact_static_friction = mu_s,
    388                         contact_dynamic_friction = mu_d,
    389                         tensile_strength = tensile_strength,
    390                         rotating=rotating)
    391             end
    392         end
    393     end
    394 
    395     t = linspace(0., sim.time_total, length(ice_flux))
    396     writedlm(sim.id * "-ice-flux.txt", [t, ice_flux])
    397     return t, ice_flux
    398 end
    399 
    400 function main()
    401     parsed_args = parse_command_line()
    402     report_args(parsed_args)
    403 
    404     nruns = parsed_args["nruns"]
    405     t = Float64[]
    406     t_jam = ones(nruns)*parsed_args["total_hours"]*60.*60.*2.
    407     Plots.gr()
    408 
    409     for i=1:nruns
    410         seed = i
    411         t, ice_flux = run_simulation(parsed_args["id"] * "-seed" * string(seed),
    412                                      parsed_args["width"],
    413                                      parsed_args["E"],
    414                                      parsed_args["nu"],
    415                                      parsed_args["k_n"],
    416                                      parsed_args["k_t"],
    417                                      parsed_args["gamma_n"],
    418                                      parsed_args["gamma_t"],
    419                                      parsed_args["mu_s"],
    420                                      parsed_args["mu_d"],
    421                                      parsed_args["mu_s_wall"],
    422                                      parsed_args["mu_d_wall"],
    423                                      parsed_args["tensile_strength"],
    424                                      parsed_args["r_min"],
    425                                      parsed_args["r_max"],
    426                                      parsed_args["rotating"],
    427                                      parsed_args["ocean_vel_fac"],
    428                                      parsed_args["atmosphere_vel_fac"],
    429                                      parsed_args["total_hours"],
    430                                      i)
    431         Plots.plot!(t/(60.*60.), ice_flux)
    432 
    433         time_elapsed_while_jammed = 0.
    434         for it=(length(t) - 1):-1:1
    435             if ice_flux[it] ≈ ice_flux[end]
    436                 time_elapsed_while_jammed = t[end] - t[it]
    437             end
    438         end
    439 
    440         if time_elapsed_while_jammed > 60.*60.
    441             t_jam[i] = t[end] - time_elapsed_while_jammed
    442             info("simulation $i jammed at t = $(t_jam[i]/(60.*60.)) h")
    443         end
    444 
    445     end
    446     Plots.title!(parsed_args["id"])
    447     Plots.xaxis!("Time [h]")
    448     Plots.yaxis!("Cumulative ice throughput [kg]")
    449 
    450     Plots.savefig(parsed_args["id"])
    451     Plots.closeall()
    452 
    453     jam_fraction = zeros(length(t))
    454     for it=1:length(t)
    455         for i=1:nruns
    456             if t_jam[i] <= t[it]
    457                 jam_fraction[it] += 1./float(nruns)
    458             end
    459         end
    460     end
    461     Plots.plot(t/(60.*60.), jam_fraction)
    462     Plots.title!(parsed_args["id"] * ", N = " * string(nruns))
    463     Plots.xaxis!("Time [h]")
    464     Plots.yaxis!("Fraction of runs jammed [-]")
    465     Plots.savefig(parsed_args["id"] * "-jam_fraction.pdf")
    466 end
    467 
    468 main()