src/Logger.jl

Wed, 22 Dec 2021 11:14:38 +0200

author
Tuomo Valkonen <tuomov@iki.fi>
date
Wed, 22 Dec 2021 11:14:38 +0200
changeset 35
d881275c6564
parent 34
22a64e826ee7
permissions
-rw-r--r--

Add metaprogramming tools and fast multidimensional loops.

#
# This module implements logging of intermediate computational results for
# generating convergence graphs, etc.
#

"""
Logging routines for intermediate computational results.
Includes `Log`, `log!`, `if_log!`, and `write_log`.
"""
module Logger

import DelimitedFiles: writedlm

##############
# Our exports
##############

export Log,
       log!,
       if_log!,
       write_log

##########
# Logging
##########

"""
`struct Log{T}`

A log of items of type `T` along with log configuration.
The constructor takes no arguments;  create the log with `Log{T}()` for `T` your
data type, e.g., `Float64`.
"""
struct Log{T}
    log :: Dict{Int, T}

    Log{T}() where T = new{T}(Dict{Int, T}())
end

"""
`if_log!(f :: Functdion, log :: Log{T}, i :: Int)`

If based on the log settings, `i` is a verbose iteration, store the value of `f()`
in the log at index `i`. Typically to be used with `do`-notation:

```julia
if_log!(log, iteration) do
    # calculate value to be logged
end
```
"""
function if_log!(f :: Function, log :: Log{T}, i :: Int) where T
    if mod(i, log.verbose_iterations)==0
        log!(f, log, i)
        #printstyled("$(i): $(val)\n", color=:light_black)
    end
end

"""
`log!(f :: Function, log :: Log{T}, i :: Int) `

Store the value `f()` in the log at index `i`.
"""
function log!(f :: Function, log :: Log{T}, i :: Int) where T
    val = f()
    push!(log.log, i => val)
end

##############
# Data export
##############

"""
`write_log(filename :: AbstractString, log :: Log{T})`

Write a `Log{T}` as a CSV file using `DelimitedFiles`.
If T is a structural type, the field names are used as header fields.
Otherwise a single `value` field is attempted to be written.
"""
function write_log(filename :: AbstractString, log :: Log{T}) where T
    k = fieldnames(T)
    @assert(:iter ∉ k)
    
    open(filename, "w") do io
        # Write header
        writedlm(io, isempty(k) ? [:iter :value] : ((:iter, k...),))
        # Write values
        iters = sort(collect(keys(log.log)))
        for i ∈ iters
            v = log.log[i]
            writedlm(io, isempty(k) ? [i v] : ((i, (getfield(v, j) for j ∈ k)...),))
        end
    end
end

end # module

mercurial