diff --git a/NEWS.md b/NEWS.md index a39a322efc0..b87a369b042 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,7 +17,13 @@ for human readability. In v0.13, `flux_lax_friedrichs = FluxLaxFriedrichs(max_abs_speed = max_abs_speed)` instead of the previous default `FluxLaxFriedrichs(max_abs_speed = max_abs_speed_naive)` ([#2458]). +- The signature of the `VisualizationCallback` constructor changed. + In the new version, it is mandatory to pass the semidiscretization `semi` to + determine the default plotting type (1D for 1D simulations, 2D for 2D and 3D simulations). + This can further be customized via the keyword argument `plot_data_creator`, which had + the default value `plot_data_creator = PlotData2D` before the change ([#2468]). +#### Removed - Deprecations introduced in earlier versions of Trixi.jl have been removed. diff --git a/examples/tree_1d_dgsem/elixir_advection_extended.jl b/examples/tree_1d_dgsem/elixir_advection_extended.jl index 576882a19a7..8df04c72334 100644 --- a/examples/tree_1d_dgsem/elixir_advection_extended.jl +++ b/examples/tree_1d_dgsem/elixir_advection_extended.jl @@ -1,5 +1,6 @@ using OrdinaryDiffEqLowStorageRK using Trixi +using Plots # For visualization callback ############################################################################### # semidiscretization of the linear advection equation @@ -64,10 +65,14 @@ save_solution = SaveSolutionCallback(interval = 100, # The StepsizeCallback handles the re-calculation of the maximum Δt after each time step stepsize_callback = StepsizeCallback(cfl = 1.6) +# Enable in-situ visualization with a new plot generated at every time step +visualization = VisualizationCallback(semi; interval = 1) + # Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, - save_restart, save_solution, + save_restart, + save_solution, visualization, stepsize_callback) ############################################################################### diff --git a/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl b/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl index 04c9818cebb..f6a44153b95 100644 --- a/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl +++ b/examples/tree_2d_dgsem/elixir_advection_amr_visualization.jl @@ -49,7 +49,7 @@ save_solution = SaveSolutionCallback(interval = 100, # Enable in-situ visualization with a new plot generated every 20 time steps # and additional plotting options passed as keyword arguments -visualization = VisualizationCallback(interval = 20, clims = (0, 1)) +visualization = VisualizationCallback(semi; interval = 20, clims = (0, 1)) amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable = first), base_level = 3, diff --git a/src/callbacks_step/visualization.jl b/src/callbacks_step/visualization.jl index 302e7e4462a..367b30bd573 100644 --- a/src/callbacks_step/visualization.jl +++ b/src/callbacks_step/visualization.jl @@ -5,13 +5,13 @@ @muladd begin #! format: noindent -mutable struct VisualizationCallback{SolutionVariables, VariableNames, PlotDataCreator, +mutable struct VisualizationCallback{PlotDataCreator, SolutionVariables, VariableNames, PlotCreator} + plot_data_creator::PlotDataCreator interval::Int solution_variables::SolutionVariables variable_names::VariableNames show_mesh::Bool - plot_data_creator::PlotDataCreator plot_creator::PlotCreator plot_arguments::Dict{Symbol, Any} end @@ -22,13 +22,13 @@ function Base.show(io::IO, VisualizationCallback } visualization_callback = cb.affect! - @unpack interval, plot_arguments, solution_variables, variable_names, show_mesh, plot_creator, plot_data_creator = visualization_callback + @unpack plot_data_creator, interval, plot_arguments, solution_variables, variable_names, show_mesh, plot_creator = visualization_callback print(io, "VisualizationCallback(", + "plot_data_creator=", plot_data_creator, ", ", "interval=", interval, ", ", "solution_variables=", solution_variables, ", ", "variable_names=", variable_names, ", ", "show_mesh=", show_mesh, ", ", - "plot_data_creator=", plot_data_creator, ", ", "plot_creator=", plot_creator, ", ", "plot_arguments=", plot_arguments, ")") end @@ -44,32 +44,32 @@ function Base.show(io::IO, ::MIME"text/plain", visualization_callback = cb.affect! setup = [ + "plot data creator" => visualization_callback.plot_data_creator, "interval" => visualization_callback.interval, "plot arguments" => visualization_callback.plot_arguments, "solution variables" => visualization_callback.solution_variables, "variable names" => visualization_callback.variable_names, "show mesh" => visualization_callback.show_mesh, - "plot creator" => visualization_callback.plot_creator, - "plot data creator" => visualization_callback.plot_data_creator + "plot creator" => visualization_callback.plot_creator ] summary_box(io, "VisualizationCallback", setup) end end """ - VisualizationCallback(; interval=0, - solution_variables=cons2prim, - variable_names=[], - show_mesh=false, - plot_data_creator=PlotData2D, - plot_creator=show_plot, - plot_arguments...) + VisualizationCallback(semi, plot_data_creator = nothing; + interval=0, + solution_variables=cons2prim, + variable_names=[], + show_mesh=false, + plot_creator=show_plot, + plot_arguments...) Create a callback that visualizes results during a simulation, also known as *in-situ visualization*. -!!! warning "Experimental implementation" - This is an experimental feature and may change in any future releases. +To customize the generated figure, `plot_data_creator` allows to use different plot data types. +Currently provided are [`PlotData1D`](@ref) and [`PlotData2D`](@ref), while the latter is used for both 2D and 3D. The `interval` specifies the number of time step iterations after which a new plot is generated. The available variables to plot are configured with the `solution_variables` parameter, which acts the @@ -77,16 +77,15 @@ same way as for the [`SaveSolutionCallback`](@ref). The variables to be actually selected by providing a single string or a list of strings to `variable_names`, and if `show_mesh` is `true`, an additional plot with the mesh will be generated. -To customize the generated figure, `plot_data_creator` allows to use different plot data types. With -`plot_creator` you can further specify an own function to visualize results, which must support the +With `plot_creator` you can further specify an own function to visualize results, which must support the same interface as the default implementation [`show_plot`](@ref). All remaining keyword arguments are collected and passed as additional arguments to the plotting command. """ -function VisualizationCallback(; interval = 0, +function VisualizationCallback(semi, plot_data_creator = nothing; + interval = 0, solution_variables = cons2prim, variable_names = [], show_mesh = false, - plot_data_creator = PlotData2D, plot_creator = show_plot, plot_arguments...) mpi_isparallel() && error("this callback does not work in parallel yet") @@ -95,10 +94,19 @@ function VisualizationCallback(; interval = 0, variable_names = String[variable_names] end - visualization_callback = VisualizationCallback(interval, + if plot_data_creator === nothing # No custom plot data type provided + if ndims(semi) == 1 + plot_data_creator = PlotData1D + else # 2D or 3D + plot_data_creator = PlotData2D + end + end + + visualization_callback = VisualizationCallback(plot_data_creator, + interval, solution_variables, variable_names, show_mesh, - plot_data_creator, plot_creator, + plot_creator, Dict{Symbol, Any}(plot_arguments)) # Warn users if they create a visualization callback without having loaded the Plots package @@ -145,7 +153,7 @@ end function (visualization_callback::VisualizationCallback)(integrator) u_ode = integrator.u semi = integrator.p - @unpack plot_arguments, solution_variables, variable_names, show_mesh, plot_data_creator, plot_creator = visualization_callback + @unpack plot_data_creator, plot_arguments, solution_variables, variable_names, show_mesh, plot_creator = visualization_callback # Extract plot data plot_data = plot_data_creator(u_ode, semi, solution_variables = solution_variables) @@ -177,9 +185,6 @@ variables in `variable_names` and, optionally, the mesh (if `show_mesh` is `true This function is the default `plot_creator` argument for the [`VisualizationCallback`](@ref). `time` and `timestep` are currently unused by this function. -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. - See also: [`VisualizationCallback`](@ref), [`save_plot`](@ref) """ function show_plot(plot_data, variable_names; @@ -229,9 +234,6 @@ is `true`). Additionally, `plot_arguments` will be unpacked and passed as keywo The `timestep` is used in the filename. `time` is currently unused by this function. -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. - See also: [`VisualizationCallback`](@ref), [`show_plot`](@ref) """ function save_plot(plot_data, variable_names; diff --git a/src/visualization/recipes_plots.jl b/src/visualization/recipes_plots.jl index 81b6a38fffd..14e985c6360 100644 --- a/src/visualization/recipes_plots.jl +++ b/src/visualization/recipes_plots.jl @@ -6,8 +6,6 @@ #! format: noindent # Visualize a single variable in a 2D plot (default: heatmap) -# -# Note: This is an experimental feature and may be changed in future releases without notice. RecipesBase.@recipe function f(pds::PlotDataSeries{<:AbstractPlotData{2}}) @unpack plot_data, variable_id = pds @unpack x, y, data, variable_names, orientation_x, orientation_y = plot_data @@ -32,8 +30,6 @@ RecipesBase.@recipe function f(pds::PlotDataSeries{<:AbstractPlotData{2}}) end # Visualize the mesh in a 2D plot -# -# Note: This is an experimental feature and may be changed in future releases without notice. RecipesBase.@recipe function f(pm::PlotMesh{<:AbstractPlotData{2}}) @unpack plot_data = pm @unpack x, y, mesh_vertices_x, mesh_vertices_y = plot_data @@ -55,8 +51,6 @@ RecipesBase.@recipe function f(pm::PlotMesh{<:AbstractPlotData{2}}) end # Visualize the mesh in a 2D plot -# -# Note: This is an experimental feature and may be changed in future releases without notice. RecipesBase.@recipe function f(pm::PlotMesh{<:PlotData2DCartesian{<:Any, <:AbstractVector{<:AbstractVector}}}) @unpack plot_data = pm @@ -79,8 +73,6 @@ RecipesBase.@recipe function f(pm::PlotMesh{<:PlotData2DCartesian{<:Any, end # Plot all available variables at once for convenience -# -# Note: This is an experimental feature and may be changed in future releases without notice. RecipesBase.@recipe function f(pd::AbstractPlotData) # Create layout that is as square as possible, when there are more than 3 subplots. # This is done with a preference for more columns than rows if not. @@ -152,8 +144,6 @@ end # Create a plot directly from a TrixiODESolution for convenience # The plot is created by a PlotData1D or PlotData2D object. -# -# Note: This is an experimental feature and may be changed in future releases without notice. RecipesBase.@recipe function f(sol::TrixiODESolution) # Redirect everything to the recipes below return sol.u[end], sol.prob.p diff --git a/src/visualization/types.jl b/src/visualization/types.jl index 97e67c4940d..792a8b5b89d 100644 --- a/src/visualization/types.jl +++ b/src/visualization/types.jl @@ -3,7 +3,6 @@ # This is a union of a Trixi.jl-specific SciMLBase.ODESolution and of Trixi.jl's own # TimeIntegratorSolution. # -# Note: This is an experimental feature and may be changed in future releases without notice. #! format: off const TrixiODESolution = Union{ODESolution{T, N, uType, uType2, DType, tType, rateType, discType, P} where {T, N, uType, uType2, DType, tType, rateType, discType, P<:ODEProblem{uType_, tType_, isinplace, P_, F_} where @@ -42,9 +41,6 @@ end Base.getindex(pd::AbstractPlotData, variable_name) Extract a single variable `variable_name` from `pd` for plotting with `Plots.plot`. - -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. """ function Base.getindex(pd::AbstractPlotData, variable_name) variable_id = findfirst(isequal(variable_name), pd.variable_names) @@ -63,9 +59,6 @@ Base.eltype(pd::AbstractPlotData) = Pair{String, PlotDataSeries{typeof(pd)}} Holds all relevant data for creating 2D plots of multiple solution variables and to visualize the mesh. - -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. """ struct PlotData2DCartesian{Coordinates, Data, VariableNames, Vertices} <: AbstractPlotData{2} @@ -123,9 +116,6 @@ end Holds all relevant data for creating 1D plots of multiple solution variables and to visualize the mesh. - -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. """ struct PlotData1D{Coordinates, Data, VariableNames, Vertices} <: AbstractPlotData{1} x::Coordinates @@ -146,8 +136,6 @@ function Base.show(io::IO, pd::PlotData1D) end # Auxiliary data structure for visualizing a single variable -# -# Note: This is an experimental feature and may be changed in future releases without notice. struct PlotDataSeries{PD <: AbstractPlotData} plot_data::PD variable_id::Int @@ -177,9 +165,6 @@ end getmesh(pd::AbstractPlotData) Extract grid lines from `pd` for plotting with `Plots.plot`. - -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. """ getmesh(pd::AbstractPlotData) = PlotMesh(pd) @@ -207,9 +192,6 @@ When visualizing data from a three-dimensional simulation, a 2D slice is extract The slice position is specified by a `point` that lies on it, which defaults to `(0.0, 0.0, 0.0)`. Both of these values are ignored when visualizing 2D data. -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. - # Examples ```julia julia> using Trixi, Plots @@ -294,9 +276,6 @@ end Create a `PlotData2D` object from a solution object created by either `OrdinaryDiffEq.solve!` (which returns a `SciMLBase.ODESolution`) or Trixi.jl's own `solve!` (which returns a `TimeIntegratorSolution`). - -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. """ function PlotData2D(sol::TrixiODESolution; kwargs...) PlotData2D(sol.u[end], sol.prob.p; kwargs...) @@ -548,9 +527,6 @@ This applies analogously to three-dimensional simulations, where `slice` may be Another way to visualize 2D/3D data is by creating a plot along a given curve. This is done with the keyword argument `curve`. It can be set to a list of 2D/3D points which define the curve. When using `curve` any other input from `slice` or `point` will be ignored. - -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. """ function PlotData1D(u_ode, semi; kwargs...) PlotData1D(wrap_array_native(u_ode, semi), @@ -761,9 +737,6 @@ end Create a `PlotData1D` object from a solution object created by either `OrdinaryDiffEq.solve!` (which returns a `SciMLBase.ODESolution`) or Trixi.jl's own `solve!` (which returns a `TimeIntegratorSolution`). - -!!! warning "Experimental implementation" - This is an experimental feature and may change in future releases. """ function PlotData1D(sol::TrixiODESolution; kwargs...) PlotData1D(sol.u[end], sol.prob.p; kwargs...) diff --git a/test/test_tree_1d.jl b/test/test_tree_1d.jl index 448aeb8d605..07380d50196 100644 --- a/test/test_tree_1d.jl +++ b/test/test_tree_1d.jl @@ -64,7 +64,8 @@ end l2=[0.00017373554109980247], linf=[0.0006021275678165239], maxiters=1, - initial_condition=Trixi.initial_condition_sin) + initial_condition=Trixi.initial_condition_sin, + visualization=TrivialCallback()) end @trixi_testset "elixir_advection_extended.jl with initial_condition_constant" begin @@ -72,7 +73,8 @@ end l2=[2.441369287653687e-16], linf=[4.440892098500626e-16], maxiters=1, - initial_condition=initial_condition_constant) + initial_condition=initial_condition_constant, + visualization=TrivialCallback()) end @trixi_testset "elixir_advection_extended.jl with initial_condition_linear_x" begin @@ -82,7 +84,8 @@ end maxiters=1, initial_condition=Trixi.initial_condition_linear_x, boundary_conditions=Trixi.boundary_condition_linear_x, - periodicity=false) + periodicity=false, + visualization=TrivialCallback()) end @trixi_testset "elixir_advection_extended.jl with initial_condition_convergence_test" begin @@ -92,7 +95,8 @@ end maxiters=1, initial_condition=initial_condition_convergence_test, boundary_conditions=BoundaryConditionDirichlet(initial_condition_convergence_test), - periodicity=false) + periodicity=false, + visualization=TrivialCallback()) end end @@ -181,6 +185,7 @@ end redirect_stderr(f) do trixi_include(joinpath(EXAMPLES_DIR, "elixir_advection_extended.jl"), + visualization = TrivialCallback(), summary_callback = TrivialCallback(), analysis_callback = TrivialCallback(), alive_callback = TrivialCallback()) diff --git a/test/test_visualization.jl b/test/test_visualization.jl index c924d0e41f8..14ea164b093 100644 --- a/test/test_visualization.jl +++ b/test/test_visualization.jl @@ -717,7 +717,8 @@ end @test_nowarn_mod trixi_include(@__MODULE__, joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_amr_visualization.jl"), - visualization = VisualizationCallback(interval = 20, + visualization = VisualizationCallback(semi; + interval = 20, clims = (0, 1), plot_creator = Trixi.save_plot), tspan = (0.0, 3.0))