• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

trixi-framework / Trixi.jl / 20679838146

03 Jan 2026 04:24PM UTC coverage: 96.993%. Remained the same
20679838146

push

github

ranocha
set development version to v0.13.22-DEV

42292 of 43603 relevant lines covered (96.99%)

104628030.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.77
/src/callbacks_step/visualization.jl
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7

8
struct VisualizationCallback{PlotDataCreator, SolutionVariables, VariableNames,
9
                             PlotCreator}
10
    plot_data_creator::PlotDataCreator
4✔
11
    interval::Int
12
    solution_variables::SolutionVariables
13
    variable_names::VariableNames
14
    show_mesh::Bool
15
    plot_creator::PlotCreator
16
    plot_arguments::Dict{Symbol, Any}
17
end
18

19
function Base.show(io::IO,
1✔
20
                   cb::DiscreteCallback{Condition, Affect!}) where {Condition,
21
                                                                    Affect! <:
22
                                                                    VisualizationCallback
23
                                                                    }
24
    visualization_callback = cb.affect!
1✔
25
    @unpack plot_data_creator, interval, plot_arguments, solution_variables, variable_names, show_mesh, plot_creator = visualization_callback
1✔
26
    print(io, "VisualizationCallback(",
1✔
27
          "plot_data_creator=", plot_data_creator, ", ",
28
          "interval=", interval, ", ",
29
          "solution_variables=", solution_variables, ", ",
30
          "variable_names=", variable_names, ", ",
31
          "show_mesh=", show_mesh, ", ",
32
          "plot_creator=", plot_creator, ", ",
33
          "plot_arguments=", plot_arguments, ")")
34
    return nothing
1✔
35
end
36

37
function Base.show(io::IO, ::MIME"text/plain",
5✔
38
                   cb::DiscreteCallback{Condition, Affect!}) where {Condition,
39
                                                                    Affect! <:
40
                                                                    VisualizationCallback
41
                                                                    }
42
    if get(io, :compact, false)
18✔
43
        show(io, cb)
×
44
    else
45
        visualization_callback = cb.affect!
5✔
46

47
        setup = [
5✔
48
            "plot data creator" => visualization_callback.plot_data_creator,
49
            "interval" => visualization_callback.interval,
50
            "plot arguments" => visualization_callback.plot_arguments,
51
            "solution variables" => visualization_callback.solution_variables,
52
            "variable names" => visualization_callback.variable_names,
53
            "show mesh" => visualization_callback.show_mesh,
54
            "plot creator" => visualization_callback.plot_creator
55
        ]
56
        summary_box(io, "VisualizationCallback", setup)
5✔
57
    end
58
end
59

60
"""
61
    VisualizationCallback(semi, plot_data_creator = nothing;
62
                          interval=0,
63
                          solution_variables=cons2prim,
64
                          variable_names=[],
65
                          show_mesh=false,
66
                          plot_creator=show_plot,
67
                          plot_arguments...)
68

69
Create a callback that visualizes results during a simulation, also known as *in-situ
70
visualization*.
71

72
To customize the generated figure, `plot_data_creator` allows to use different plot data types.
73
Currently provided are [`PlotData1D`](@ref) and [`PlotData2D`](@ref), while the latter is used for both 2D and 3D.
74

75
The `interval` specifies the number of time step iterations after which a new plot is generated. The
76
available variables to plot are configured with the `solution_variables` parameter, which acts the
77
same way as for the [`SaveSolutionCallback`](@ref). The variables to be actually plotted can be
78
selected by providing a single string or a list of strings to `variable_names`, and if `show_mesh`
79
is `true`, an additional plot with the mesh will be generated.
80

81
With `plot_creator` you can further specify an own function to visualize results, which must support the
82
same interface as the default implementation [`show_plot`](@ref). All remaining
83
keyword arguments are collected and passed as additional arguments to the plotting command.
84
"""
85
function VisualizationCallback(semi, plot_data_creator = nothing;
12✔
86
                               interval = 0,
87
                               solution_variables = cons2prim,
88
                               variable_names = [],
89
                               show_mesh = false,
90
                               plot_creator = show_plot,
91
                               plot_arguments...)
