Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/visualization/recipes_plots.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
# Visualize a single variable in a 2D plot (default: heatmap)
9
RecipesBase.@recipe function f(pds::PlotDataSeries{<:AbstractPlotData{2}})
10
@unpack plot_data, variable_id = pds
11
@unpack x, y, data, variable_names, orientation_x, orientation_y = plot_data
12
13
# Set geometric properties
14
xlims --> (x[begin], x[end])
15
ylims --> (y[begin], y[end])
16
aspect_ratio --> :equal
17
18
# Set annotation properties
19
legend --> :none
20
title --> variable_names[variable_id]
21
colorbar --> :true
22
xguide --> _get_guide(orientation_x)
23
yguide --> _get_guide(orientation_y)
24
25
# Set series properties
26
seriestype --> :heatmap
27
28
# Return data for plotting
29
x, y, data[variable_id]
30
end
31
32
# Visualize the mesh in a 2D plot
33
RecipesBase.@recipe function f(pm::PlotMesh{<:AbstractPlotData{2}})
34
@unpack plot_data = pm
35
@unpack x, y, mesh_vertices_x, mesh_vertices_y = plot_data
36
37
# Set geometric and annotation properties
38
xlims --> (x[begin], x[end])
39
ylims --> (y[begin], y[end])
40
aspect_ratio --> :equal
41
legend --> :none
42
grid --> false
43
44
# Set series properties
45
seriestype --> :path
46
linecolor --> :grey
47
linewidth --> 1
48
49
# Return data for plotting
50
mesh_vertices_x, mesh_vertices_y
51
end
52
53
# Visualize the mesh in a 2D plot
54
RecipesBase.@recipe function f(pm::PlotMesh{<:PlotData2DCartesian{<:Any,
55
<:AbstractVector{<:AbstractVector}}})
56
@unpack plot_data = pm
57
@unpack x, y, mesh_vertices_x, mesh_vertices_y = plot_data
58
59
# Set geometric and annotation properties
60
xlims --> (minimum(x), maximum(x))
61
ylims --> (minimum(y), maximum(y))
62
aspect_ratio --> :equal
63
legend --> :none
64
grid --> false
65
66
# Set series properties
67
seriestype --> :path
68
linecolor --> :grey
69
linewidth --> 1
70
71
# Return data for plotting
72
mesh_vertices_x, mesh_vertices_y
73
end
74
75
# Plot all available variables at once for convenience
76
RecipesBase.@recipe function f(pd::AbstractPlotData)
77
# Create layout that is as square as possible, when there are more than 3 subplots.
78
# This is done with a preference for more columns than rows if not.
79
80
if length(pd) <= 3
81
cols = length(pd)
82
rows = 1
83
else
84
cols = ceil(Int, sqrt(length(pd)))
85
rows = ceil(Int, length(pd) / cols)
86
end
87
88
layout := (rows, cols)
89
90
# Plot all existing variables
91
for (i, (variable_name, series)) in enumerate(pd)
92
RecipesBase.@series begin
93
subplot := i
94
series
95
end
96
end
97
98
# Fill remaining subplots with empty plot
99
for i in (length(pd) + 1):(rows * cols)
100
RecipesBase.@series begin
101
subplot := i
102
axis := false
103
ticks := false
104
legend := false
105
[], []
106
end
107
end
108
end
109
110
# Plot a single variable.
111
RecipesBase.@recipe function f(pds::PlotDataSeries{<:AbstractPlotData{1}})
112
@unpack plot_data, variable_id = pds
113
@unpack x, data, variable_names, orientation_x = plot_data
114
115
# Set geometric properties
116
xlims --> (x[begin], x[end])
117
118
# Set annotation properties
119
legend --> :none
120
title --> variable_names[variable_id]
121
xguide --> _get_guide(orientation_x)
122
123
# Return data for plotting
124
x, data[:, variable_id]
125
end
126
127
# Plot the mesh as vertical lines from a PlotMesh object.
128
RecipesBase.@recipe function f(pm::PlotMesh{<:AbstractPlotData{1}})
129
@unpack plot_data = pm
130
@unpack x, mesh_vertices_x = plot_data
131
132
# Set geometric and annotation properties
133
xlims --> (x[begin], x[end])
134
legend --> :none
135
136
# Set series properties
137
seriestype --> :vline
138
linecolor --> :grey
139
linewidth --> 1
140
141
# Return data for plotting
142
mesh_vertices_x
143
end
144
145
# Create a plot directly from a TrixiODESolution for convenience
146
# The plot is created by a PlotData1D or PlotData2D object.
147
RecipesBase.@recipe function f(sol::TrixiODESolution)
148
# Redirect everything to the recipes below
149
return sol.u[end], sol.prob.p
150
end
151
152
# Recipe for general semidiscretizations
153
# Note: If you change the defaults values here, you need to also change them in the PlotData1D or PlotData2D
154
# constructor.
155
RecipesBase.@recipe function f(u, semi::AbstractSemidiscretization;
156
solution_variables = nothing)
157
if ndims(semi) == 1
158
return PlotData1D(u, semi; solution_variables = solution_variables)
159
else
160
return PlotData2D(u, semi; solution_variables = solution_variables)
161
end
162
end
163
164
# Also allow plotting a function with signature `func(x, equations)`, e.g., for initial conditions.
165
# We need this recipe in addition to the one above to avoid method ambiguities.
166
RecipesBase.@recipe function f(func::Function, semi::AbstractSemidiscretization;
167
solution_variables = nothing)
168
n_variables = length(func(SVector(0.0), semi.equations))
169
variable_names = SVector(["func[$i]" for i in 1:n_variables]...)
170
if ndims(semi) == 1
171
return PlotData1D(func, semi; solution_variables = cons2cons, variable_names)
172
else
173
throw(ArgumentError("Plotting of functions is only supported in 1D."))
174
end
175
end
176
177
# Recipe specifically for TreeMesh-type solutions
178
# Note: If you change the defaults values here, you need to also change them in the PlotData1D or PlotData2D
179
# constructor.
180
RecipesBase.@recipe function f(u, semi::SemidiscretizationHyperbolic{<:TreeMesh};
181
solution_variables = nothing,
182
grid_lines = true, max_supported_level = 11,
183
nvisnodes = nothing,
184
reinterpolate = default_reinterpolate(semi.solver),
185
slice = :xy, point = (0.0, 0.0, 0.0), curve = nothing)
186
# Create a PlotData1D or PlotData2D object depending on the dimension.
187
if ndims(semi) == 1
188
return PlotData1D(u, semi; solution_variables, nvisnodes, reinterpolate,
189
slice, point, curve)
190
else
191
return PlotData2D(u, semi;
192
solution_variables, grid_lines, max_supported_level,
193
nvisnodes, slice, point)
194
end
195
end
196
197
# Also allow plotting a function with signature `func(x, equations)`, e.g., for initial conditions.
198
RecipesBase.@recipe function f(func::Function,
199
semi::SemidiscretizationHyperbolic{<:TreeMesh};
200
solution_variables = nothing,
201
nvisnodes = nothing, slice = :xy,
202
point = (0.0, 0.0, 0.0), curve = nothing)
203
n_variables = length(func(SVector(0.0), semi.equations))
204
variable_names = SVector(["func[$i]" for i in 1:n_variables]...)
205
if ndims(semi) == 1
206
return PlotData1D(func, semi; solution_variables = cons2cons, nvisnodes, slice,
207
point, curve, variable_names)
208
else
209
throw(ArgumentError("Plotting of functions is only supported in 1D."))
210
end
211
end
212
213
# Series recipe for PlotData2DTriangulated
214
RecipesBase.@recipe function f(pds::PlotDataSeries{<:PlotData2DTriangulated})
215
pd = pds.plot_data
216
@unpack variable_id = pds
217
@unpack x, y, data, t, variable_names = pd
218
219
# extract specific solution field to plot
220
data_field = zeros(eltype(first(data)), size(data))
221
for (i, data_i) in enumerate(data)
222
data_field[i] = data_i[variable_id]
223
end
224
225
legend --> false
226
aspect_ratio --> 1
227
title --> pd.variable_names[variable_id]
228
xlims --> extrema(x)
229
ylims --> extrema(y)
230
xguide --> _get_guide(1)
231
yguide --> _get_guide(2)
232
seriestype --> :heatmap
233
colorbar --> :true
234
235
return DGTriPseudocolor(global_plotting_triangulation_triplot((x, y), data_field,
236
t)...)
237
end
238
239
# Visualize a 2D mesh given an `PlotData2DTriangulated` object
240
RecipesBase.@recipe function f(pm::PlotMesh{<:PlotData2DTriangulated})
241
pd = pm.plot_data
242
@unpack x_face, y_face = pd
243
244
# This line separates solution lines on each edge by NaNs to ensure that they are rendered
245
# separately. The coordinates `xf`, `yf` and the solution `sol_f`` are assumed to be a matrix
246
# whose columns correspond to different elements. We add NaN separators by appending a row of
247
# NaNs to this matrix. We also flatten (e.g., apply `vec` to) the result, as this speeds up
248
# plotting.
249
x_face, y_face = map(x -> vec(vcat(x, fill(NaN, 1, size(x, 2)))), (x_face, y_face))
250
251
xlims --> extrema(x_face)
252
ylims --> extrema(y_face)
253
aspect_ratio --> :equal
254
legend --> :none
255
256
# Set series properties
257
seriestype --> :path
258
linecolor --> :grey
259
linewidth --> 1
260
261
return x_face, y_face
262
end
263
264
# Visualizes a single scalar field. Intended for use with ScalarPlotData2D.
265
# Example usage: `plot(ScalarPlotData2D(u, semi))`.
266
RecipesBase.@recipe function f(pd::PlotData2DTriangulated{<:ScalarData})
267
@unpack x, y, data, t, variable_names = pd
268
269
title_string = isnothing(variable_names) ? "" : variable_names
270
271
legend --> false
272
aspect_ratio --> 1
273
title --> title_string
274
xlims --> extrema(x)
275
ylims --> extrema(y)
276
xguide --> _get_guide(1)
277
yguide --> _get_guide(2)
278
seriestype --> :heatmap
279
colorbar --> :true
280
281
# Since `data` is simply a ScalarData wrapper around the actual plot data, we pass in
282
# `data.data` instead.
283
return DGTriPseudocolor(global_plotting_triangulation_triplot((x, y), data.data,
284
t)...)
285
end
286
287
RecipesBase.@recipe function f(cb::DiscreteCallback{<:Any, <:TimeSeriesCallback},
288
point_id::Integer)
289
return cb.affect!, point_id
290
end
291
292
RecipesBase.@recipe function f(time_series_callback::TimeSeriesCallback,
293
point_id::Integer)
294
return PlotData1D(time_series_callback, point_id)
295
end
296
end # @muladd
297
298