Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/utils/trixi2txt.jl
2055 views
1
#
2
# To use do the following
3
# using Revise
4
# includet("utils/trixi2txt.jl")
5
# using .Trixi2Txt
6
# Trixi2Txt.trixi2txt("out/file_name")
7
#
8
# It may be that you need to install the Glob package by running
9
# `import Pkg; Pkg.add("Glob")`.
10
#
11
# After the HDF5 files have been converted to `.txt` the 1D solution can be
12
# visualized in ParaView with the following steps:
13
#
14
# 1) Open the set of `solution_*.txt` files
15
# 2) Change the "Field Delimiter Characters" field to be a space instead of a comma
16
# 3) Check the box for "Merge Consecutive Delimiters"
17
# 4) Create a plot using Filters -> Data Analysis -> Plot Data
18
# 5) Within Plot Data, uncheck "Use Index For XAxis"
19
# 6) Select the `x` values for "X Array Name"
20
# 7) Now you can adjust the plots and make movies or save screenshots of the solution
21
module Trixi2Txt
22
23
using EllipsisNotation
24
using Glob: glob
25
using Printf: @printf
26
using HDF5: h5open, attributes, haskey
27
using MuladdMacro: @muladd
28
# using Tullio: @tullio
29
using LoopVectorization
30
using StaticArrays
31
using UnPack: @unpack
32
33
include("../src/basic_types.jl")
34
include("../src/solvers/dgsem/basis_lobatto_legendre.jl")
35
include("../src/solvers/dgsem/interpolation.jl")
36
37
function trixi2txt(filename::AbstractString...;
38
variables = [], output_directory = ".", nvisnodes = nothing,
39
max_supported_level = 11)
40
# Convert filenames to a single list of strings
41
if isempty(filename)
42
error("no input file was provided")
43
end
44
filenames = String[]
45
for pattern in filename
46
append!(filenames, glob(pattern))
47
end
48
49
# Iterate over input files
50
for (index, filename) in enumerate(filenames)
51
# Check if data file exists
52
if !isfile(filename)
53
error("file '$filename' does not exist")
54
end
55
56
# Make sure it is a data file
57
if !is_solution_restart_file(filename)
58
error("file '$filename' is not a data file")
59
end
60
61
# Get mesh file name
62
meshfile = extract_mesh_filename(filename)
63
64
# Check if mesh file exists
65
if !isfile(meshfile)
66
error("mesh file '$meshfile' does not exist")
67
end
68
69
# Read mesh
70
center_level_0, length_level_0, leaf_cells, coordinates, levels = read_meshfile(meshfile)
71
72
# Read data
73
labels, data, n_elements, n_nodes, element_variables, node_variables, time = read_datafile(filename)
74
75
# Check if dimensions match
76
if length(leaf_cells) != n_elements
77
error("number of elements in '$(filename)' do not match number of leaf cells in " *
78
"'$(meshfile)' " *
79
"(did you forget to clean your 'out/' directory between different runs?)")
80
end
81
82
# Determine resolution for data interpolation
83
max_level = maximum(levels)
84
if max_level > max_supported_level
85
error("Maximum refinement level in data file $max_level is higher than " *
86
"maximum supported level $max_supported_level")
87
end
88
max_available_nodes_per_finest_element = 2^(max_supported_level - max_level)
89
if nvisnodes === nothing
90
max_nvisnodes = 2 * n_nodes
91
elseif nvisnodes == 0
92
max_nvisnodes = n_nodes
93
else
94
max_nvisnodes = nvisnodes
95
end
96
nvisnodes_at_max_level = min(max_available_nodes_per_finest_element, max_nvisnodes)
97
resolution = nvisnodes_at_max_level * 2^max_level
98
nvisnodes_per_level = [2^(max_level - level) * nvisnodes_at_max_level
99
for level in 0:max_level]
100
101
# Interpolate data
102
structured_data = unstructured2structured(data, levels, resolution,
103
nvisnodes_per_level)
104
105
# Interpolate cell-centered values to node-centered values
106
node_centered_data = cell2node(structured_data)
107
108
# Determine x coordinates
109
xs = collect(range(-1, 1, length = resolution + 1)) .* length_level_0 / 2 .+
110
center_level_0[1]
111
112
# Check that all variables exist in data file
113
if isempty(variables)
114
append!(variables, labels)
115
else
116
for var in variables
117
if !(var in labels)
118
error("variable '$var' does not exist in the data file $filename")
119
end
120
end
121
end
122
123
# Create output directory if it does not exist
124
mkpath(output_directory)
125
126
# Determine output file name
127
base, _ = splitext(splitdir(filename)[2])
128
output_filename = joinpath(output_directory, "$(base).txt")
129
130
# Write to file
131
open(output_filename, "w") do io
132
# Header
133
print(io, "x ")
134
for label in variables
135
@printf(io, " %-14s", label)
136
end
137
println(io)
138
139
# Data
140
for idx in eachindex(xs)
141
@printf(io, "%+10.8e", xs[idx])
142
for variable_id in eachindex(variables)
143
@printf(io, " %+10.8e ", node_centered_data[idx, variable_id])
144
end
145
println(io)
146
end
147
end
148
end
149
end
150
151
# Check if file is a data file
152
function is_solution_restart_file(filename::String)
153
# Open file for reading
154
h5open(filename, "r") do file
155
# If attribute "mesh_file" exists, this must be a data file
156
return haskey(attributes(file), "mesh_file")
157
end
158
end
159
160
# Use data file to extract mesh filename from attributes
161
function extract_mesh_filename(filename::String)
162
# Open file for reading
163
h5open(filename, "r") do file
164
# Extract filename relative to data file
165
mesh_file = read(attributes(file)["mesh_file"])
166
167
return joinpath(dirname(filename), mesh_file)
168
end
169
end
170
171
# Read in mesh file and return relevant data
172
function read_meshfile(filename::String)
173
# Open file for reading
174
h5open(filename, "r") do file
175
# Check dimension - only 1D supported
176
if haskey(attributes(file), "ndims")
177
ndims_ = read(attributes(file)["ndims"])
178
else
179
ndims_ = read(attributes(file)["ndim"]) # FIXME once Trixi.jl's 3D branch is merged & released
180
end
181
if ndims_ != 1
182
error("currently only 1D files can be processed, but '$filename' is $(ndims_)D")
183
end
184
185
# Extract basic information
186
n_cells = read(attributes(file)["n_cells"])
187
n_leaf_cells = read(attributes(file)["n_leaf_cells"])
188
center_level_0 = read(attributes(file)["center_level_0"])
189
length_level_0 = read(attributes(file)["length_level_0"])
190
191
# Extract coordinates, levels, child cells
192
coordinates = Array{Float64}(undef, ndims_, n_cells)
193
coordinates .= read(file["coordinates"])
194
levels = Array{Int}(undef, n_cells)
195
levels .= read(file["levels"])
196
child_ids = Array{Int}(undef, 2^ndims_, n_cells)
197
child_ids .= read(file["child_ids"])
198
199
# Extract leaf cells (= cells to be plotted) and contract all other arrays accordingly
200
leaf_cells = similar(levels)
201
n_cells = 0
202
for cell_id in eachindex(levels)
203
if sum(child_ids[:, cell_id]) > 0
204
continue
205
end
206
207
n_cells += 1
208
leaf_cells[n_cells] = cell_id
209
end
210
leaf_cells = leaf_cells[1:n_cells]
211
212
coordinates = coordinates[:, leaf_cells]
213
levels = levels[leaf_cells]
214
215
return center_level_0, length_level_0, leaf_cells, coordinates, levels
216
end
217
end
218
219
# Read in data file and return all relevant information
220
function read_datafile(filename::String)
221
# Open file for reading
222
h5open(filename, "r") do file
223
# Extract basic information
224
if haskey(attributes(file), "ndims")
225
ndims_ = read(attributes(file)["ndims"])
226
else
227
ndims_ = read(attributes(file)["ndim"])
228
end
229
if haskey(attributes(file), "polydeg")
230
polydeg = read(attributes(file)["polydeg"])
231
else
232
polydeg = read(attributes(file)["N"])
233
end
234
n_elements = read(attributes(file)["n_elements"])
235
n_variables = read(attributes(file)["n_vars"])
236
time = read(attributes(file)["time"])
237
238
# Extract labels for legend
239
labels = Array{String}(undef, 1, n_variables)
240
for v in 1:n_variables
241
labels[1, v] = read(attributes(file["variables_$v"])["name"])
242
end
243
244
# Extract data arrays
245
n_nodes = polydeg + 1
246
247
if ndims_ == 1
248
data = Array{Float64}(undef, n_nodes, n_elements, n_variables)
249
for v in 1:n_variables
250
vardata = read(file["variables_$v"])
251
@views data[:, :, v][:] .= vardata
252
end
253
else
254
error("Unsupported number of spatial dimensions: ", ndims_)
255
end
256
257
# Extract element variable arrays
258
element_variables = Dict{String, Union{Vector{Float64}, Vector{Int}}}()
259
index = 1
260
while haskey(file, "element_variables_$index")
261
varname = read(attributes(file["element_variables_$index"])["name"])
262
element_variables[varname] = read(file["element_variables_$index"])
263
index += 1
264
end
265
266
# Extract node variable arrays
267
node_variables = Dict{String, Union{Vector{Float64}, Vector{Int}}}()
268
index = 1
269
while haskey(file, "node_variables_$index")
270
varname = read(attributes(file["node_variables_$index"])["name"])
271
node_variables[varname] = read(file["node_variables_$index"])
272
index += 1
273
end
274
275
return labels, data, n_elements, n_nodes, element_variables, node_variables, time
276
end
277
end
278
279
# Interpolate unstructured DG data to structured data (cell-centered)
280
function unstructured2structured(unstructured_data::AbstractArray{Float64},
281
levels::AbstractArray{Int}, resolution::Int,
282
nvisnodes_per_level::AbstractArray{Int})
283
# Extract data shape information
284
n_nodes_in, n_elements, n_variables = size(unstructured_data)
285
286
# Get node coordinates for DG locations on reference element
287
nodes_in, _ = gauss_lobatto_nodes_weights(n_nodes_in)
288
289
# Calculate interpolation vandermonde matrices for each level
290
max_level = length(nvisnodes_per_level) - 1
291
vandermonde_per_level = []
292
for l in 0:max_level
293
n_nodes_out = nvisnodes_per_level[l + 1]
294
dx = 2 / n_nodes_out
295
nodes_out = collect(range(-1 + dx / 2, 1 - dx / 2, length = n_nodes_out))
296
push!(vandermonde_per_level, polynomial_interpolation_matrix(nodes_in, nodes_out))
297
end
298
299
# Create output data structure
300
structured = Array{Float64}(undef, resolution, n_variables)
301
302
# For each variable, interpolate element data and store to global data structure
303
for v in 1:n_variables
304
first = 1
305
306
# Reshape data array for use in interpolate_nodes function
307
@views reshaped_data = reshape(unstructured_data[:, :, v], 1, n_nodes_in,
308
n_elements)
309
310
for element_id in 1:n_elements
311
# Extract level for convenience
312
level = levels[element_id]
313
314
# Determine target indices
315
n_nodes_out = nvisnodes_per_level[level + 1]
316
last = first + (n_nodes_out - 1)
317
318
# Interpolate data
319
vandermonde = vandermonde_per_level[level + 1]
320
@views structured[first:last, v] .= (reshape(multiply_dimensionwise_naive(reshaped_data[:,
321
:,
322
element_id],
323
vandermonde),
324
n_nodes_out))
325
326
# Update first index for next iteration
327
first += n_nodes_out
328
end
329
end
330
331
return structured
332
end
333
334
# Convert cell-centered values to node-centered values by averaging over all
335
# four neighbors and making use of the periodicity of the solution
336
function cell2node(cell_centered_data::AbstractArray{Float64})
337
# Create temporary data structure to make the averaging algorithm as simple
338
# as possible (by using a ghost layer)
339
tmp = similar(cell_centered_data, size(cell_centered_data) .+ (2, 0))
340
341
# Fill center with original data
342
tmp[2:(end - 1), :] .= cell_centered_data
343
344
# # Fill sides with opposite data (periodic domain)
345
# # x-direction
346
# tmp[1, :] .= cell_centered_data[end, :]
347
# tmp[end, :] .= cell_centered_data[1, :]
348
349
# Fill sides with duplicate information
350
# x-direction
351
tmp[1, :] .= cell_centered_data[1, :]
352
tmp[end, :] .= cell_centered_data[end, :]
353
354
# Create output data structure
355
resolution_in, n_variables = size(cell_centered_data)
356
resolution_out = resolution_in + 1
357
node_centered_data = Array{Float64}(undef, resolution_out, n_variables)
358
359
# Obtain node-centered value by averaging over neighboring cell-centered values
360
for i in 1:resolution_out
361
node_centered_data[i, :] = (tmp[i, :] + tmp[i + 1, :]) / 2
362
end
363
364
return node_centered_data
365
end
366
367
end
368
369