92
    mpi_isparallel() && error("this callback does not work in parallel yet")
4✔
93

94
    if variable_names isa String
4✔
95
        variable_names = String[variable_names]
×
96
    end
97

98
    if plot_data_creator === nothing # No custom plot data type provided
4✔
99
        if ndims(semi) == 1
4✔
100
            plot_data_creator = PlotData1D
×
101
        else # 2D or 3D
102
            plot_data_creator = PlotData2D
4✔
103
        end
104
    end
105

106
    visualization_callback = VisualizationCallback(plot_data_creator,
4✔
107
                                                   interval,
108
                                                   solution_variables, variable_names,
109
                                                   show_mesh,
110
                                                   plot_creator,
111
                                                   Dict{Symbol, Any}(plot_arguments))
112

113
    # Warn users if they create a visualization callback without having loaded the Plots package
114
    #
115
    # Note: This warning is added for convenience, as Plots is the only "officially" supported
116
    #       visualization package right now. However, in general nothing prevents anyone from using
117
    #       other packages such as Makie, Gadfly etc., given that appropriate `plot_creator`s are
118
    #       passed. This is also the reason why the visualization callback is not included via
119
    #       Requires.jl only when Plots is present.
120
    #       In the future, we should update/remove this warning if other plotting packages are
121
    #       starting to be used.
122
    if !(:Plots in names(@__MODULE__, all = true))
9,868✔
123
        @warn "Package `Plots` not loaded but required by `VisualizationCallback` to visualize results"
×
124
    end
125

126
    return DiscreteCallback(visualization_callback, visualization_callback, # the first one is the condition, the second the affect!
4✔
127
                            save_positions = (false, false),
128
                            initialize = initialize!)
129
end
130

131
function initialize!(cb::DiscreteCallback{Condition, Affect!}, u, t,
4✔
132
                     integrator) where {Condition, Affect! <: VisualizationCallback}
133
    visualization_callback = cb.affect!
4✔
134

135
    visualization_callback(integrator)
8✔
136

137
    return nothing
4✔
138
end
139

140
# this method is called to determine whether the callback should be activated
141
function (visualization_callback::VisualizationCallback)(u, t, integrator)
454✔
142
    @unpack interval = visualization_callback
454✔
143

144
    # With error-based step size control, some steps can be rejected. Thus,
145
    #   `integrator.iter >= integrator.stats.naccept`
146
    #    (total #steps)       (#accepted steps)
147
    # We need to check the number of accepted steps since callbacks are not
148
    # activated after a rejected step.
149
    return interval > 0 && (integrator.stats.naccept % interval == 0 ||
886✔
150
            isfinished(integrator))
151
end
152

153
# this method is called when the callback is activated
154
function (visualization_callback::VisualizationCallback)(integrator)
30✔
155
    u_ode = integrator.u
30✔
156
    semi = integrator.p
30✔
157
    @unpack plot_data_creator, plot_arguments, solution_variables, variable_names, show_mesh, plot_creator = visualization_callback
30✔
158

159
    # Extract plot data
160
    plot_data = plot_data_creator(u_ode, semi, solution_variables = solution_variables)
30✔
161

162
    # If variable names were not specified, plot everything
163
    if isempty(variable_names)
30✔
164
        variable_names = String[keys(plot_data)...]
30✔
165
    end
166

167
    # Create plot
168
    plot_creator(plot_data, variable_names;
60✔
169
                 show_mesh = show_mesh, plot_arguments = plot_arguments,
170
                 time = integrator.t, timestep = integrator.stats.naccept)
171

172
    # avoid re-evaluating possible FSAL stages
173
    u_modified!(integrator, false)
30✔
174
    return nothing
