| |
1 # |
| |
2 # This module implements logging of intermediate computational results for |
| |
3 # generating convergence graphs, etc. |
| |
4 # |
| |
5 |
| |
6 """ |
| |
7 Logging routines for intermediate computational results. |
| |
8 Includes `Log`, `log!`, `if_log!`, and `write_log`. |
| |
9 """ |
| |
10 module Logger |
| |
11 |
| |
12 import DelimitedFiles: writedlm |
| |
13 |
| |
14 ############## |
| |
15 # Our exports |
| |
16 ############## |
| |
17 |
| |
18 export Log, |
| |
19 log!, |
| |
20 if_log!, |
| |
21 write_log |
| |
22 |
| |
23 ########## |
| |
24 # Logging |
| |
25 ########## |
| |
26 |
| |
27 """ |
| |
28 `struct Log{T}` |
| |
29 |
| |
30 A log of items of type `T` along with log configuration. |
| |
31 The constructor takes no arguments; create the log with `Log{T}()` for `T` your |
| |
32 data type, e.g., `Float64`. |
| |
33 """ |
| |
34 struct Log{T} |
| |
35 log :: Dict{Int, T} |
| |
36 |
| |
37 Log{T}() where T = new{T}(Dict{Int, T}()) |
| |
38 end |
| |
39 |
| |
40 """ |
| |
41 `if_log!(f :: Functdion, log :: Log{T}, i :: Int)` |
| |
42 |
| |
43 If based on the log settings, `i` is a verbose iteration, store the value of `f()` |
| |
44 in the log at index `i`. Typically to be used with `do`-notation: |
| |
45 |
| |
46 ```julia |
| |
47 if_log!(log, iteration) do |
| |
48 # calculate value to be logged |
| |
49 end |
| |
50 ``` |
| |
51 """ |
| |
52 function if_log!(f :: Function, log :: Log{T}, i :: Int) where T |
| |
53 if mod(i, log.verbose_iterations)==0 |
| |
54 log!(f, log, i) |
| |
55 #printstyled("$(i): $(val)\n", color=:light_black) |
| |
56 end |
| |
57 end |
| |
58 |
| |
59 """ |
| |
60 `log!(f :: Function, log :: Log{T}, i :: Int) ` |
| |
61 |
| |
62 Store the value `f()` in the log at index `i`. |
| |
63 """ |
| |
64 function log!(f :: Function, log :: Log{T}, i :: Int) where T |
| |
65 val = f() |
| |
66 push!(log.log, i => val) |
| |
67 end |
| |
68 |
| |
69 ############## |
| |
70 # Data export |
| |
71 ############## |
| |
72 |
| |
73 """ |
| |
74 `write_log(filename :: AbstractString, log :: Log{T})` |
| |
75 |
| |
76 Write a `Log{T}` as a CSV file using `DelimitedFiles`. |
| |
77 If T is a structural type, the field names are used as header fields. |
| |
78 Otherwise a single `value` field is attempted to be written. |
| |
79 """ |
| |
80 function write_log(filename :: AbstractString, log :: Log{T}) where T |
| |
81 k = fieldnames(T) |
| |
82 @assert(:iter ∉ k) |
| |
83 |
| |
84 open(filename, "w") do io |
| |
85 # Write header |
| |
86 writedlm(io, isempty(k) ? [:iter :value] : ((:iter, k...),)) |
| |
87 # Write values |
| |
88 iters = sort(collect(keys(log.log))) |
| |
89 for i ∈ iters |
| |
90 v = log.log[i] |
| |
91 writedlm(io, isempty(k) ? [i v] : ((i, (getfield(v, j) for j ∈ k)...),)) |
| |
92 end |
| |
93 end |
| |
94 end |
| |
95 |
| |
96 end # module |