Path: blob/main/src/visualization/recipes_plots.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# Visualize a single variable in a 2D plot (default: heatmap)8RecipesBase.@recipe function f(pds::PlotDataSeries{<:AbstractPlotData{2}})9@unpack plot_data, variable_id = pds10@unpack x, y, data, variable_names, orientation_x, orientation_y = plot_data1112# Set geometric properties13xlims --> (x[begin], x[end])14ylims --> (y[begin], y[end])15aspect_ratio --> :equal1617# Set annotation properties18legend --> :none19title --> variable_names[variable_id]20colorbar --> :true21xguide --> _get_guide(orientation_x)22yguide --> _get_guide(orientation_y)2324# Set series properties25seriestype --> :heatmap2627# Return data for plotting28x, y, data[variable_id]29end3031# Visualize the mesh in a 2D plot32RecipesBase.@recipe function f(pm::PlotMesh{<:AbstractPlotData{2}})33@unpack plot_data = pm34@unpack x, y, mesh_vertices_x, mesh_vertices_y = plot_data3536# Set geometric and annotation properties37xlims --> (x[begin], x[end])38ylims --> (y[begin], y[end])39aspect_ratio --> :equal40legend --> :none41grid --> false4243# Set series properties44seriestype --> :path45linecolor --> :grey46linewidth --> 14748# Return data for plotting49mesh_vertices_x, mesh_vertices_y50end5152# Visualize the mesh in a 2D plot53RecipesBase.@recipe function f(pm::PlotMesh{<:PlotData2DCartesian{<:Any,54<:AbstractVector{<:AbstractVector}}})55@unpack plot_data = pm56@unpack x, y, mesh_vertices_x, mesh_vertices_y = plot_data5758# Set geometric and annotation properties59xlims --> (minimum(x), maximum(x))60ylims --> (minimum(y), maximum(y))61aspect_ratio --> :equal62legend --> :none63grid --> false6465# Set series properties66seriestype --> :path67linecolor --> :grey68linewidth --> 16970# Return data for plotting71mesh_vertices_x, mesh_vertices_y72end7374# Plot all available variables at once for convenience75RecipesBase.@recipe function f(pd::AbstractPlotData)76# Create layout that is as square as possible, when there are more than 3 subplots.77# This is done with a preference for more columns than rows if not.7879if length(pd) <= 380cols = length(pd)81rows = 182else83cols = ceil(Int, sqrt(length(pd)))84rows = ceil(Int, length(pd) / cols)85end8687layout := (rows, cols)8889# Plot all existing variables90for (i, (variable_name, series)) in enumerate(pd)91RecipesBase.@series begin92subplot := i93series94end95end9697# Fill remaining subplots with empty plot98for i in (length(pd) + 1):(rows * cols)99RecipesBase.@series begin100subplot := i101axis := false102ticks := false103legend := false104[], []105end106end107end108109# Plot a single variable.110RecipesBase.@recipe function f(pds::PlotDataSeries{<:AbstractPlotData{1}})111@unpack plot_data, variable_id = pds112@unpack x, data, variable_names, orientation_x = plot_data113114# Set geometric properties115xlims --> (x[begin], x[end])116117# Set annotation properties118legend --> :none119title --> variable_names[variable_id]120xguide --> _get_guide(orientation_x)121122# Return data for plotting123x, data[:, variable_id]124end125126# Plot the mesh as vertical lines from a PlotMesh object.127RecipesBase.@recipe function f(pm::PlotMesh{<:AbstractPlotData{1}})128@unpack plot_data = pm129@unpack x, mesh_vertices_x = plot_data130131# Set geometric and annotation properties132xlims --> (x[begin], x[end])133legend --> :none134135# Set series properties136seriestype --> :vline137linecolor --> :grey138linewidth --> 1139140# Return data for plotting141mesh_vertices_x142end143144# Create a plot directly from a TrixiODESolution for convenience145# The plot is created by a PlotData1D or PlotData2D object.146RecipesBase.@recipe function f(sol::TrixiODESolution)147# Redirect everything to the recipes below148return sol.u[end], sol.prob.p149end150151# Recipe for general semidiscretizations152# Note: If you change the defaults values here, you need to also change them in the PlotData1D or PlotData2D153# constructor.154RecipesBase.@recipe function f(u, semi::AbstractSemidiscretization;155solution_variables = nothing)156if ndims(semi) == 1157return PlotData1D(u, semi; solution_variables = solution_variables)158else159return PlotData2D(u, semi; solution_variables = solution_variables)160end161end162163# Also allow plotting a function with signature `func(x, equations)`, e.g., for initial conditions.164# We need this recipe in addition to the one above to avoid method ambiguities.165RecipesBase.@recipe function f(func::Function, semi::AbstractSemidiscretization;166solution_variables = nothing)167n_variables = length(func(SVector(0.0), semi.equations))168variable_names = SVector(["func[$i]" for i in 1:n_variables]...)169if ndims(semi) == 1170return PlotData1D(func, semi; solution_variables = cons2cons, variable_names)171else172throw(ArgumentError("Plotting of functions is only supported in 1D."))173end174end175176# Recipe specifically for TreeMesh-type solutions177# Note: If you change the defaults values here, you need to also change them in the PlotData1D or PlotData2D178# constructor.179RecipesBase.@recipe function f(u, semi::SemidiscretizationHyperbolic{<:TreeMesh};180solution_variables = nothing,181grid_lines = true, max_supported_level = 11,182nvisnodes = nothing,183reinterpolate = default_reinterpolate(semi.solver),184slice = :xy, point = (0.0, 0.0, 0.0), curve = nothing)185# Create a PlotData1D or PlotData2D object depending on the dimension.186if ndims(semi) == 1187return PlotData1D(u, semi; solution_variables, nvisnodes, reinterpolate,188slice, point, curve)189else190return PlotData2D(u, semi;191solution_variables, grid_lines, max_supported_level,192nvisnodes, slice, point)193end194end195196# Also allow plotting a function with signature `func(x, equations)`, e.g., for initial conditions.197RecipesBase.@recipe function f(func::Function,198semi::SemidiscretizationHyperbolic{<:TreeMesh};199solution_variables = nothing,200nvisnodes = nothing, slice = :xy,201point = (0.0, 0.0, 0.0), curve = nothing)202n_variables = length(func(SVector(0.0), semi.equations))203variable_names = SVector(["func[$i]" for i in 1:n_variables]...)204if ndims(semi) == 1205return PlotData1D(func, semi; solution_variables = cons2cons, nvisnodes, slice,206point, curve, variable_names)207else208throw(ArgumentError("Plotting of functions is only supported in 1D."))209end210end211212# Series recipe for PlotData2DTriangulated213RecipesBase.@recipe function f(pds::PlotDataSeries{<:PlotData2DTriangulated})214pd = pds.plot_data215@unpack variable_id = pds216@unpack x, y, data, t, variable_names = pd217218# extract specific solution field to plot219data_field = zeros(eltype(first(data)), size(data))220for (i, data_i) in enumerate(data)221data_field[i] = data_i[variable_id]222end223224legend --> false225aspect_ratio --> 1226title --> pd.variable_names[variable_id]227xlims --> extrema(x)228ylims --> extrema(y)229xguide --> _get_guide(1)230yguide --> _get_guide(2)231seriestype --> :heatmap232colorbar --> :true233234return DGTriPseudocolor(global_plotting_triangulation_triplot((x, y), data_field,235t)...)236end237238# Visualize a 2D mesh given an `PlotData2DTriangulated` object239RecipesBase.@recipe function f(pm::PlotMesh{<:PlotData2DTriangulated})240pd = pm.plot_data241@unpack x_face, y_face = pd242243# This line separates solution lines on each edge by NaNs to ensure that they are rendered244# separately. The coordinates `xf`, `yf` and the solution `sol_f`` are assumed to be a matrix245# whose columns correspond to different elements. We add NaN separators by appending a row of246# NaNs to this matrix. We also flatten (e.g., apply `vec` to) the result, as this speeds up247# plotting.248x_face, y_face = map(x -> vec(vcat(x, fill(NaN, 1, size(x, 2)))), (x_face, y_face))249250xlims --> extrema(x_face)251ylims --> extrema(y_face)252aspect_ratio --> :equal253legend --> :none254255# Set series properties256seriestype --> :path257linecolor --> :grey258linewidth --> 1259260return x_face, y_face261end262263# Visualizes a single scalar field. Intended for use with ScalarPlotData2D.264# Example usage: `plot(ScalarPlotData2D(u, semi))`.265RecipesBase.@recipe function f(pd::PlotData2DTriangulated{<:ScalarData})266@unpack x, y, data, t, variable_names = pd267268title_string = isnothing(variable_names) ? "" : variable_names269270legend --> false271aspect_ratio --> 1272title --> title_string273xlims --> extrema(x)274ylims --> extrema(y)275xguide --> _get_guide(1)276yguide --> _get_guide(2)277seriestype --> :heatmap278colorbar --> :true279280# Since `data` is simply a ScalarData wrapper around the actual plot data, we pass in281# `data.data` instead.282return DGTriPseudocolor(global_plotting_triangulation_triplot((x, y), data.data,283t)...)284end285286RecipesBase.@recipe function f(cb::DiscreteCallback{<:Any, <:TimeSeriesCallback},287point_id::Integer)288return cb.affect!, point_id289end290291RecipesBase.@recipe function f(time_series_callback::TimeSeriesCallback,292point_id::Integer)293return PlotData1D(time_series_callback, point_id)294end295end # @muladd296297298