30✔
175
end
176

177
"""
178
    show_plot(plot_data, variable_names;
179
              show_mesh=true, plot_arguments=Dict{Symbol,Any}(),
180
              time=nothing, timestep=nothing)
181

182
Visualize the plot data object provided in `plot_data` and display result, plotting only the
183
variables in `variable_names` and, optionally, the mesh (if `show_mesh` is `true`).  Additionally,
184
`plot_arguments` will be unpacked and passed as keyword arguments to the `Plots.plot` command.
185

186
This function is the default `plot_creator` argument for the [`VisualizationCallback`](@ref).
187
`time` and `timestep` are currently unused by this function.
188

189
See also: [`VisualizationCallback`](@ref), [`save_plot`](@ref)
190
"""
191
function show_plot(plot_data, variable_names;
54✔
192
                   show_mesh = true, plot_arguments = Dict{Symbol, Any}(),
193
                   time = nothing, timestep = nothing)
194
    # Gather subplots
195
    plots = []
27✔
196
    for v in variable_names
27✔
197
        push!(plots, Plots.plot(plot_data[v]; plot_arguments...))
54✔
198
    end
27✔
199
    if show_mesh
27✔
200
        push!(plots, Plots.plot(getmesh(plot_data); plot_arguments...))
×
201
    end
202

203
    # Note, for the visualization callback to work for general equation systems
204
    # this layout construction would need to use the if-logic below.
205
    # Currently, there is no use case for this so it is left here as a note.
206
    #
207
    # Determine layout
208
    # if length(plots) <= 3
209
    #   cols = length(plots)
210
    #   rows = 1
211
    # else
212
    #   cols = ceil(Int, sqrt(length(plots)))
213
    #   rows = div(length(plots), cols, RoundUp)
214
    # end
215
    # layout = (rows, cols)
216

217
    # Determine layout
218
    cols = ceil(Int, sqrt(length(plots)))
27✔
219
    rows = div(length(plots), cols, RoundUp)
27✔
220
    layout = (rows, cols)
27✔
221

222
    # Show plot
223
    return display(Plots.plot(plots..., layout = layout))
27✔
224
end
225

226
"""
227
    save_plot(plot_data, variable_names;
228
              show_mesh=true, plot_arguments=Dict{Symbol,Any}(),
229
              time=nothing, timestep=nothing)
230

231
Visualize the plot data object provided in `plot_data` and save result as a PNG file in the `out`
232
directory, plotting only the variables in `variable_names` and, optionally, the mesh (if `show_mesh`
233
is `true`).  Additionally, `plot_arguments` will be unpacked and passed as keyword arguments to the
234
`Plots.plot` command.
235

236
The `timestep` is used in the filename. `time` is currently unused by this function.
237

238
See also: [`VisualizationCallback`](@ref), [`show_plot`](@ref)
239
"""
240
function save_plot(plot_data, variable_names;
6✔
241
                   show_mesh = true, plot_arguments = Dict{Symbol, Any}(),
242
                   time = nothing, timestep = nothing)
243
    # Gather subplots
244
    plots = []
3✔
245
    for v in variable_names
3✔
246
        push!(plots, Plots.plot(plot_data[v]; plot_arguments...))
6✔
247
    end
3✔
248
    if show_mesh
3✔
249
        push!(plots, Plots.plot(getmesh(plot_data); plot_arguments...))
×
250
    end
251

252
    # Determine layout
253
    cols = ceil(Int, sqrt(length(plots)))
3✔
254
    rows = div(length(plots), cols, RoundUp)
3✔
255
    layout = (rows, cols)
3✔
256

257
    # Create plot
258
    Plots.plot(plots..., layout = layout)
3✔
259

260
    # Determine filename and save plot
261
    filename = joinpath("out", @sprintf("solution_%09d.png", timestep))
3✔
262
    return Plots.savefig(filename)
3✔
263
end
264
end # @muladd
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc