Introduction
Introduction Statistics Contact Development Disclaimer Help
tio.jl - Granular.jl - Julia package for granular dynamics simulation
git clone git://src.adamsgaard.dk/Granular.jl
Log
Files
Refs
README
LICENSE
---
tio.jl (58171B)
---
1 import WriteVTK
2 import Pkg
3 import Dates
4 import JLD2
5 using DelimitedFiles
6
7 ## IO functions
8
9 export writeSimulation
10 """
11 writeSimulation(simulation::Simulation;
12 filename::String="",
13 folder::String=".",
14 verbose::Bool=true)
15
16 Write all content from `Simulation` to disk in JLD2 format. If the `fil…
17 parameter is not specified, it will be saved to a subdirectory under the…
18 directory named after the simulation identifier `simulation.id`.
19 """
20 function writeSimulation(simulation::Simulation;
21 filename::String="",
22 folder::String=".",
23 verbose::Bool=true)
24 if filename == ""
25 folder = folder * "/" * simulation.id
26 mkpath(folder)
27 filename = string(folder, "/", simulation.id, ".",
28 simulation.file_number, ".jld2")
29 end
30
31 JLD2.@save(filename, simulation)
32
33 if verbose
34 @info "simulation written to $filename"
35 end
36 nothing
37 end
38
39 export readSimulation
40 """
41 readSimulation(filename::String="";
42 verbose::Bool=true)
43
44 Return `Simulation` content read from disk using the JLD2 format.
45
46 # Arguments
47 * `filename::String`: path to file on disk containing the simulation
48 information.
49 * `verbose::Bool=true`: confirm to console that the file has been read.
50 """
51 function readSimulation(filename::String;
52 verbose::Bool=true)
53 simulation = createSimulation()
54 JLD2.@load(filename, simulation)
55 return simulation
56 end
57 """
58 readSimulation(simulation::Simulation;
59 step::Integer = -1,
60 verbose::Bool = true)
61
62 Read the simulation state from disk and return as new simulation object.
63
64 # Arguments
65 * `simulation::Simulation`: use the `simulation.id` to determine the fil…
66 to read from, and read information from the file into this object.
67 * `step::Integer=-1`: attempt to read this output file step. At its defa…
68 value (`-1`), the function will try to read the latest file, determi…
69 calling [`readSimulationStatus`](@ref).
70 * `verbose::Bool=true`: confirm to console that the file has been read.
71 """
72 function readSimulation(simulation::Simulation;
73 step::Integer = -1,
74 verbose::Bool = true)
75 if step == -1
76 step = readSimulationStatus(simulation)
77 end
78 filename = string(simulation.id, "/", simulation.id, ".$step.jld2")
79 if verbose
80 @info "Read simulation from $filename"
81 end
82 simulation = createSimulation()
83 JLD2.@load(filename, simulation)
84 return simulation
85 end
86
87 export writeSimulationStatus
88 """
89 writeSimulationStatus(simulation::Simulation;
90 folder::String=".",
91 verbose::Bool=false)
92
93 Write current simulation status to disk in a minimal txt file.
94 """
95 function writeSimulationStatus(simulation::Simulation;
96 folder::String=".",
97 verbose::Bool=false)
98 folder = folder * "/" * simulation.id
99 mkpath(folder)
100 filename = string(folder, "/", simulation.id, ".status.txt")
101
102 writedlm(filename, [simulation.time
103 simulation.time/simulation.time_total*100.
104 float(simulation.file_number)])
105 if verbose
106 @info "Wrote status to $filename"
107 end
108 nothing
109 end
110
111 export readSimulationStatus
112 """
113 readSimulationStatus(simulation_id[, folder, verbose])
114
115 Read the current simulation status from disk (`<sim.id>/<sim.id>.status.…
116 and return the last output file number.
117
118 # Arguments
119 * `simulation_id::String`: the simulation identifying string.
120 * `folder::String="."`: the folder in which to search for the status fil…
121 * `verbose::Bool=true`: show simulation status in console.
122 """
123 function readSimulationStatus(simulation_id::String;
124 folder::String=".",
125 verbose::Bool=true)
126
127 folder = folder * "/" * simulation_id
128 filename = string(folder, "/", simulation_id, ".status.txt")
129
130 data = readdlm(filename)
131 if verbose
132 @info "$simulation_id:\n" *
133 " time: $(data[1]) s\n" *
134 " complete: $(abs(data[2]))%\n" *
135 " last output file: $(Int(round(data[3])))\n"
136 end
137 return Int(round(data[3]))
138 """
139 readSimulationStatus(simulation[, folder, verbose])
140
141 Read the current simulation status from disk (`<sim.id>/<sim.id>.status.…
142 and return the last output file number.
143
144 # Arguments
145 * `simulation::Simulation`: the simulation to read the status for.
146 * `folder::String="."`: the folder in which to search for the status fil…
147 * `verbose::Bool=true`: show simulation status in console.
148 """
149 end
150 function readSimulationStatus(sim::Simulation;
151 folder::String=".",
152 verbose::Bool=true)
153 readSimulationStatus(sim.id, folder=folder, verbose=verbose)
154 end
155
156 export status
157 """
158 status(folder[, loop, t_int, colored_output, write_header, render)
159
160 Shows the status of all simulations with output files written under the
161 specified `folder`, which is the current working directory by default.
162
163 # Arguments
164 `folder::String="."`: directory (including subdirectories) to scan for
165 simulation output.
166 `loop::Bool=false`: continue printing the status every `t_int` seconds.
167 `t_int::Int=10`: interval between status updates when `loop=true`.
168 `colored_output::Bool=true`: display output with colors.
169 `write_header::Bool=true`: write header line explaining the data.
170 visualize::Bool=false`: render the simulation output. Does not work well…
171 `loop=true`, as the script regenerates (and overwrites) all output …
172 on every call.
173 """
174 function status(folder::String=".";
175 loop::Bool=false,
176 t_int::Int=10,
177 colored_output::Bool=true,
178 write_header::Bool=true,
179 visualize::Bool=false)
180
181 if colored_output
182 id_color_complete = :green
183 id_color_in_progress = :yellow
184 time_color = :white
185 percentage_color = :blue
186 lastfile_color = :cyan
187 else
188 id_color_complete = :default
189 id_color_in_progress = :default
190 time_color = :default
191 percentage_color = :default
192 lastfile_color = :default
193 end
194
195 repeat = true
196 while repeat
197
198 status_files = String[]
199 println()
200 println(Dates.format(Dates.DateTime(Dates.now()), Dates.RFC1123F…
201
202 for (root, dirs, files) in walkdir(folder, follow_symlinks=false)
203
204 for file in files
205 if occursin(".status.txt", file)
206 push!(status_files, joinpath(root, file))
207 end
208 end
209 end
210
211 if length(status_files) > 0
212
213 cols = displaysize(stdout)[2]
214 if write_header
215 for i=1:cols
216 print('-')
217 end
218 println()
219
220 left_label_width = 36
221 printstyled("simulation folder ", color=:default)
222 right_labels_width = 22
223 for i=18:cols-right_labels_width
224 print(' ')
225 end
226 printstyled("time ", color=time_color)
227 printstyled("complete ", color=percentage_color)
228 printstyled("files\n", color=lastfile_color)
229 for i=1:cols
230 print('-')
231 end
232 println()
233 end
234
235 for file in status_files
236 data = readdlm(file)
237 id = replace(file, ".status.txt" => "")
238 id = replace(id, "./" => "")
239 id = replace(id, r".*/" => "")
240 percentage = @sprintf "%9.0f%%" data[2]
241 lastfile = @sprintf "%7d" data[3]
242 if data[2] < 99.
243 printstyled("$id", color=id_color_in_progress)
244 else
245 printstyled("$id", color=id_color_complete)
246 end
247 right_fields_width = 25
248 for i=length(id):cols-right_fields_width
249 print(' ')
250 end
251 if data[1] < 60.0 # secs
252 time = @sprintf "%6.2fs" data[1]
253 elseif data[1] < 60.0*60.0 # mins
254 time = @sprintf "%6.2fm" data[1]/60.
255 elseif data[1] < 60.0*60.0*24.0 # hours
256 time = @sprintf "%6.2fh" data[1]/(60.0 * 60.0)
257 else # days
258 time = @sprintf "%6.2fd" data[1]/(60.0 * 60.0 * 24.0)
259 end
260 printstyled("$time", color=time_color)
261 printstyled("$percentage", color=percentage_color)
262 printstyled("$lastfile\n", color=lastfile_color)
263
264 if visualize
265 sim = createSimulation(id)
266 render(sim)
267 end
268 end
269 if write_header
270 for i=1:cols
271 print('-')
272 end
273 println()
274 end
275 else
276 @warn "no simulations found in $(pwd())/$folder"
277 end
278
279 if loop && t_int > 0
280 sleep(t_int)
281 end
282 if !loop
283 repeat = false
284 end
285 end
286 nothing
287 end
288
289 export writeVTK
290 """
291 Write a VTK file to disk containing all grains in the `simulation` in an
292 unstructured mesh (file type `.vtu`). These files can be read by ParaVi…
293 can be visualized by applying a *Glyph* filter.
294
295 If the simulation contains an `Ocean` data structure, it's contents will…
296 written to separate `.vtu` files. This can be disabled by setting the a…
297 `ocean=false`. The same is true for the atmosphere.
298
299 The VTK files will be saved in a subfolder named after the simulation.
300 """
301 function writeVTK(simulation::Simulation;
302 folder::String=".",
303 verbose::Bool=true,
304 ocean::Bool=true,
305 atmosphere::Bool=true)
306
307 simulation.file_number += 1
308 folder = folder * "/" * simulation.id
309 mkpath(folder)
310
311 filename = string(folder, "/", simulation.id, ".grains.",
312 simulation.file_number)
313 writeGrainVTK(simulation, filename, verbose=verbose)
314
315 filename = string(folder, "/", simulation.id, ".grain-interaction.",
316 simulation.file_number)
317 writeGrainInteractionVTK(simulation, filename, verbose=verbose)
318
319 if typeof(simulation.ocean.input_file) != Bool && ocean
320 filename = string(folder, "/", simulation.id, ".ocean.",
321 simulation.file_number)
322 writeGridVTK(simulation.ocean, filename, verbose=verbose)
323 end
324
325 if typeof(simulation.atmosphere.input_file) != Bool && atmosphere
326 filename = string(folder, "/", simulation.id, ".atmosphere.",
327 simulation.file_number)
328 writeGridVTK(simulation.atmosphere, filename, verbose=verbose)
329 end
330 nothing
331 end
332
333 export writeGrainVTK
334 """
335 Write a VTK file to disk containing all grains in the `simulation` in an
336 unstructured mesh (file type `.vtu`). These files can be read by ParaVi…
337 can be visualized by applying a *Glyph* filter. This function is called…
338 `writeVTK()`.
339 """
340 function writeGrainVTK(simulation::Simulation,
341 filename::String;
342 verbose::Bool=false)
343
344 ifarr = convertGrainDataToArrays(simulation)
345
346 # add arrays to VTK file
347 vtkfile = WriteVTK.vtk_grid("$filename.vtu", ifarr.lin_pos,
348 WriteVTK.MeshCell[])
349
350 WriteVTK.vtk_point_data(vtkfile, ifarr.density, "Density [kg m^-3]")
351
352 WriteVTK.vtk_point_data(vtkfile, ifarr.thickness, "Thickness [m]")
353 WriteVTK.vtk_point_data(vtkfile, ifarr.contact_radius*2.,
354 "Diameter (contact) [m]")
355 WriteVTK.vtk_point_data(vtkfile, ifarr.areal_radius*2.,
356 "Diameter (areal) [m]")
357 WriteVTK.vtk_point_data(vtkfile, ifarr.circumreference,
358 "Circumreference [m]")
359 WriteVTK.vtk_point_data(vtkfile, ifarr.horizontal_surface_area,
360 "Horizontal surface area [m^2]")
361 WriteVTK.vtk_point_data(vtkfile, ifarr.side_surface_area,
362 "Side surface area [m^2]")
363 WriteVTK.vtk_point_data(vtkfile, ifarr.volume, "Volume [m^3]")
364 WriteVTK.vtk_point_data(vtkfile, ifarr.mass, "Mass [kg]")
365 WriteVTK.vtk_point_data(vtkfile, ifarr.moment_of_inertia,
366 "Moment of inertia [kg m^2]")
367
368 WriteVTK.vtk_point_data(vtkfile, ifarr.lin_vel, "Linear velocity [m …
369 WriteVTK.vtk_point_data(vtkfile, ifarr.lin_acc,
370 "Linear acceleration [m s^-2]")
371 WriteVTK.vtk_point_data(vtkfile, ifarr.force, "Sum of forces [N]")
372 WriteVTK.vtk_point_data(vtkfile, ifarr.lin_disp, "Linear displacemen…
373
374 WriteVTK.vtk_point_data(vtkfile, ifarr.ang_pos, "Angular position [r…
375 WriteVTK.vtk_point_data(vtkfile, ifarr.ang_vel,
376 "Angular velocity [rad s^-1]")
377 WriteVTK.vtk_point_data(vtkfile, ifarr.ang_acc,
378 "Angular acceleration [rad s^-2]")
379 WriteVTK.vtk_point_data(vtkfile, ifarr.torque, "Sum of torques [N*m]…
380
381 WriteVTK.vtk_point_data(vtkfile, ifarr.fixed, "Fixed in space [-]")
382 WriteVTK.vtk_point_data(vtkfile, ifarr.allow_x_acc,
383 "Fixed but allow (x) acceleration [-]")
384 WriteVTK.vtk_point_data(vtkfile, ifarr.allow_y_acc,
385 "Fixed but allow (y) acceleration [-]")
386 WriteVTK.vtk_point_data(vtkfile, ifarr.allow_z_acc,
387 "Fixed but allow (z) acceleration [-]")
388 WriteVTK.vtk_point_data(vtkfile, ifarr.rotating, "Free to rotate [-]…
389 WriteVTK.vtk_point_data(vtkfile, ifarr.enabled, "Enabled [-]")
390
391 WriteVTK.vtk_point_data(vtkfile, ifarr.contact_stiffness_normal,
392 "Contact stiffness (normal) [N m^-1]")
393 WriteVTK.vtk_point_data(vtkfile, ifarr.contact_stiffness_tangential,
394 "Contact stiffness (tangential) [N m^-1]")
395 WriteVTK.vtk_point_data(vtkfile, ifarr.contact_viscosity_normal,
396 "Contact viscosity (normal) [N m^-1 s]")
397 WriteVTK.vtk_point_data(vtkfile, ifarr.contact_viscosity_tangential,
398 "Contact viscosity (tangential) [N m^-1 s]")
399 WriteVTK.vtk_point_data(vtkfile, ifarr.contact_static_friction,
400 "Contact friction (static) [-]")
401 WriteVTK.vtk_point_data(vtkfile, ifarr.contact_dynamic_friction,
402 "Contact friction (dynamic) [-]")
403
404 WriteVTK.vtk_point_data(vtkfile, ifarr.youngs_modulus,
405 "Young's modulus [Pa]")
406 WriteVTK.vtk_point_data(vtkfile, ifarr.poissons_ratio,
407 "Poisson's ratio [-]")
408 WriteVTK.vtk_point_data(vtkfile, ifarr.tensile_strength,
409 "Tensile strength [Pa]")
410 WriteVTK.vtk_point_data(vtkfile, ifarr.shear_strength,
411 "Shear strength [Pa]")
412 WriteVTK.vtk_point_data(vtkfile, ifarr.strength_heal_rate,
413 "Bond strength healing rate [Pa/s]")
414 WriteVTK.vtk_point_data(vtkfile, ifarr.fracture_toughness,
415 "Fracture toughness [m^0.5 Pa]")
416
417 WriteVTK.vtk_point_data(vtkfile, ifarr.ocean_drag_coeff_vert,
418 "Ocean drag coefficient (vertical) [-]")
419 WriteVTK.vtk_point_data(vtkfile, ifarr.ocean_drag_coeff_horiz,
420 "Ocean drag coefficient (horizontal) [-]")
421 WriteVTK.vtk_point_data(vtkfile, ifarr.atmosphere_drag_coeff_vert,
422 "Atmosphere drag coefficient (vertical) [-]")
423 WriteVTK.vtk_point_data(vtkfile, ifarr.atmosphere_drag_coeff_horiz,
424 "Atmosphere drag coefficient (horizontal) [-…
425
426 WriteVTK.vtk_point_data(vtkfile, ifarr.pressure,
427 "Contact pressure [Pa]")
428 WriteVTK.vtk_point_data(vtkfile, ifarr.n_contacts,
429 "Number of contacts [-]")
430
431 WriteVTK.vtk_point_data(vtkfile, ifarr.granular_stress,
432 "Granular stress [Pa]")
433 WriteVTK.vtk_point_data(vtkfile, ifarr.ocean_stress,
434 "Ocean stress [Pa]")
435 WriteVTK.vtk_point_data(vtkfile, ifarr.atmosphere_stress,
436 "Atmosphere stress [Pa]")
437
438 WriteVTK.vtk_point_data(vtkfile, ifarr.thermal_energy,
439 "Thermal energy [J]")
440
441 WriteVTK.vtk_point_data(vtkfile, ifarr.color,
442 "Color [-]")
443
444 deleteGrainArrays!(ifarr)
445 ifarr = 0
446
447 outfiles = WriteVTK.vtk_save(vtkfile)
448 if verbose
449 @info "Output file: $(outfiles[1])"
450 end
451 nothing
452 end
453
454 export writeGrainInteractionVTK
455 """
456 writeGrainInteractionVTK(simulation::Simulation,
457 filename::String;
458 verbose::Bool=false)
459
460 Saves grain interactions to `.vtp` files for visualization with VTK, for
461 example in Paraview. Convert Cell Data to Point Data and use with Tube …
462 """
463 function writeGrainInteractionVTK(simulation::Simulation,
464 filename::String;
465 verbose::Bool=false)
466
467 i1 = Int64[]
468 i2 = Int64[]
469 inter_particle_vector = Vector{Float64}[]
470 force = Float64[]
471 effective_radius = Float64[]
472 contact_area = Float64[]
473 contact_stiffness = Float64[]
474 tensile_stress = Float64[]
475 shear_displacement = Vector{Float64}[]
476 contact_rotation = Vector{Float64}[]
477 contact_age = Float64[]
478 contact_area = Float64[]
479 compressive_failure = Int[]
480
481 for i=1:length(simulation.grains)
482 for ic=1:simulation.Nc_max
483 if simulation.grains[i].contacts[ic] > 0
484 j = simulation.grains[i].contacts[ic]
485
486 if !simulation.grains[i].enabled ||
487 !simulation.grains[j].enabled
488 continue
489 end
490
491 p = simulation.grains[i].lin_pos -
492 simulation.grains[j].lin_pos
493 dist = norm(p)
494
495 r_i = simulation.grains[i].contact_radius
496 r_j = simulation.grains[j].contact_radius
497
498 # skip visualization of contacts across periodic BCs
499 if dist > 5.0*(r_i + r_j) &&
500 (simulation.ocean.bc_west == 2 ||
501 simulation.ocean.bc_east == 2 ||
502 simulation.ocean.bc_north == 2 ||
503 simulation.ocean.bc_south == 2 ||
504 simulation.atmosphere.bc_west == 2 ||
505 simulation.atmosphere.bc_east == 2 ||
506 simulation.atmosphere.bc_north == 2 ||
507 simulation.atmosphere.bc_south == 2)
508 continue
509 end
510 δ_n = dist - (r_i + r_j)
511 R_ij = harmonicMean(r_i, r_j)
512 A_ij = R_ij*min(simulation.grains[i].thickness,
513 simulation.grains[j].thickness)
514
515 if simulation.grains[i].youngs_modulus > 0. &&
516 simulation.grains[j].youngs_modulus > 0.
517 E_ij = harmonicMean(simulation.grains[i].
518 youngs_modulus,
519 simulation.grains[j].
520 youngs_modulus)
521 k_n = E_ij*A_ij/R_ij
522 else
523 k_n = harmonicMean(simulation.grains[i].
524 contact_stiffness_normal,
525 simulation.grains[j].
526 contact_stiffness_normal)
527 end
528
529
530 push!(i1, i)
531 push!(i2, j)
532 push!(inter_particle_vector, p)
533
534 push!(force, k_n*δ_n)
535 push!(effective_radius, R_ij)
536 push!(contact_area, A_ij)
537 push!(contact_stiffness, k_n)
538 push!(tensile_stress, k_n*δ_n/A_ij)
539
540 push!(shear_displacement,
541 simulation.grains[i]. contact_parallel_displacemen…
542 push!(contact_rotation,
543 simulation.grains[i].contact_rotation[ic])
544
545 push!(contact_age, simulation.grains[i].contact_age[ic])
546 push!(contact_area, simulation.grains[i].contact_area[ic…
547 push!(compressive_failure,
548 Int(simulation.grains[i].compressive_failure[ic]))
549 end
550 end
551 end
552
553 # Insert a piece for each grain interaction using grain positions as
554 # coordinates and connect them with lines by referencing their index…
555 open(filename * ".vtp", "w") do f
556 write(f, "<?xml version=\"1.0\"?>\n")
557 write(f, "<VTKFile type=\"PolyData\" version=\"0.1\" " *
558 "byte_order=\"LittleEndian\">\n")
559 write(f, " <PolyData>\n")
560 write(f, " <Piece " *
561 "NumberOfPoints=\"$(length(simulation.grains))\" " *
562 "NumberOfVerts=\"0\" " *
563 "NumberOfLines=\"$(length(i1))\" " *
564 "NumberOfStrips=\"0\" " *
565 "NumberOfPolys=\"0\">\n")
566 write(f, " <PointData>\n")
567 write(f, " </PointData>\n")
568 write(f, " <CellData>\n")
569
570 # Write values associated to each line
571 write(f, " <DataArray type=\"Float32\" " *
572 "Name=\"Inter-particle vector [m]\" " *
573 "NumberOfComponents=\"3\" format=\"ascii\">\n")
574 for i=1:length(i1)
575 write(f, "$(inter_particle_vector[i][1]) ")
576 write(f, "$(inter_particle_vector[i][2]) ")
577 write(f, "$(inter_particle_vector[i][3]) ")
578 end
579 write(f, "\n")
580 write(f, " </DataArray>\n")
581 write(f, " <DataArray type=\"Float32\" " *
582 "Name=\"Shear displacement [m]\" " *
583 "NumberOfComponents=\"3\" format=\"ascii\">\n")
584 for i=1:length(i1)
585 @inbounds write(f, "$(shear_displacement[i][1]) ")
586 @inbounds write(f, "$(shear_displacement[i][2]) ")
587 @inbounds write(f, "$(shear_displacement[i][3]) ")
588 end
589 write(f, "\n")
590 write(f, " </DataArray>\n")
591
592 write(f, " <DataArray type=\"Float32\" Name=\"Force [N]\"…
593 "NumberOfComponents=\"1\" format=\"ascii\">\n")
594 for i=1:length(i1)
595 @inbounds write(f, "$(force[i]) ")
596 end
597 write(f, "\n")
598 write(f, " </DataArray>\n")
599
600 write(f, " <DataArray type=\"Float32\" " *
601 "Name=\"Effective radius [m]\" " *
602 "NumberOfComponents=\"1\" format=\"ascii\">\n")
603 for i=1:length(i1)
604 @inbounds write(f, "$(effective_radius[i]) ")
605 end
606 write(f, "\n")
607 write(f, " </DataArray>\n")
608
609 write(f, " <DataArray type=\"Float32\" " *
610 "Name=\"Contact area [m^2]\" " *
611 "NumberOfComponents=\"1\" format=\"ascii\">\n")
612 for i=1:length(i1)
613 @inbounds write(f, "$(contact_area[i]) ")
614 end
615 write(f, "\n")
616 write(f, " </DataArray>\n")
617
618 write(f, " <DataArray type=\"Float32\" " *
619 "Name=\"Contact stiffness [N/m]\" " *
620 "NumberOfComponents=\"1\" format=\"ascii\">\n")
621 for i=1:length(i1)
622 @inbounds write(f, "$(contact_stiffness[i]) ")
623 end
624 write(f, "\n")
625 write(f, " </DataArray>\n")
626
627 write(f, " <DataArray type=\"Float32\" " *
628 "Name=\"Tensile stress [Pa]\" " *
629 "NumberOfComponents=\"1\" format=\"ascii\">\n")
630 for i=1:length(i1)
631 @inbounds write(f, "$(tensile_stress[i]) ")
632 end
633 write(f, "\n")
634 write(f, " </DataArray>\n")
635
636 write(f, " <DataArray type=\"Float32\" " *
637 "Name=\"Contact rotation [rad]\" NumberOfComponents=\"3\"
638 format=\"ascii\">\n")
639 for i=1:length(i1)
640 @inbounds write(f, "$(contact_rotation[i][1]) ")
641 @inbounds write(f, "$(contact_rotation[i][2]) ")
642 @inbounds write(f, "$(contact_rotation[i][3]) ")
643 end
644 write(f, "\n")
645 write(f, " </DataArray>\n")
646
647 write(f, " <DataArray type=\"Float32\" " *
648 "Name=\"Contact age [s]\" NumberOfComponents=\"1\"
649 format=\"ascii\">\n")
650 for i=1:length(i1)
651 @inbounds write(f, "$(contact_age[i]) ")
652 end
653 write(f, "\n")
654 write(f, " </DataArray>\n")
655
656 write(f, " <DataArray type=\"Float32\" " *
657 "Name=\"Contact area [m*m]\" NumberOfComponents=\"1\"
658 format=\"ascii\">\n")
659 for i=1:length(i1)
660 @inbounds write(f, "$(contact_area[i]) ")
661 end
662 write(f, "\n")
663 write(f, " </DataArray>\n")
664
665 write(f, " <DataArray type=\"Float32\" " *
666 "Name=\"Compressive failure [-]\" NumberOfComponents=\"1\"
667 format=\"ascii\">\n")
668 for i=1:length(i1)
669 @inbounds write(f, "$(Int(compressive_failure[i])) ")
670 end
671 write(f, "\n")
672 write(f, " </DataArray>\n")
673
674 write(f, " </CellData>\n")
675 write(f, " <Points>\n")
676
677 # Write line endpoints (grain centers)
678 #write(f, " <DataArray Name=\"Position [m]\" type=\"Float…
679 write(f, " <DataArray type=\"Float32\" Name=\"Points\" " *
680 "NumberOfComponents=\"3\" format=\"ascii\">\n")
681 for i in simulation.grains
682 @inbounds write(f, "$(i.lin_pos[1]) $(i.lin_pos[2]) $(i.lin_…
683 end
684 write(f, "\n")
685 write(f, " </DataArray>\n")
686 write(f, " </Points>\n")
687 write(f, " <Verts>\n")
688 write(f, " <DataArray type=\"Int64\" Name=\"connectivity\…
689 "format=\"ascii\">\n")
690 write(f, " </DataArray>\n")
691 write(f, " <DataArray type=\"Int64\" Name=\"offsets\" " *
692 "format=\"ascii\">\n")
693 write(f, " </DataArray>\n")
694 write(f, " </Verts>\n")
695 write(f, " <Lines>\n")
696
697 # Write contact connectivity by referring to point indexes
698 write(f, " <DataArray type=\"Int64\" Name=\"connectivity\…
699 "format=\"ascii\">\n")
700 for i=1:length(i1)
701 @inbounds write(f, "$(i1[i] - 1) $(i2[i] - 1) ")
702 end
703 write(f, "\n")
704 write(f, " </DataArray>\n")
705
706 # Write 0-indexed offset for the connectivity array for the end …
707 # cell
708 write(f, " <DataArray type=\"Int64\" Name=\"offsets\" " *
709 "format=\"ascii\">\n")
710 for i=1:length(i1)
711 @inbounds write(f, "$((i - 1)*2 + 2) ")
712 end
713 write(f, "\n")
714 write(f, " </DataArray>\n")
715
716 write(f, " </Lines>\n")
717 write(f, " <Strips>\n")
718 write(f, " <DataArray type=\"Int64\" Name=\"connectivity\…
719 "format=\"ascii\">\n")
720 write(f, " </DataArray>\n")
721 write(f, " <DataArray type=\"Int64\" Name=\"offsets\" " *
722 "format=\"ascii\">\n")
723 write(f, " </DataArray>\n")
724 write(f, " </Strips>\n")
725 write(f, " <Polys>\n")
726 write(f, " <DataArray type=\"Int64\" Name=\"connectivity\…
727 "format=\"ascii\">\n")
728 write(f, " </DataArray>\n")
729 write(f, " <DataArray type=\"Int64\" Name=\"offsets\" " *
730 "format=\"ascii\">\n")
731 write(f, " </DataArray>\n")
732 write(f, " </Polys>\n")
733 write(f, " </Piece>\n")
734 write(f, " </PolyData>\n")
735 write(f, "</VTKFile>\n")
736 end
737 nothing
738 end
739
740 export writeOceanVTK
741 """
742 Write a VTK file to disk containing all ocean data in the `simulation` i…
743 structured grid (file type `.vts`). These files can be read by ParaView…
744 be visualized by applying a *Glyph* filter. This function is called by
745 `writeVTK()`.
746 """
747 function writeGridVTK(grid::Any,
748 filename::String;
749 verbose::Bool=false)
750
751 # make each coordinate array three-dimensional
752 xq = similar(grid.u[:,:,:,1])
753 yq = similar(grid.u[:,:,:,1])
754 zq = similar(grid.u[:,:,:,1])
755
756 for iz=1:size(xq, 3)
757 @inbounds xq[:,:,iz] = grid.xq
758 @inbounds yq[:,:,iz] = grid.yq
759 end
760 for ix=1:size(xq, 1)
761 for iy=1:size(xq, 2)
762 @inbounds zq[ix,iy,:] = grid.zl
763 end
764 end
765
766 # add arrays to VTK file
767 vtkfile = WriteVTK.vtk_grid("$filename.vts", xq, yq, zq)
768
769 WriteVTK.vtk_point_data(vtkfile, grid.u[:, :, :, 1],
770 "u: Zonal velocity [m/s]")
771 WriteVTK.vtk_point_data(vtkfile, grid.v[:, :, :, 1],
772 "v: Meridional velocity [m/s]")
773 # write velocities as 3d vector
774 vel = zeros(3, size(xq, 1), size(xq, 2), size(xq, 3))
775 for ix=1:size(xq, 1)
776 for iy=1:size(xq, 2)
777 for iz=1:size(xq, 3)
778 @inbounds vel[1, ix, iy, iz] = grid.u[ix, iy, iz, 1]
779 @inbounds vel[2, ix, iy, iz] = grid.v[ix, iy, iz, 1]
780 end
781 end
782 end
783 WriteVTK.vtk_point_data(vtkfile, vel, "Velocity vector [m/s]")
784
785 # Porosity is in the grids stored on the cell center, but is here
786 # interpolated to the cell corners
787 porosity = zeros(size(xq, 1), size(xq, 2), size(xq, 3))
788 for ix=1:size(grid.xh, 1)
789 for iy=1:size(grid.xh, 2)
790 @inbounds porosity[ix, iy, 1] = grid.porosity[ix, iy]
791 if ix == size(grid.xh, 1)
792 @inbounds porosity[ix+1, iy, 1] = grid.porosity[ix, iy]
793 end
794 if iy == size(grid.xh, 2)
795 @inbounds porosity[ix, iy+1, 1] = grid.porosity[ix, iy]
796 end
797 if ix == size(grid.xh, 1) && iy == size(grid.xh, 2)
798 @inbounds porosity[ix+1, iy+1, 1] = grid.porosity[ix, iy]
799 end
800 end
801 end
802 WriteVTK.vtk_point_data(vtkfile, porosity, "Porosity [-]")
803
804 if typeof(grid) == Ocean
805 WriteVTK.vtk_point_data(vtkfile, grid.h[:, :, :, 1],
806 "h: Layer thickness [m]")
807 WriteVTK.vtk_point_data(vtkfile, grid.e[:, :, :, 1],
808 "e: Relative interface height [m]")
809 end
810
811 outfiles = WriteVTK.vtk_save(vtkfile)
812 if verbose
813 @info "Output file: $(outfiles[1])"
814 end
815 nothing
816 end
817
818 export writeParaviewPythonScript
819 """
820 function writeParaviewPythonScript(simulation,
821 [filename, folder, vtk_folder, verbos…
822
823 Create a `".py"` script for visualizing the simulation VTK files in Para…
824 The script can be run from the command line with `pvpython` (bundled with
825 Paraview), or from the interactive Python shell inside Paraview.
826
827 # Arguments
828 * `simulation::Simulation`: input simulation file containing the data.
829 * `filename::String`: output file name for the Python script. At its def…
830 (blank) value, the script is named after the simulation id (`simulat…
831 * `folder::String`: output directory, current directory the default.
832 * `vtk_folder::String`: directory containing the VTK output files, by de…
833 points to the full system path equivalent to `"./<simulation.id>/"`.
834 * `save_animation::Bool`: make the generated script immediately save a r…
835 animation to disk when the `".py"` script is called.
836 * `verbose::Bool`: show diagnostic information during
837 function call, on by
838 default.
839 """
840 function writeParaviewPythonScript(simulation::Simulation;
841 filename::String="",
842 folder::String=".",
843 vtk_folder::String="",
844 save_animation::Bool=true,
845 save_images::Bool=false,
846 width::Integer=1920,
847 height::Integer=1080,
848 framerate::Integer=10,
849 grains_color_scheme::String="X Ray",
850 verbose::Bool=true)
851 if filename == ""
852 folder = string(folder, "/", simulation.id)
853 mkpath(folder)
854 filename = string(folder, "/", simulation.id, ".py")
855 end
856 if vtk_folder == ""
857 vtk_folder = string(pwd(), "/", simulation.id)
858 end
859
860 if simulation.file_number == 0
861 simulation.file_number = readSimulationStatus(simulation,
862 verbose=verbose)
863 end
864
865 open(filename, "w") do f
866 write(f, """from paraview.simple import *
867 paraview.simple._DisableFirstRenderCameraReset()
868 FileName=[""")
869 for i=1:simulation.file_number
870 write(f, "'$(vtk_folder)/$(simulation.id).grains.$(i).vtu', …
871 end
872 write(f, """]
873 imagegrains = XMLUnstructuredGridReader(FileName=FileName)
874
875 imagegrains.PointArrayStatus = [
876 'Density [kg m^-3]',
877 'Thickness [m]',
878 'Diameter (contact) [m]',
879 'Diameter (areal) [m]',
880 'Circumreference [m]',
881 'Horizontal surface area [m^2]',
882 'Side surface area [m^2]',
883 'Volume [m^3]',
884 'Mass [kg]',
885 'Moment of inertia [kg m^2]',
886 'Linear velocity [m s^-1]',
887 'Linear acceleration [m s^-2]',
888 'Sum of forces [N]',
889 'Linear displacement [m]',
890 'Angular position [rad]',
891 'Angular velocity [rad s^-1]',
892 'Angular acceleration [rad s^-2]',
893 'Sum of torques [N*m]',
894 'Fixed in space [-]',
895 'Fixed but allow (x) acceleration [-]',
896 'Fixed but allow (y) acceleration [-]',
897 'Fixed but allow (z) acceleration [-]',
898 'Free to rotate [-]',
899 'Enabled [-]',
900 'Contact stiffness (normal) [N m^-1]',
901 'Contact stiffness (tangential) [N m^-1]',
902 'Contact viscosity (normal) [N m^-1 s]',
903 'Contact viscosity (tangential) [N m^-1 s]',
904 'Contact friction (static) [-]',
905 'Contact friction (dynamic) [-]',
906 "Young's modulus [Pa]",
907 "Poisson's ratio [-]",
908 'Tensile strength [Pa]'
909 'Shear strength [Pa]'
910 'Strength heal rate [Pa/s]'
911 'Compressive strength prefactor [m^0.5 Pa]',
912 'Ocean drag coefficient (vertical) [-]',
913 'Ocean drag coefficient (horizontal) [-]',
914 'Atmosphere drag coefficient (vertical) [-]',
915 'Atmosphere drag coefficient (horizontal) [-]',
916 'Contact pressure [Pa]',
917 'Number of contacts [-]',
918 'Granular stress [Pa]',
919 'Ocean stress [Pa]',
920 'Atmosphere stress [Pa]',
921 'Color [-]']
922
923 animationScene1 = GetAnimationScene()
924
925 # update animation scene based on data timesteps
926 animationScene1.UpdateAnimationUsingDataTimeSteps()
927
928 # get active view
929 renderView1 = GetActiveViewOrCreate('RenderView')
930 # uncomment following to set a specific view size
931 # renderView1.ViewSize = [2478, 1570]
932
933 # show data in view
934 imagegrainsDisplay = Show(imagegrains, renderView1)
935 # trace defaults for the display properties.
936 imagegrainsDisplay.Representation = 'Surface'
937 imagegrainsDisplay.AmbientColor = [0.0, 0.0, 0.0]
938 imagegrainsDisplay.ColorArrayName = [None, '']
939 imagegrainsDisplay.OSPRayScaleArray = 'Angular acceleration [rad s^-2]'
940 imagegrainsDisplay.OSPRayScaleFunction = 'PiecewiseFunction'
941 imagegrainsDisplay.SelectOrientationVectors = 'Angular acceleration [rad…
942 imagegrainsDisplay.ScaleFactor = 6.050000000000001
943 imagegrainsDisplay.SelectScaleArray = 'Angular acceleration [rad s^-2]'
944 imagegrainsDisplay.GlyphType = 'Arrow'
945 imagegrainsDisplay.GlyphTableIndexArray = 'Angular acceleration [rad s^-…
946 imagegrainsDisplay.DataAxesGrid = 'GridAxesRepresentation'
947 imagegrainsDisplay.PolarAxes = 'PolarAxesRepresentation'
948 imagegrainsDisplay.ScalarOpacityUnitDistance = 64.20669746996803
949 imagegrainsDisplay.GaussianRadius = 3.0250000000000004
950 imagegrainsDisplay.SetScaleArray = ['POINTS', 'Atmosphere drag coefficie…
951 imagegrainsDisplay.ScaleTransferFunction = 'PiecewiseFunction'
952 imagegrainsDisplay.OpacityArray = ['POINTS', 'Atmosphere drag coefficien…
953 imagegrainsDisplay.OpacityTransferFunction = 'PiecewiseFunction'
954
955 # init the 'GridAxesRepresentation' selected for 'DataAxesGrid'
956 imagegrainsDisplay.DataAxesGrid.XTitleColor = [0.0, 0.0, 0.0]
957 imagegrainsDisplay.DataAxesGrid.YTitleColor = [0.0, 0.0, 0.0]
958 imagegrainsDisplay.DataAxesGrid.ZTitleColor = [0.0, 0.0, 0.0]
959 imagegrainsDisplay.DataAxesGrid.GridColor = [0.0, 0.0, 0.0]
960 imagegrainsDisplay.DataAxesGrid.XLabelColor = [0.0, 0.0, 0.0]
961 imagegrainsDisplay.DataAxesGrid.YLabelColor = [0.0, 0.0, 0.0]
962 imagegrainsDisplay.DataAxesGrid.ZLabelColor = [0.0, 0.0, 0.0]
963
964 # init the 'PolarAxesRepresentation' selected for 'PolarAxes'
965 imagegrainsDisplay.PolarAxes.PolarAxisTitleColor = [0.0, 0.0, 0.0]
966 imagegrainsDisplay.PolarAxes.PolarAxisLabelColor = [0.0, 0.0, 0.0]
967 imagegrainsDisplay.PolarAxes.LastRadialAxisTextColor = [0.0, 0.0, 0.0]
968 imagegrainsDisplay.PolarAxes.SecondaryRadialAxesTextColor = [0.0, 0.0, 0…
969
970 # reset view to fit data
971 renderView1.ResetCamera()
972
973 #changing interaction mode based on data extents
974 renderView1.InteractionMode = '2D'
975
976 # update the view to ensure updated data information
977 renderView1.Update()
978
979 # create a new 'Glyph'
980 glyph1 = Glyph(Input=imagegrains,
981 GlyphType='Arrow')
982 glyph1.add_attribute = ['POINTS', 'Atmosphere drag coefficient (horizont…
983 glyph1.add_attribute = ['POINTS', 'Angular acceleration [rad s^-2]']
984 glyph1.ScaleFactor = 6.050000000000001
985 glyph1.GlyphTransform = 'Transform2'
986
987 # Properties modified on glyph1
988 glyph1.add_attribute = ['POINTS', 'Diameter (areal) [m]']
989 glyph1.add_attribute = ['POINTS', 'Angular position [rad]']
990 glyph1.ScaleFactor = 1.0
991 glyph1.GlyphMode = 'All Points'
992
993 # get color transfer function/color map for 'Diameterarealm'
994 diameterarealmLUT = GetColorTransferFunction('Diameterarealm')
995
996 # show data in view
997 glyph1Display = Show(glyph1, renderView1)
998 # trace defaults for the display properties.
999 glyph1Display.Representation = 'Surface'
1000 glyph1Display.AmbientColor = [0.0, 0.0, 0.0]
1001 glyph1Display.ColorArrayName = ['POINTS', 'Diameter (areal) [m]']
1002 glyph1Display.LookupTable = diameterarealmLUT
1003 glyph1Display.OSPRayScaleArray = 'Diameter (areal) [m]'
1004 glyph1Display.OSPRayScaleFunction = 'PiecewiseFunction'
1005 glyph1Display.SelectOrientationVectors = 'GlyphVector'
1006 glyph1Display.ScaleFactor = 6.1000000000000005
1007 glyph1Display.SelectScaleArray = 'Diameter (areal) [m]'
1008 glyph1Display.GlyphType = 'Arrow'
1009 glyph1Display.GlyphTableIndexArray = 'Diameter (areal) [m]'
1010 glyph1Display.DataAxesGrid = 'GridAxesRepresentation'
1011 glyph1Display.PolarAxes = 'PolarAxesRepresentation'
1012 glyph1Display.GaussianRadius = 3.0500000000000003
1013 glyph1Display.SetScaleArray = ['POINTS', 'Diameter (areal) [m]']
1014 glyph1Display.ScaleTransferFunction = 'PiecewiseFunction'
1015 glyph1Display.OpacityArray = ['POINTS', 'Diameter (areal) [m]']
1016 glyph1Display.OpacityTransferFunction = 'PiecewiseFunction'
1017
1018 # init the 'GridAxesRepresentation' selected for 'DataAxesGrid'
1019 glyph1Display.DataAxesGrid.XTitleColor = [0.0, 0.0, 0.0]
1020 glyph1Display.DataAxesGrid.YTitleColor = [0.0, 0.0, 0.0]
1021 glyph1Display.DataAxesGrid.ZTitleColor = [0.0, 0.0, 0.0]
1022 glyph1Display.DataAxesGrid.GridColor = [0.0, 0.0, 0.0]
1023 glyph1Display.DataAxesGrid.XLabelColor = [0.0, 0.0, 0.0]
1024 glyph1Display.DataAxesGrid.YLabelColor = [0.0, 0.0, 0.0]
1025 glyph1Display.DataAxesGrid.ZLabelColor = [0.0, 0.0, 0.0]
1026
1027 # init the 'PolarAxesRepresentation' selected for 'PolarAxes'
1028 glyph1Display.PolarAxes.PolarAxisTitleColor = [0.0, 0.0, 0.0]
1029 glyph1Display.PolarAxes.PolarAxisLabelColor = [0.0, 0.0, 0.0]
1030 glyph1Display.PolarAxes.LastRadialAxisTextColor = [0.0, 0.0, 0.0]
1031 glyph1Display.PolarAxes.SecondaryRadialAxesTextColor = [0.0, 0.0, 0.0]
1032
1033 # show color bar/color legend
1034 glyph1Display.SetScalarBarVisibility(renderView1, True)
1035
1036 # update the view to ensure updated data information
1037 renderView1.Update()
1038
1039 # reset view to fit data
1040 renderView1.ResetCamera()
1041
1042 # Properties modified on glyph1
1043 glyph1.GlyphType = 'Sphere'
1044
1045 # update the view to ensure updated data information
1046 renderView1.Update()
1047
1048 # hide color bar/color legend
1049 glyph1Display.SetScalarBarVisibility(renderView1, False)
1050
1051 # rescale color and/or opacity maps used to exactly fit the current data…
1052 glyph1Display.RescaleTransferFunctionToDataRange(False, True)
1053
1054 # get opacity transfer function/opacity map for 'Diameterarealm'
1055 diameterarealmPWF = GetOpacityTransferFunction('Diameterarealm')
1056
1057 # Apply a preset using its name. Note this may not work as expected when…
1058 diameterarealmLUT.ApplyPreset('$(grains_color_scheme)', True)
1059
1060 # Hide orientation axes
1061 renderView1.OrientationAxesVisibility = 0
1062
1063 # current camera placement for renderView1
1064 renderView1.InteractionMode = '2D'
1065 """)
1066 if save_animation
1067 write(f, """
1068 SaveAnimation('$(vtk_folder)/$(simulation.id).avi', renderView1,
1069 ImageResolution=[$(width), $(height)],
1070 FrameRate=$(framerate),
1071 FrameWindow=[0, $(simulation.file_number)])
1072 """)
1073 end
1074
1075 if save_images
1076 write(f, """
1077 SaveAnimation('$(folder)/$(simulation.id).png', renderView1,
1078 ImageResolution=[$(width), $(height)],
1079 FrameRate=$(framerate),
1080 FrameWindow=[0, $(simulation.file_number)])
1081 """)
1082 end
1083 end
1084 if verbose
1085 @info "$(filename) written, execute with " *
1086 "'pvpython $(vtk_folder)/$(simulation.id).py'"
1087 end
1088 end
1089
1090 export render
1091 """
1092 render(simulation[, pvpython, images, animation])
1093
1094 Wrapper function which calls `writeParaviewPythonScript(...)` and execut…
1095 from the shell using the supplied `pvpython` argument.
1096
1097 # Arguments
1098 * `simulation::Simulation`: simulation object containing the grain data.
1099 * `pvpython::String`: path to the `pvpython` executable to use. By defa…
1100 script uses the pvpython in the system PATH.
1101 * `images::Bool`: render images to disk (default: true)
1102 * `gif::Bool`: merge images as GIF and save to disk (default: false, req…
1103 `images=true`)
1104 * `animation::Bool`: render animation as movie to disk (default: false).…
1105 ffmpeg is available on the system, the `.avi` file is converted to `…
1106 * `trim::Bool`: trim images in animated sequence (default: true)
1107 * `reverse::Bool`: if `images=true` additionally render reverse-animated…
1108 (default: false)
1109 """
1110 function render(simulation::Simulation; pvpython::String="pvpython",
1111 images::Bool=true,
1112 gif::Bool=false,
1113 animation::Bool=false,
1114 trim::Bool=true,
1115 reverse::Bool=false)
1116
1117 writeParaviewPythonScript(simulation, save_animation=animation,
1118 save_images=images, verbose=false)
1119 try
1120 run(`$(pvpython) $(simulation.id)/$(simulation.id).py`)
1121
1122 if animation
1123 try
1124 run(`ffmpeg -i $(simulation.id)/$(simulation.id).avi
1125 -vf scale='trunc\(iw/2\)\*2:trunc\(ih/2\)\*2'
1126 -c:v libx264 -profile:v high -pix_fmt yuv420p
1127 -g 30 -r 30 -y
1128 $(simulation.id)/$(simulation.id).mp4`)
1129 if isfile("$(simulation.id)/$(simulation.id).mp4")
1130 rm("$(simulation.id)/$(simulation.id).avi")
1131 end
1132 catch return_signal
1133 if isa(return_signal, Base.IOError)
1134 @warn "Could not run external ffmpeg command, " *
1135 "skipping conversion from " *
1136 "$(simulation.id)/$(simulation.id).avi to mp4."
1137 end
1138 end
1139 end
1140
1141 # if available, use imagemagick to create gif from images
1142 if images && gif
1143 try
1144 trim_string = ""
1145 if trim
1146 trim_string = "-trim"
1147 end
1148
1149 # use ImageMagick installed with Homebrew.jl if availabl…
1150 # otherwise search for convert in $PATH
1151 convert = "convert"
1152
1153 run(`$convert $trim_string +repage -delay 10
1154 -transparent-color white
1155 -loop 0 $(simulation.id)/$(simulation.id).'*'.png
1156 $(simulation.id)/$(simulation.id).gif`)
1157 if reverse
1158 run(`$convert -trim +repage -delay 10
1159 -transparent-color white
1160 -loop 0 -reverse
1161 $(simulation.id)/$(simulation.id).'*'.png
1162 $(simulation.id)/$(simulation.id)-reverse.gif`)
1163 end
1164 catch return_signal
1165 if isa(return_signal, Base.IOError)
1166 @warn "Skipping gif merge since `$convert` " *
1167 "was not found."
1168 end
1169 end
1170 end
1171 catch return_signal
1172 if isa(return_signal, Base.IOError)
1173 error("`pvpython` was not found.")
1174 end
1175 end
1176 end
1177
1178 export removeSimulationFiles
1179 """
1180 removeSimulationFiles(simulation[, folder])
1181
1182 Remove all simulation output files from the specified folder.
1183 """
1184 function removeSimulationFiles(simulation::Simulation; folder::String=".…
1185 folder = folder * "/" * simulation.id
1186 run(`sh -c "rm -rf $(folder)/$(simulation.id).*.vtu"`)
1187 run(`sh -c "rm -rf $(folder)/$(simulation.id).*.vtp"`)
1188 run(`sh -c "rm -rf $(folder)/$(simulation.id).*.vts"`)
1189 run(`sh -c "rm -rf $(folder)/$(simulation.id).status.txt"`)
1190 run(`sh -c "rm -rf $(folder)/$(simulation.id).*.jld2"`)
1191 run(`sh -c "rm -rf $(folder)/$(simulation.id).py"`)
1192 run(`sh -c "rm -rf $(folder)/$(simulation.id).avi"`)
1193 run(`sh -c "rm -rf $(folder)/$(simulation.id).*.png"`)
1194 run(`sh -c "rm -rf $(folder)"`)
1195 nothing
1196 end
1197
1198 export plotGrainSizeDistribution
1199 """
1200 plotGrainSizeDistribution(simulation, [filename_postfix, nbins,
1201 size_type, filetype, gnuplot_terminal,
1202 skip_fixed, log_y, verbose)
1203
1204 Plot the grain size distribution as a histogram and save it to the disk.…
1205 plot is saved accoring to the simulation id, the optional `filename_post…
1206 string, and the `filetype`, and is written to the current folder.
1207
1208 # Arguments
1209 * `simulation::Simulation`: the simulation object containing the grains.
1210 * `filename_postfix::String`: optional string for the output filename.
1211 * `nbins::Int`: number of bins in the histogram (default = 12).
1212 * `size_type::String`: specify whether to use the `contact` or `areal` r…
1213 for the grain size. The default is `contact`.
1214 * `filetype::String`: the output file type (default = "png").
1215 * `gnuplot_terminal::String`: the gnuplot output terminal to use (defaul…
1216 "png").
1217 * `skip_fixed::Bool`: ommit grains that are fixed in space from the size
1218 distribution (default = true).
1219 * `log_y::Bool`: plot y-axis in log scale.
1220 * `verbose::String`: show output file as info message in stdout (default…
1221 true).
1222 """
1223 function plotGrainSizeDistribution(simulation::Simulation;
1224 filename_postfix::String = "",
1225 nbins::Int=12,
1226 size_type::String = "contact",
1227 filetype::String = "png",
1228 gnuplot_terminal::String = "png",
1229 skip_fixed::Bool = true,
1230 log_y::Bool = false,
1231 verbose::Bool = true)
1232
1233 diameters = Float64[]
1234 for i=1:length(simulation.grains)
1235 if simulation.grains[i].fixed && skip_fixed
1236 continue
1237 end
1238 if size_type == "contact"
1239 push!(diameters, simulation.grains[i].contact_radius*2.)
1240 elseif size_type == "areal"
1241 push!(diameters, simulation.grains[i].areal_radius*2.)
1242 else
1243 error("size_type '$size_type' not understood")
1244 end
1245 end
1246
1247 filename = string(simulation.id * filename_postfix *
1248 "-grain-size-distribution." * filetype)
1249
1250 # write data to temporary file on disk
1251 datafile = Base.Filesystem.tempname()
1252 writedlm(datafile, diameters)
1253 gnuplotscript = Base.Filesystem.tempname()
1254
1255 #if maximum(diameters) ≈ minimum(diameters)
1256 #@info "Overriding `nbins = $nbins` -> `nbins = 1`."
1257 #nbins = 1
1258 #end
1259
1260 open(gnuplotscript, "w") do f
1261
1262 write(f, """#!/usr/bin/env gnuplot
1263 set term $gnuplot_terminal
1264 set out "$(filename)"\n""")
1265 if log_y
1266 write(f, "set logscale y\n")
1267 end
1268 write(f, """set xlabel "Diameter [m]"
1269 set ylabel "Count [-]"
1270 binwidth = $((maximum(diameters) - minimum(diameters)+1e-7…
1271 binstart = $(minimum(diameters))
1272 set boxwidth 1.0*binwidth
1273 set style fill solid 0.5
1274 set key off
1275 hist = 'u (binwidth*(floor((\$1-binstart)/binwidth)+0.5)+b…
1276 plot "$(datafile)" i 0 @hist ls 1
1277 """)
1278 end
1279
1280 try
1281 run(`gnuplot $gnuplotscript`)
1282 catch return_signal
1283 if isa(return_signal, Base.IOError)
1284 error("Could not launch external gnuplot process")
1285 end
1286 end
1287
1288 if verbose
1289 @info filename
1290 end
1291 end
1292
1293 export plotGrains
1294 """
1295 plotGrains(simulation, [filetype, gnuplot_terminal, verbose])
1296
1297 Plot the grains using Gnuplot and save the figure to disk.
1298
1299 # Arguments
1300 * `simulation::Simulation`: the simulation object containing the grains.
1301 * `filetype::String`: the output file type (default = "png").
1302 * `gnuplot_terminal::String`: the gnuplot output terminal to use (defaul…
1303 "png crop size 1200,1200").
1304 * `plot_interactions::Bool`: show grain-grain interactions in the plot.
1305 * `verbose::String`: show output file as info message in stdout (default…
1306 true).
1307 """
1308 function plotGrains(sim::Simulation;
1309 filetype::String = "png",
1310 gnuplot_terminal::String = "png crop size 1200,1200",
1311 plot_interactions::Bool = true,
1312 palette_scalar::String = "contact_radius",
1313 cbrange::Vector{Float64} = [NaN, NaN],
1314 show_figure::Bool = true,
1315 verbose::Bool = true)
1316
1317 mkpath(sim.id)
1318 filename = string(sim.id, "/", sim.id, ".grains.", sim.file_number, …
1319 filetype)
1320
1321 # prepare grain data
1322 x = Float64[]
1323 y = Float64[]
1324 r = Float64[]
1325 scalars = Float64[]
1326 for grain in sim.grains
1327 push!(x, grain.lin_pos[1])
1328 push!(y, grain.lin_pos[2])
1329 push!(r, grain.contact_radius)
1330
1331 if palette_scalar == "contact_radius"
1332 push!(scalars, grain.contact_radius)
1333
1334 elseif palette_scalar == "areal_radius"
1335 push!(scalars, grain.areal_radius)
1336
1337 elseif palette_scalar == "color"
1338 push!(scalars, grain.color)
1339
1340 else
1341 error("palette_scalar = '$palette_scalar' not understood.")
1342 end
1343 end
1344
1345 # prepare interaction data
1346 if plot_interactions
1347 i1 = Int64[]
1348 i2 = Int64[]
1349 inter_particle_vector = Vector{Float64}[]
1350 force = Float64[]
1351 effective_radius = Float64[]
1352 contact_area = Float64[]
1353 contact_stiffness = Float64[]
1354 tensile_stress = Float64[]
1355 shear_displacement = Vector{Float64}[]
1356 contact_rotation = Vector{Float64}[]
1357 contact_age = Float64[]
1358 compressive_failure = Int[]
1359 for i=1:length(sim.grains)
1360 for ic=1:sim.Nc_max
1361 if sim.grains[i].contacts[ic] > 0
1362 j = sim.grains[i].contacts[ic]
1363
1364 if !sim.grains[i].enabled ||
1365 !sim.grains[j].enabled
1366 continue
1367 end
1368
1369 p = sim.grains[i].lin_pos -
1370 sim.grains[j].lin_pos
1371 dist = norm(p)
1372
1373 r_i = sim.grains[i].contact_radius
1374 r_j = sim.grains[j].contact_radius
1375 δ_n = dist - (r_i + r_j)
1376 R_ij = harmonicMean(r_i, r_j)
1377
1378 if sim.grains[i].youngs_modulus > 0. &&
1379 sim.grains[j].youngs_modulus > 0.
1380 E_ij = harmonicMean(sim.grains[i].
1381 youngs_modulus,
1382 sim.grains[j].
1383 youngs_modulus)
1384 A_ij = R_ij*min(sim.grains[i].thickness,
1385 sim.grains[j].thickness)
1386 k_n = E_ij*A_ij/R_ij
1387 else
1388 k_n = harmonicMean(sim.grains[i].
1389 contact_stiffness_normal,
1390 sim.grains[j].
1391 contact_stiffness_normal)
1392 end
1393
1394 push!(i1, i)
1395 push!(i2, j)
1396 push!(inter_particle_vector, p)
1397
1398 push!(force, k_n*δ_n)
1399 push!(effective_radius, R_ij)
1400 push!(contact_area, A_ij)
1401 push!(contact_stiffness, k_n)
1402 push!(tensile_stress, k_n*δ_n/A_ij)
1403
1404 push!(shear_displacement,
1405 sim.grains[i].contact_parallel_displacement[ic…
1406 push!(contact_rotation,
1407 sim.grains[i].contact_rotation[ic])
1408
1409 push!(contact_age, sim.grains[i].contact_age[ic])
1410 push!(compressive_failure,
1411 sim.grains[i].compressive_failure[ic])
1412 end
1413 end
1414 end
1415 end
1416
1417 # write grain data to temporary file on disk
1418 datafile = Base.Filesystem.tempname()
1419 writedlm(datafile, [x y r scalars])
1420 gnuplotscript = Base.Filesystem.tempname()
1421
1422 #=
1423 if plot_interactions
1424 # write grain data to temporary file on disk
1425 datafile_int = Base.Filesystem.tempname()
1426
1427 open(datafile_int, "w") do f
1428 for i=1:length(i1)
1429 write(f, "$(sim.grains[i1[i]].lin_pos[1]) ")
1430 write(f, "$(sim.grains[i1[i]].lin_pos[2]) ")
1431 write(f, "$(sim.grains[i2[i]].lin_pos[1]) ")
1432 write(f, "$(sim.grains[i2[i]].lin_pos[2]) ")
1433 write(f, "$(force[i]) ")
1434 write(f, "$(effective_radius[i]) ")
1435 write(f, "$(contact_area[i]) ")
1436 write(f, "$(contact_stiffness[i]) ")
1437 write(f, "$(tensile_stress[i]) ")
1438 write(f, "$(shear_displacement[i]) ")
1439 write(f, "$(contact_age[i]) ")
1440 write(f, "\n")
1441 end
1442 end
1443 end=#
1444
1445 open(gnuplotscript, "w") do f
1446
1447 write(f, """#!/usr/bin/env gnuplot
1448 set term $(gnuplot_terminal)
1449 set out "$(filename)"
1450 set xlabel "x [m]"
1451 set ylabel "y [m]"\n""")
1452 if typeof(sim.ocean.input_file) != Bool
1453 write(f, "set xrange ")
1454 write(f, "[$(sim.ocean.xq[1,1]):$(sim.ocean.xq[end,end])]\n")
1455 write(f, "set yrange ")
1456 write(f, "[$(sim.ocean.yq[1,1]):$(sim.ocean.yq[end,end])]\n")
1457 else
1458 write(f, "set xrange [$(minimum(x - r)):$(maximum(x + r))]\n…
1459 write(f, "set yrange [$(minimum(y - r)):$(maximum(y + r))]\n…
1460 end
1461
1462 # light gray to black to red
1463 #write(f, "set palette defined ( 1 '#d3d3d3', 2 '#000000')\n")
1464
1465 # light gray to black to red
1466 write(f, "set palette defined ( 1 '#d3d3d3', 2 '#000000', 3 '#99…
1467
1468 if !isnan(cbrange[1])
1469 write(f, "set cbrange [$(cbrange[1]):$(cbrange[2])]\n")
1470 end
1471
1472 # gray to white
1473 #write(f, "set palette defined (0 'gray', 1 'white')\n")
1474
1475 write(f, """set cblabel "$palette_scalar"
1476 set size ratio -1
1477 set key off\n""")
1478
1479 if length(i1) > 0
1480 max_tensile_stress = maximum(abs.(tensile_stress))
1481 max_line_width = 5.
1482 if plot_interactions
1483 write(f, "set cbrange [-$max_tensile_stress:$max_tensile…
1484 for i=1:length(i1)
1485 write(f, "set arrow $i from " *
1486 "$(sim.grains[i1[i]].lin_pos[1])," *
1487 "$(sim.grains[i1[i]].lin_pos[2]) to " *
1488 "$(sim.grains[i2[i]].lin_pos[1])," *
1489 "$(sim.grains[i2[i]].lin_pos[2]) ")
1490 if tensile_stress[i] > 0
1491 write(f, "nohead ")
1492 else
1493 write(f, "heads ")
1494 end
1495 write(f, "lw $(abs(tensile_stress[i])/
1496 max_tensile_stress*max_line_width) ")
1497 write(f, "lc palette cb $(tensile_stress[i])\n")
1498 end
1499 end
1500 end
1501
1502 #write(f,"""plot "$(datafile)" with circles lt 1 lc rgb "black" …
1503 write(f,"""plot "$(datafile)" with circles fill solid fillcolor …
1504 """)
1505 end
1506
1507 try
1508 run(`gnuplot $gnuplotscript`)
1509 catch return_signal
1510 if isa(return_signal, Base.IOError)
1511 error("Could not launch external gnuplot process")
1512 end
1513 end
1514
1515 if verbose
1516 @info filename
1517 end
1518
1519 if show_figure
1520 if Sys.isapple()
1521 run(`open $(filename)`)
1522 elseif Sys.islinux()
1523 run(`xdg-open $(filename)`)
1524 end
1525 end
1526 end
You are viewing proxied material from mx1.adamsgaard.dk. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.