Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/callbacks_step/visualization.jl
2055 views
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
mutable struct VisualizationCallback{PlotDataCreator, SolutionVariables, VariableNames,
9
PlotCreator}
10
plot_data_creator::PlotDataCreator
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,
20
cb::DiscreteCallback{Condition, Affect!}) where {Condition,
21
Affect! <:
22
VisualizationCallback
23
}
24
visualization_callback = cb.affect!
25
@unpack plot_data_creator, interval, plot_arguments, solution_variables, variable_names, show_mesh, plot_creator = visualization_callback
26
print(io, "VisualizationCallback(",
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
end
35
36
function Base.show(io::IO, ::MIME"text/plain",
37
cb::DiscreteCallback{Condition, Affect!}) where {Condition,
38
Affect! <:
39
VisualizationCallback
40
}
41
if get(io, :compact, false)
42
show(io, cb)
43
else
44
visualization_callback = cb.affect!
45
46
setup = [
47
"plot data creator" => visualization_callback.plot_data_creator,
48
"interval" => visualization_callback.interval,
49
"plot arguments" => visualization_callback.plot_arguments,
50
"solution variables" => visualization_callback.solution_variables,
51
"variable names" => visualization_callback.variable_names,
52
"show mesh" => visualization_callback.show_mesh,
53
"plot creator" => visualization_callback.plot_creator
54
]
55
summary_box(io, "VisualizationCallback", setup)
56
end
57
end
58
59
"""
60
VisualizationCallback(semi, plot_data_creator = nothing;
61
interval=0,
62
solution_variables=cons2prim,
63
variable_names=[],
64
show_mesh=false,
65
plot_creator=show_plot,
66
plot_arguments...)
67
68
Create a callback that visualizes results during a simulation, also known as *in-situ
69
visualization*.
70
71
To customize the generated figure, `plot_data_creator` allows to use different plot data types.
72
Currently provided are [`PlotData1D`](@ref) and [`PlotData2D`](@ref), while the latter is used for both 2D and 3D.
73
74
The `interval` specifies the number of time step iterations after which a new plot is generated. The
75
available variables to plot are configured with the `solution_variables` parameter, which acts the
76
same way as for the [`SaveSolutionCallback`](@ref). The variables to be actually plotted can be
77
selected by providing a single string or a list of strings to `variable_names`, and if `show_mesh`
78
is `true`, an additional plot with the mesh will be generated.
79
80
With `plot_creator` you can further specify an own function to visualize results, which must support the
81
same interface as the default implementation [`show_plot`](@ref). All remaining
82
keyword arguments are collected and passed as additional arguments to the plotting command.
83
"""
84
function VisualizationCallback(semi, plot_data_creator = nothing;
85
interval = 0,
86
solution_variables = cons2prim,
87
variable_names = [],
88
show_mesh = false,
89
plot_creator = show_plot,
90
plot_arguments...)
91
mpi_isparallel() && error("this callback does not work in parallel yet")
92
93
if variable_names isa String
94
variable_names = String[variable_names]
95
end
96
97
if plot_data_creator === nothing # No custom plot data type provided
98
if ndims(semi) == 1
99
plot_data_creator = PlotData1D
100
else # 2D or 3D
101
plot_data_creator = PlotData2D
102
end
103
end
104
105
visualization_callback = VisualizationCallback(plot_data_creator,
106
interval,
107
solution_variables, variable_names,
108
show_mesh,
109
plot_creator,
110
Dict{Symbol, Any}(plot_arguments))
111
112
# Warn users if they create a visualization callback without having loaded the Plots package
113
#
114
# Note: This warning is added for convenience, as Plots is the only "officially" supported
115
# visualization package right now. However, in general nothing prevents anyone from using
116
# other packages such as Makie, Gadfly etc., given that appropriate `plot_creator`s are
117
# passed. This is also the reason why the visualization callback is not included via
118
# Requires.jl only when Plots is present.
119
# In the future, we should update/remove this warning if other plotting packages are
120
# starting to be used.
121
if !(:Plots in names(@__MODULE__, all = true))
122
@warn "Package `Plots` not loaded but required by `VisualizationCallback` to visualize results"
123
end
124
125
DiscreteCallback(visualization_callback, visualization_callback, # the first one is the condition, the second the affect!
126
save_positions = (false, false),
127
initialize = initialize!)
128
end
129
130
function initialize!(cb::DiscreteCallback{Condition, Affect!}, u, t,
131
integrator) where {Condition, Affect! <: VisualizationCallback}
132
visualization_callback = cb.affect!
133
134
visualization_callback(integrator)
135
136
return nothing
137
end
138
139
# this method is called to determine whether the callback should be activated
140
function (visualization_callback::VisualizationCallback)(u, t, integrator)
141
@unpack interval = visualization_callback
142
143
# With error-based step size control, some steps can be rejected. Thus,
144
# `integrator.iter >= integrator.stats.naccept`
145
# (total #steps) (#accepted steps)
146
# We need to check the number of accepted steps since callbacks are not
147
# activated after a rejected step.
148
return interval > 0 && (integrator.stats.naccept % interval == 0 ||
149
isfinished(integrator))
150
end
151
152
# this method is called when the callback is activated
153
function (visualization_callback::VisualizationCallback)(integrator)
154
u_ode = integrator.u
155
semi = integrator.p
156
@unpack plot_data_creator, plot_arguments, solution_variables, variable_names, show_mesh, plot_creator = visualization_callback
157
158
# Extract plot data
159
plot_data = plot_data_creator(u_ode, semi, solution_variables = solution_variables)
160
161
# If variable names were not specified, plot everything
162
if isempty(variable_names)
163
variable_names = String[keys(plot_data)...]
164
end
165
166
# Create plot
167
plot_creator(plot_data, variable_names;
168
show_mesh = show_mesh, plot_arguments = plot_arguments,
169
time = integrator.t, timestep = integrator.stats.naccept)
170
171
# avoid re-evaluating possible FSAL stages
172
u_modified!(integrator, false)
173
return nothing
174
end
175
176
"""
177
show_plot(plot_data, variable_names;
178
show_mesh=true, plot_arguments=Dict{Symbol,Any}(),
179
time=nothing, timestep=nothing)
180
181
Visualize the plot data object provided in `plot_data` and display result, plotting only the
182
variables in `variable_names` and, optionally, the mesh (if `show_mesh` is `true`). Additionally,
183
`plot_arguments` will be unpacked and passed as keyword arguments to the `Plots.plot` command.
184
185
This function is the default `plot_creator` argument for the [`VisualizationCallback`](@ref).
186
`time` and `timestep` are currently unused by this function.
187
188
See also: [`VisualizationCallback`](@ref), [`save_plot`](@ref)
189
"""
190
function show_plot(plot_data, variable_names;
191
show_mesh = true, plot_arguments = Dict{Symbol, Any}(),
192
time = nothing, timestep = nothing)
193
# Gather subplots
194
plots = []
195
for v in variable_names
196
push!(plots, Plots.plot(plot_data[v]; plot_arguments...))
197
end
198
if show_mesh
199
push!(plots, Plots.plot(getmesh(plot_data); plot_arguments...))
200
end
201
202
# Note, for the visualization callback to work for general equation systems
203
# this layout construction would need to use the if-logic below.
204
# Currently, there is no use case for this so it is left here as a note.
205
#
206
# Determine layout
207
# if length(plots) <= 3
208
# cols = length(plots)
209
# rows = 1
210
# else
211
# cols = ceil(Int, sqrt(length(plots)))
212
# rows = div(length(plots), cols, RoundUp)
213
# end
214
# layout = (rows, cols)
215
216
# Determine layout
217
cols = ceil(Int, sqrt(length(plots)))
218
rows = div(length(plots), cols, RoundUp)
219
layout = (rows, cols)
220
221
# Show plot
222
display(Plots.plot(plots..., layout = layout))
223
end
224
225
"""
226
save_plot(plot_data, variable_names;
227
show_mesh=true, plot_arguments=Dict{Symbol,Any}(),
228
time=nothing, timestep=nothing)
229
230
Visualize the plot data object provided in `plot_data` and save result as a PNG file in the `out`
231
directory, plotting only the variables in `variable_names` and, optionally, the mesh (if `show_mesh`
232
is `true`). Additionally, `plot_arguments` will be unpacked and passed as keyword arguments to the
233
`Plots.plot` command.
234
235
The `timestep` is used in the filename. `time` is currently unused by this function.
236
237
See also: [`VisualizationCallback`](@ref), [`show_plot`](@ref)
238
"""
239
function save_plot(plot_data, variable_names;
240
show_mesh = true, plot_arguments = Dict{Symbol, Any}(),
241
time = nothing, timestep = nothing)
242
# Gather subplots
243
plots = []
244
for v in variable_names
245
push!(plots, Plots.plot(plot_data[v]; plot_arguments...))
246
end
247
if show_mesh
248
push!(plots, Plots.plot(getmesh(plot_data); plot_arguments...))
249
end
250
251
# Determine layout
252
cols = ceil(Int, sqrt(length(plots)))
253
rows = div(length(plots), cols, RoundUp)
254
layout = (rows, cols)
255
256
# Create plot
257
Plots.plot(plots..., layout = layout)
258
259
# Determine filename and save plot
260
filename = joinpath("out", @sprintf("solution_%09d.png", timestep))
261
Plots.savefig(filename)
262
end
263
end # @muladd
264
265