diff --git a/Project.toml b/Project.toml index bfca055831..7b640325df 100644 --- a/Project.toml +++ b/Project.toml @@ -55,11 +55,13 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [weakdeps] Convex = "f65535da-76fb-5f13-bab9-19810c17039a" ECOS = "e2685f51-7e38-5353-a97d-a921fd2c8199" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" [extensions] TrixiConvexECOSExt = ["Convex", "ECOS"] +TrixiGLMakieExt = "GLMakie" TrixiMakieExt = "Makie" TrixiNLsolveExt = "NLsolve" @@ -77,6 +79,7 @@ ECOS = "1.1.2" EllipsisNotation = "1.0" FillArrays = "0.13.2, 1" ForwardDiff = "0.10.24" +GLMakie = "0.9" HDF5 = "0.16.10, 0.17" IfElse = "0.1" LinearAlgebra = "1" @@ -120,5 +123,6 @@ julia = "1.10" [extras] Convex = "f65535da-76fb-5f13-bab9-19810c17039a" ECOS = "e2685f51-7e38-5353-a97d-a921fd2c8199" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" diff --git a/examples/tree_2d_dgsem/elixir_advection_amr_visualization_makie.jl b/examples/tree_2d_dgsem/elixir_advection_amr_visualization_makie.jl new file mode 100644 index 0000000000..fb4c926619 --- /dev/null +++ b/examples/tree_2d_dgsem/elixir_advection_amr_visualization_makie.jl @@ -0,0 +1,78 @@ + +using OrdinaryDiffEq +using Trixi +using GLMakie + +############################################################################### +# semidiscretization of the linear advection equation + +advection_velocity = (0.2, -0.7) +equations = LinearScalarAdvectionEquation2D(advection_velocity) + +function initial_condition_gauss_largedomain(x, t, + equation::LinearScalarAdvectionEquation2D) + # Store translated coordinate for easy use of exact solution + domain_length = SVector(10, 10) + x_trans = Trixi.x_trans_periodic_2d(x - equation.advection_velocity * t, domain_length) + + return SVector(exp(-(x_trans[1]^2 + x_trans[2]^2))) +end +initial_condition = initial_condition_gauss_largedomain + +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) + +coordinates_min = (-5.0, -5.0) +coordinates_max = (5.0, 5.0) +mesh = TreeMesh(coordinates_min, coordinates_max, + initial_refinement_level = 3, + n_cells_max = 30_000) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 20.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_integrals = (entropy,)) + +alive_callback = AliveCallback(analysis_interval = analysis_interval) + +save_solution = SaveSolutionCallback(interval = 100, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) + +# Enable in-situ visualization with a new plot generated every 20 time steps +# and additional plotting options passed as keyword arguments +visualization = VisualizationCallback(interval = 100, clims = (0, 1), + plot_creator = Trixi.show_plot_makie) + +amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable = first), + base_level = 3, + med_level = 4, med_threshold = 0.1, + max_level = 5, max_threshold = 0.6) +amr_callback = AMRCallback(semi, amr_controller, + interval = 5, + adapt_initial_condition = true, + adapt_initial_condition_only_refine = true) + +stepsize_callback = StepsizeCallback(cfl = 1.6) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + save_solution, visualization, + amr_callback, stepsize_callback); + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback + save_everystep = false, callback = callbacks); +summary_callback() # print the timer summary diff --git a/ext/TrixiGLMakieExt.jl b/ext/TrixiGLMakieExt.jl new file mode 100644 index 0000000000..fb8981ae4a --- /dev/null +++ b/ext/TrixiGLMakieExt.jl @@ -0,0 +1,51 @@ +# Package extension for adding Makie-based features to Trixi.jl +module TrixiGLMakieExt + +# Required for visualization code +using GLMakie + +# Use functions that are to be extended and additional symbols that are not exported +using Trixi: Trixi, @unpack, @muladd + +@muladd begin +#! format: noindent + +# converts a single int into a tuple of ints, to get a square arrangement +# example: f(1) = (1,1) f(2) = (2,1) f(3) = (2,2) f(4) = (1,2) +function makieLayoutHelper(n) + if n == 1 + return (1, 1) + end + t = makieLayoutHelper(n - 1) + if t[1] == 1 + return (t[2] + 1, 1) + elseif t[1] > t[2] + return (t[1], t[2] + 1) + elseif t[2] >= t[1] + return (t[1] - 1, t[2]) + end +end + +function Trixi.show_plot_makie(visualization_callback, plot_data, variable_names; + show_mesh = true, plot_arguments = Dict{Symbol, Any}(), + time = nothing, timestep = nothing) + if visualization_callback.figure === nothing + @info "Creating new GLMakie figure" + visualization_callback.figure = GLMakie.Figure() + for v in 1:size(variable_names)[1] + push!(visualization_callback.axis, + GLMakie.Axis(visualization_callback.figure[makieLayoutHelper(v)...], + title = variable_names[v])) + end + GLMakie.display(visualization_callback.figure) + end + + @unpack axis = visualization_callback + for v in 1:size(variable_names)[1] + GLMakie.heatmap!(axis[v], plot_data.x, plot_data.y, plot_data.data[v]) + end + + # TODO: handle `show_mesh` +end +end # @muladd +end # module TrixiGLMakieExt diff --git a/src/callbacks_step/visualization.jl b/src/callbacks_step/visualization.jl index 302e7e4462..6b1262ee81 100644 --- a/src/callbacks_step/visualization.jl +++ b/src/callbacks_step/visualization.jl @@ -6,13 +6,15 @@ #! format: noindent mutable struct VisualizationCallback{SolutionVariables, VariableNames, PlotDataCreator, - PlotCreator} + PlotCreator, Figure, Axis} interval::Int solution_variables::SolutionVariables variable_names::VariableNames show_mesh::Bool plot_data_creator::PlotDataCreator plot_creator::PlotCreator + figure::Figure + axis::Axis plot_arguments::Dict{Symbol, Any} end @@ -99,19 +101,25 @@ function VisualizationCallback(; interval = 0, solution_variables, variable_names, show_mesh, plot_data_creator, plot_creator, + nothing, [], Dict{Symbol, Any}(plot_arguments)) - # Warn users if they create a visualization callback without having loaded the Plots package + # Warn users if they create a visualization callback without having loaded a plotting + # package # - # Note: This warning is added for convenience, as Plots is the only "officially" supported - # visualization package right now. However, in general nothing prevents anyone from using - # other packages such as Makie, Gadfly etc., given that appropriate `plot_creator`s are - # passed. This is also the reason why the visualization callback is not included via - # Requires.jl only when Plots is present. - # In the future, we should update/remove this warning if other plotting packages are - # starting to be used. - if !(:Plots in names(@__MODULE__, all = true)) - @warn "Package `Plots` not loaded but required by `VisualizationCallback` to visualize results" + # Note: This warning is added for convenience, as Plots and Makie are currently the + # only "officially" supported visualization packages. However, in general nothing + # prevents anyone from using other packages, given that appropriate `plot_creator`s are + # passed. + + # TODO: rest of this comment? + # This is also the reason why the visualization callback is not included via + # Requires.jl only when Plots is present. + # In the future, we should update/remove this warning if other plotting packages are + # starting to be used. + if !(:Plots in names(@__MODULE__, all = true)) && + Base.get_extension(Trixi, :TrixiGLMakieExt) === nothing + @warn "Neither `Plots` nor `GLMakie` loaded but required by `VisualizationCallback` to visualize results" end DiscreteCallback(visualization_callback, visualization_callback, # the first one is the condition, the second the affect! @@ -156,7 +164,7 @@ function (visualization_callback::VisualizationCallback)(integrator) end # Create plot - plot_creator(plot_data, variable_names; + plot_creator(visualization_callback, plot_data, variable_names; show_mesh = show_mesh, plot_arguments = plot_arguments, time = integrator.t, timestep = integrator.stats.naccept) @@ -166,7 +174,7 @@ function (visualization_callback::VisualizationCallback)(integrator) end """ - show_plot(plot_data, variable_names; + show_plot(visualization_callback, plot_data, variable_names; show_mesh=true, plot_arguments=Dict{Symbol,Any}(), time=nothing, timestep=nothing) @@ -182,7 +190,7 @@ This function is the default `plot_creator` argument for the [`VisualizationCall See also: [`VisualizationCallback`](@ref), [`save_plot`](@ref) """ -function show_plot(plot_data, variable_names; +function show_plot(visualization_callback, plot_data, variable_names; show_mesh = true, plot_arguments = Dict{Symbol, Any}(), time = nothing, timestep = nothing) # Gather subplots @@ -218,7 +226,7 @@ function show_plot(plot_data, variable_names; end """ - save_plot(plot_data, variable_names; + save_plot(visualization_callback, plot_data, variable_names; show_mesh=true, plot_arguments=Dict{Symbol,Any}(), time=nothing, timestep=nothing) @@ -234,7 +242,7 @@ The `timestep` is used in the filename. `time` is currently unused by this funct See also: [`VisualizationCallback`](@ref), [`show_plot`](@ref) """ -function save_plot(plot_data, variable_names; +function save_plot(visualization_callback, plot_data, variable_names; show_mesh = true, plot_arguments = Dict{Symbol, Any}(), time = nothing, timestep = nothing) # Gather subplots @@ -258,4 +266,8 @@ function save_plot(plot_data, variable_names; filename = joinpath("out", @sprintf("solution_%09d.png", timestep)) Plots.savefig(filename) end + +# Add definitions of Makie plot functions here such that hey can be exported from Trixi.jl +# and extended in the TrixiGLMakieExt extension +function show_plot_makie end end # @muladd