Path: blob/main/src/callbacks_step/save_restart.jl
2055 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67"""8SaveRestartCallback(; interval=0,9save_final_restart=true,10output_directory="out")1112Save the current numerical solution in a restart file every `interval` time steps.13"""14mutable struct SaveRestartCallback15interval::Int16save_final_restart::Bool17output_directory::String18end1920function Base.show(io::IO, cb::DiscreteCallback{<:Any, <:SaveRestartCallback})21@nospecialize cb # reduce precompilation time2223restart_callback = cb.affect!24print(io, "SaveRestartCallback(interval=", restart_callback.interval, ")")25end2627function Base.show(io::IO, ::MIME"text/plain",28cb::DiscreteCallback{<:Any, <:SaveRestartCallback})29@nospecialize cb # reduce precompilation time3031if get(io, :compact, false)32show(io, cb)33else34save_restart_callback = cb.affect!3536setup = [37"interval" => save_restart_callback.interval,38"save final solution" => save_restart_callback.save_final_restart ? "yes" :39"no",40"output directory" => abspath(normpath(save_restart_callback.output_directory))41]42summary_box(io, "SaveRestartCallback", setup)43end44end4546function SaveRestartCallback(; interval = 0,47save_final_restart = true,48output_directory = "out")49restart_callback = SaveRestartCallback(interval, save_final_restart,50output_directory)5152DiscreteCallback(restart_callback, restart_callback, # the first one is the condition, the second the affect!53save_positions = (false, false),54initialize = initialize!)55end5657function initialize!(cb::DiscreteCallback{Condition, Affect!}, u, t,58integrator) where {Condition, Affect! <: SaveRestartCallback}59restart_callback = cb.affect!6061mpi_isroot() && mkpath(restart_callback.output_directory)6263semi = integrator.p6465@trixi_timeit timer() "I/O" begin66save_mesh(semi, restart_callback.output_directory)67end6869return nothing70end7172# this method is called to determine whether the callback should be activated73function (restart_callback::SaveRestartCallback)(u, t, integrator)74@unpack interval, save_final_restart = restart_callback7576# With error-based step size control, some steps can be rejected. Thus,77# `integrator.iter >= integrator.stats.naccept`78# (total #steps) (#accepted steps)79# We need to check the number of accepted steps since callbacks are not80# activated after a rejected step.81return interval > 0 && (integrator.stats.naccept % interval == 0 ||82(save_final_restart && isfinished(integrator)))83end8485# this method is called when the callback is activated86function (restart_callback::SaveRestartCallback)(integrator)87u_ode = integrator.u88@unpack t, dt = integrator89iter = integrator.stats.naccept90semi = integrator.p9192@trixi_timeit timer() "I/O" begin93save_mesh(semi, restart_callback.output_directory, iter)94save_restart_file(u_ode, t, dt, iter, semi, restart_callback)95# If using an adaptive time stepping scheme, store controller values for restart96if integrator.opts.adaptive97save_adaptive_time_integrator(integrator, integrator.opts.controller,98restart_callback)99end100end101102# avoid re-evaluating possible FSAL stages103u_modified!(integrator, false)104return nothing105end106107@inline function save_restart_file(u_ode, t, dt, iter,108semi::AbstractSemidiscretization, restart_callback)109mesh, equations, solver, cache = mesh_equations_solver_cache(semi)110u = wrap_array_native(u_ode, mesh, equations, solver, cache)111save_restart_file(u, t, dt, iter, mesh, equations, solver, cache, restart_callback)112end113114"""115load_time(restart_file::AbstractString)116117Load the time saved in a `restart_file`.118"""119function load_time(restart_file::AbstractString)120h5open(restart_file, "r") do file121read(attributes(file)["time"])122end123end124125"""126load_timestep(restart_file::AbstractString)127128Load the time step number (`iter` in OrdinaryDiffEq.jl) saved in a `restart_file`.129"""130function load_timestep(restart_file::AbstractString)131h5open(restart_file, "r") do file132read(attributes(file)["timestep"])133end134end135136"""137load_timestep!(integrator, restart_file::AbstractString)138139Load the time step number saved in a `restart_file` and assign it to both the time step140number and and the number of accepted steps141(`iter` and `stats.naccept` in OrdinaryDiffEq.jl, respectively) in `integrator`.142"""143function load_timestep!(integrator, restart_file::AbstractString)144integrator.iter = load_timestep(restart_file)145integrator.stats.naccept = integrator.iter146end147148"""149load_dt(restart_file::AbstractString)150151Load the time step size (`dt` in OrdinaryDiffEq.jl) saved in a `restart_file`.152"""153function load_dt(restart_file::AbstractString)154h5open(restart_file, "r") do file155read(attributes(file)["dt"])156end157end158159function load_restart_file(semi::AbstractSemidiscretization, restart_file)160load_restart_file(mesh_equations_solver_cache(semi)..., restart_file)161end162163"""164load_adaptive_time_integrator!(integrator, restart_file::AbstractString)165166Load the context information for time integrators with error-based step size control167saved in a `restart_file`.168"""169function load_adaptive_time_integrator!(integrator, restart_file::AbstractString)170controller = integrator.opts.controller171# Read context information for controller172h5open(restart_file, "r") do file173# Ensure that the necessary information was saved174if !("time_integrator_qold" in keys(attributes(file))) ||175!("time_integrator_dtpropose" in keys(attributes(file))) ||176(hasproperty(controller, :err) &&177!("time_integrator_controller_err" in keys(attributes(file))))178error("Missing data in restart file: check the consistency of adaptive time controller with initial setup!")179end180# Load data that is required both for PIController and PIDController181integrator.qold = read(attributes(file)["time_integrator_qold"])182integrator.dtpropose = read(attributes(file)["time_integrator_dtpropose"])183# Accept step to use dtpropose already in the first step184integrator.accept_step = true185# Reevaluate integrator.fsal_first on the first step186integrator.reeval_fsal = true187# Load additional parameters for PIDController188if hasproperty(controller, :err) # Distinguish PIDController from PIController189controller.err[:] = read(attributes(file)["time_integrator_controller_err"])190end191end192end193194include("save_restart_dg.jl")195end # @muladd196197198