@muladd begin
summary_callback(u, t, integrator) = false
summary_callback(integrator) = u_modified!(integrator, false)
"""
SummaryCallback()
Create and return a callback that prints a human-readable summary of the simulation setup at the
beginning of a simulation and then resets the timer. At the end of the simulation the final timer
values are shown. When the returned callback is executed directly, the current timer values are shown.
"""
function SummaryCallback(reset_threads = true)
function initialize(cb, u, t, integrator)
return initialize_summary_callback(cb, u, t, integrator;
reset_threads)
end
return DiscreteCallback(summary_callback, summary_callback,
save_positions = (false, false),
initialize = initialize,
finalize = finalize_summary_callback)
end
function Base.show(io::IO, cb::DiscreteCallback{<:Any, <:typeof(summary_callback)})
@nospecialize cb
print(io, "SummaryCallback")
return nothing
end
function format_key_value_line(key::AbstractString, value::AbstractString, key_width,
total_width;
indentation_level = 0, guide = '…', filler = '…',
prefix = "│ ", suffix = " │")
@assert key_width < total_width
line = prefix
indentation = prefix^indentation_level
reduced_key_width = key_width - length(indentation)
squeezed_key = indentation * squeeze(key, reduced_key_width, filler = filler)
line *= squeezed_key
line *= ": "
short = key_width - length(squeezed_key)
if short <= 1
line *= " "
else
line *= guide^(short - 1) * " "
end
value_width = total_width - length(prefix) - length(suffix) - key_width - 2
squeezed_value = squeeze(value, value_width, filler = filler)
line *= squeezed_value
short = value_width - length(squeezed_value)
line *= " "^short
line *= suffix
@assert length(line)==total_width "should not happen: algorithm error!"
return line
end
function format_key_value_line(key, value, args...; kwargs...)
return format_key_value_line(string(key), string(value), args...; kwargs...)
end
function squeeze(message, max_width; filler::Char = '…')
@assert max_width>=3 "squeezing works only for a minimum `max_width` of 3"
length(message) <= max_width && return message
keep_front = div(max_width, 2)
keep_back = div(max_width, 2) - (isodd(max_width) ? 0 : 1)
remove_back = length(message) - keep_front
remove_front = length(message) - keep_back
squeezed = (chop(message, head = 0, tail = remove_back)
* filler *
chop(message, head = remove_front, tail = 0))
@assert length(squeezed)==max_width "`$(length(squeezed)) != $max_width` should not happen: algorithm error!"
return squeezed
end
function summary_box(io::IO, heading, setup = [])
summary_header(io, heading)
for (key, value) in setup
summary_line(io, key, value)
end
summary_footer(io)
return nothing
end
function summary_header(io, heading; total_width = 100, indentation_level = 0)
total_width = get(io, :total_width, total_width)
indentation_level = get(io, :indentation_level, indentation_level)
@assert indentation_level>=0 "indentation level may not be negative"
indentation_level > 0 && return
println(io, "┌" * "─"^(total_width - 2) * "┐")
println(io, "│ " * heading * " "^(total_width - length(heading) - 4) * " │")
println(io,
"│ " * "═"^length(heading) * " "^(total_width - length(heading) - 4) * " │")
return nothing
end
function summary_line(io, key, value; key_width = 35, total_width = 100,
indentation_level = 0)
@nospecialize value
key_width = get(io, :key_width, key_width)
total_width = get(io, :total_width, total_width)
indentation_level = get(io, :indentation_level, indentation_level)
s = format_key_value_line(key, value, key_width, total_width,
indentation_level = indentation_level)
println(io, s)
return nothing
end
function summary_footer(io; total_width = 100, indentation_level = 0)
total_width = get(io, :total_width, 100)
indentation_level = get(io, :indentation_level, 0)
if indentation_level == 0
s = "└" * "─"^(total_width - 2) * "┘"
else
s = ""
end
print(io, s)
return nothing
end
@inline function increment_indent(io)
return IOContext(io, :indentation_level => get(io, :indentation_level, 0) + 1)
end
function initialize_summary_callback(cb::DiscreteCallback, u, t, integrator;
reset_threads = true)
if reset_threads
Polyester.reset_threads!()
end
if !mpi_isroot()
reset_timer!(timer())
return nothing
end
print_startup_message()
io = stdout
io_context = IOContext(io,
:compact => false,
:key_width => 35,
:total_width => 100,
:indentation_level => 0)
semi = integrator.p
print_summary_semidiscretization(io_context, semi)
callbacks = integrator.opts.callback
if callbacks isa CallbackSet
foreach(callbacks.continuous_callbacks) do cb
show(io_context, MIME"text/plain"(), cb)
println(io, "\n")
return nothing
end
foreach(callbacks.discrete_callbacks) do cb
cb.affect! === summary_callback && return nothing
show(io_context, MIME"text/plain"(), cb)
println(io, "\n")
return nothing
end
else
show(io_context, MIME"text/plain"(), callbacks)
println(io, "\n")
end
setup = Pair{String, Any}["Start time" => first(integrator.sol.prob.tspan),
"Final time" => last(integrator.sol.prob.tspan),
"time integrator" => integrator.alg |> typeof |> nameof,
"adaptive" => integrator.opts.adaptive]
if integrator.opts.adaptive
push!(setup,
"abstol" => integrator.opts.abstol,
"reltol" => integrator.opts.reltol,
"controller" => integrator.opts.controller)
end
summary_box(io, "Time integration", setup)
println()
setup = Pair{String, Any}["#threads" => Threads.nthreads()]
push!(setup, "threading backend" => string(_PREFERENCE_THREADING))
if !_PREFERENCE_LOOPVECTORIZATION
push!(setup, "LoopVectorization" => "disabled")
end
if mpi_isparallel()
push!(setup,
"#MPI ranks" => mpi_nranks())
end
summary_box(io, "Environment information", setup)
println()
reset_timer!(timer())
return nothing
end
finalize_summary_callback(cb, u, t, integrator) = cb()
function print_summary_semidiscretization(io::IO, semi::AbstractSemidiscretization)
show(io, MIME"text/plain"(), semi)
println(io, "\n")
mesh, equations, solver, _ = mesh_equations_solver_cache(semi)
show(io, MIME"text/plain"(), mesh)
println(io, "\n")
show(io, MIME"text/plain"(), equations)
println(io, "\n")
show(io, MIME"text/plain"(), solver)
println(io, "\n")
return nothing
end
function (cb::DiscreteCallback{Condition, Affect!})(io::IO = stdout) where {Condition,
Affect! <:
typeof(summary_callback)
}
mpi_isroot() || return nothing
TimerOutputs.complement!(timer())
print_timer(io, timer(), title = "Trixi.jl",
allocations = true, linechars = :unicode, compact = false)
println(io)
return nothing
end
end