Path: blob/main/src/callbacks_step/time_series.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"""8TimeSeriesCallback(semi, point_coordinates;9interval=1, solution_variables=cons2cons,10output_directory="out", filename="time_series.h5",11RealT=real(solver), uEltype=eltype(cache.elements))1213Create a callback that records point-wise data at points given in `point_coordinates` every14`interval` time steps. The point coordinates are to be specified either as a vector of coordinate15tuples or as a two-dimensional array where the first dimension is the point number and the second16dimension is the coordinate dimension. By default, the conservative variables are recorded, but this17can be controlled by passing a different conversion function to `solution_variables`.1819After the last time step, the results are stored in an HDF5 file `filename` in directory20`output_directory`.2122The real data type `RealT` and data type for solution variables `uEltype` default to the respective23types used in the solver and the cache.2425Currently this callback is only implemented for [`TreeMesh`](@ref) and [`UnstructuredMesh2D`](@ref).26"""27mutable struct TimeSeriesCallback{RealT <: Real, uEltype <: Real, SolutionVariables,28VariableNames, Cache}29interval::Int30solution_variables::SolutionVariables31variable_names::VariableNames32output_directory::String33filename::String34point_coordinates::Array{RealT, 2}35# Point data is stored as a vector of vectors of the solution data type:36# * the "outer" `Vector` contains one vector for each point at which a time_series is recorded37# * the "inner" `Vector` contains the actual time series for a single point,38# with each record adding "n_vars" entries39# The reason for using this data structure is that the length of the inner vectors needs to be40# increased for each record, which can only be realized in Julia using ordinary `Vector`s.41point_data::Vector{Vector{uEltype}}42time::Vector{RealT}43step::Vector{Int}44time_series_cache::Cache45end4647function Base.show(io::IO, cb::DiscreteCallback{<:Any, <:TimeSeriesCallback})48@nospecialize cb # reduce precompilation time4950time_series_callback = cb.affect!51@unpack interval, solution_variables, output_directory, filename = time_series_callback52print(io, "TimeSeriesCallback(",53"interval=", interval, ", ",54"solution_variables=", interval, ", ",55"output_directory=", "\"output_directory\"", ", ",56"filename=", "\"filename\"",57")")58end5960function Base.show(io::IO, ::MIME"text/plain",61cb::DiscreteCallback{<:Any, <:TimeSeriesCallback})62@nospecialize cb # reduce precompilation time6364if get(io, :compact, false)65show(io, cb)66else67time_series_callback = cb.affect!6869setup = [70"#points" => size(time_series_callback.point_coordinates, 2),71"interval" => time_series_callback.interval,72"solution_variables" => time_series_callback.solution_variables,73"output_directory" => time_series_callback.output_directory,74"filename" => time_series_callback.filename75]76summary_box(io, "TimeSeriesCallback", setup)77end78end7980# Main constructor81function TimeSeriesCallback(mesh, equations, solver, cache, point_coordinates;82interval::Integer = 1,83solution_variables = cons2cons,84output_directory = "out",85filename = "time_series.h5",86RealT = real(solver),87uEltype = eltype(cache.elements))88# check arguments89if !(interval isa Integer && interval >= 0)90throw(ArgumentError("`interval` must be a non-negative integer (provided `interval = $interval`)"))91end9293if ndims(point_coordinates) != 2 || size(point_coordinates, 2) != ndims(mesh)94throw(ArgumentError("`point_coordinates` must be a matrix of size n_points × ndims"))95end9697# create the output folder if it does not exist already98if mpi_isroot() && !isdir(output_directory)99mkpath(output_directory)100end101102# Transpose point_coordinates to our usual format [ndims, n_points]103# Note: They are accepted in a different format to allow direct input from `readdlm`104point_coordinates_ = permutedims(point_coordinates)105106# Invoke callback every `interval` time steps or after final step (for storing the data on disk)107if interval > 0108# With error-based step size control, some steps can be rejected. Thus,109# `integrator.iter >= integrator.stats.naccept`110# (total #steps) (#accepted steps)111# We need to check the number of accepted steps since callbacks are not112# activated after a rejected step.113condition = (u, t, integrator) -> ((integrator.stats.naccept % interval == 0 &&114!(integrator.stats.naccept == 0 &&115integrator.iter > 0)) ||116isfinished(integrator))117else # disable the callback for interval == 0118condition = (u, t, integrator) -> false119end120121# Create data structures that are to be filled by the callback122variable_names = varnames(solution_variables, equations)123n_points = size(point_coordinates_, 2)124point_data = Vector{uEltype}[Vector{uEltype}() for _ in 1:n_points]125time = Vector{RealT}()126step = Vector{Int}()127time_series_cache = create_cache_time_series(point_coordinates_, mesh, solver,128cache)129130time_series_callback = TimeSeriesCallback(interval,131solution_variables,132variable_names,133output_directory,134filename,135point_coordinates_,136point_data,137time,138step,139time_series_cache)140141return DiscreteCallback(condition, time_series_callback,142save_positions = (false, false))143end144145# Convenience constructor that unpacks the semidiscretization into mesh, equations, solver, cache146function TimeSeriesCallback(semi, point_coordinates; kwargs...)147mesh, equations, solver, cache = mesh_equations_solver_cache(semi)148149return TimeSeriesCallback(mesh, equations, solver, cache, point_coordinates;150kwargs...)151end152153# Convenience constructor that converts a vector of points into a Trixi.jl-style coordinate array154function TimeSeriesCallback(mesh, equations, solver, cache,155point_coordinates::AbstractVector;156kwargs...)157# Coordinates are usually stored in [ndims, n_points], but here as [n_points, ndims]158n_points = length(point_coordinates)159point_coordinates_ = Matrix{eltype(eltype(point_coordinates))}(undef, n_points,160ndims(mesh))161162for p in 1:n_points163for d in 1:ndims(mesh)164point_coordinates_[p, d] = point_coordinates[p][d]165end166end167168return TimeSeriesCallback(mesh, equations, solver, cache, point_coordinates_;169kwargs...)170end171172# This method is called as callback during the time integration.173function (time_series_callback::TimeSeriesCallback)(integrator)174# Ensure this is not accidentally used with AMR enabled175if uses_amr(integrator.opts.callback)176error("the TimeSeriesCallback does not work with AMR enabled")177end178179@unpack interval = time_series_callback180181# Create record if in correct interval (needs to be checked since the callback is also called182# after the final step for storing the data on disk, independent of the current interval)183if integrator.stats.naccept % interval == 0184@trixi_timeit timer() "time series" begin185# Store time and step186push!(time_series_callback.time, integrator.t)187push!(time_series_callback.step, integrator.stats.naccept)188189# Unpack data190u_ode = integrator.u191semi = integrator.p192mesh, equations, solver, cache = mesh_equations_solver_cache(semi)193u = wrap_array(u_ode, mesh, equations, solver, cache)194195@unpack (point_data, solution_variables,196variable_names, time_series_cache) = time_series_callback197198# Record state at points (solver/mesh-dependent implementation)199record_state_at_points!(point_data, u, solution_variables,200length(variable_names), mesh,201equations, solver, time_series_cache)202end203end204205# Store time_series if this is the last time step206if isfinished(integrator)207semi = integrator.p208mesh, equations, solver, _ = mesh_equations_solver_cache(semi)209save_time_series_file(time_series_callback, mesh, equations, solver)210end211212# avoid re-evaluating possible FSAL stages213u_modified!(integrator, false)214215return nothing216end217218include("time_series_dg.jl")219include("time_series_dg_tree.jl")220include("time_series_dg_unstructured.jl")221end # @muladd222223224