diff --git a/.gitignore b/.gitignore index f1677ce..821d95b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ **/Manifest.toml out/ artifacts/ +reference_files/ docs/build public/ coverage/ diff --git a/src/convert.jl b/src/convert.jl index 5c6452e..049659c 100644 --- a/src/convert.jl +++ b/src/convert.jl @@ -1,7 +1,8 @@ """ trixi2vtk(filename::AbstractString...; format=:vtu, verbose=false, hide_progress=false, pvd=nothing, - output_directory=".", nvisnodes=nothing) + output_directory=".", nvisnodes=nothing, save_celldata=true, + reinterpolate=true, data_is_uniform=false) Convert Trixi-generated output files to VTK files (VTU or VTI). @@ -21,6 +22,13 @@ Convert Trixi-generated output files to VTK files (VTU or VTI). A value of `0` (zero) uses the number of nodes in the DG elements. - `save_celldata`: Boolean value to determine if cell-based data should be saved. (default: `true`) +- `reinterpolate`: Boolean value to determine if data should be reinterpolated + onto uniform points. When `false` the raw data at the compute nodes + is copied into the appropriate format. + (default: `true`) +- `data_is_uniform`: Boolean to indicate if the data to be converted is from a finite difference + method on a uniform grid of points. + (default: `false`) # Examples ```julia @@ -30,7 +38,8 @@ julia> trixi2vtk("out/solution_000*.h5") """ function trixi2vtk(filename::AbstractString...; format=:vtu, verbose=false, hide_progress=false, pvd=nothing, - output_directory=".", nvisnodes=nothing, save_celldata=true) + output_directory=".", nvisnodes=nothing, save_celldata=true, + reinterpolate=true, data_is_uniform=false) # Reset timer reset_timer!() @@ -103,6 +112,11 @@ function trixi2vtk(filename::AbstractString...; verbose && println("| Reading mesh file...") @timeit "read mesh" mesh = Trixi.load_mesh_serial(meshfile; n_cells_max=0, RealT=Float64) + # Check compatibility of the mesh type and the output format + if format === :vti && !(mesh isa Trixi.TreeMesh{2}) + throw(ArgumentError("VTI format only available for 2D TreeMesh")) + end + # Read data only if it is a data file if is_datafile verbose && println("| Reading data file...") @@ -113,24 +127,55 @@ function trixi2vtk(filename::AbstractString...; # Determine resolution for data interpolation n_visnodes = get_default_nvisnodes_solution(nvisnodes, n_nodes, mesh) + + # If a user requests that no reinterpolation is done automatically set + # `n_visnodes` to be the same as the number of nodes in the raw data. + if !reinterpolate + n_visnodes = n_nodes + end + + # Check if the raw data is uniform (finite difference) or not (dg) + # and create the corresponding node set for reinterpolation / copying. + if (reinterpolate && !data_is_uniform) || (!reinterpolate && data_is_uniform) + # (1) Default settings; presumably the most common + # (2) Finite difference data + node_set = collect(range(-1, 1, length=n_visnodes)) + elseif !reinterpolate && !data_is_uniform + # raw data is on a set of LGL nodes + node_set, _ = gauss_lobatto_nodes_weights(n_visnodes) + else # reinterpolate & data_is_uniform + throw(ArgumentError("Uniform data should not be reinterpolated! Set `reinterpolate=false` and try again.")) + end else # If file is a mesh file, do not interpolate data as detailed n_visnodes = get_default_nvisnodes_mesh(nvisnodes, mesh) + # Create an "empty" node set that is unused in the mesh conversion + node_set = Array{Float64}(undef, n_visnodes) end # Create output directory if it does not exist mkpath(output_directory) # Build VTK grids - vtk_nodedata, vtk_celldata = build_vtk_grids(Val(format), mesh, n_visnodes, verbose, - output_directory, is_datafile, filename) + vtk_nodedata, vtk_celldata = build_vtk_grids(Val(format), mesh, node_set, n_visnodes, verbose, + output_directory, is_datafile, filename, Val(reinterpolate)) # Interpolate data if is_datafile verbose && println("| Interpolating data...") - @timeit "interpolate data" interpolated_data = interpolate_data(Val(format), - data, mesh, - n_visnodes, verbose) + if reinterpolate + @timeit "interpolate data" interpolated_data = interpolate_data(Val(format), + data, mesh, + n_visnodes, verbose) + else # Copy the raw solution data; only works for `vtu` format + # Extract data shape information + ndims_ = ndims(data) - 2 + n_variables = length(labels) + # Save raw data as one 1D array for each variable + @timeit "interpolate data" interpolated_data = reshape(data, + n_visnodes^ndims_ * n_elements, + n_variables) + end end # Add data to file diff --git a/src/interpolate.jl b/src/interpolate.jl index 50dd902..528ece6 100644 --- a/src/interpolate.jl +++ b/src/interpolate.jl @@ -21,10 +21,10 @@ end # Interpolate data from input format to desired output format (vti version) -function interpolate_data(::Val{:vti}, input_data, mesh, n_visnodes, verbose) +function interpolate_data(::Val{:vti}, input_data, mesh::TreeMesh, n_visnodes, verbose) coordinates, levels, center_level_0, length_level_0 = extract_mesh_information(mesh) - # Normalize element coordinates: move center to (0, 0) and domain size to [-1, 1]² + # Normalize element coordinates: move center to origin and domain size to [-1, 1]² normalized_coordinates = similar(coordinates) for element_id in axes(coordinates, 2) @views normalized_coordinates[:, element_id] .= ( @@ -77,7 +77,6 @@ function unstructured2structured(unstructured_data::AbstractArray{Float64}, # Create output data structure structured = Array{Float64}(undef, resolution, resolution, n_variables) - # For each variable, interpolate element data and store to global data structure for v in 1:n_variables # Reshape data array for use in interpolate_nodes function @@ -135,7 +134,7 @@ end # Find 2D array index for a 2-tuple of normalized, cell-centered coordinates (i.e., in [-1,1]) function coordinate2index(coordinate, resolution::Integer) # Calculate 1D normalized coordinates - dx = 2/resolution + dx = 2 / resolution mesh_coordinates = collect(range(-1 + dx/2, 1 - dx/2, length=resolution)) # Find index @@ -211,6 +210,7 @@ function interpolate_nodes(data_in::AbstractArray{T, 3}, interpolate_nodes!(data_out, data_in, vandermonde, n_vars) end + function interpolate_nodes!(data_out::AbstractArray{T, 3}, data_in::AbstractArray{T, 3}, vandermonde, n_vars) where T n_nodes_out = size(vandermonde, 1) diff --git a/src/vtktools.jl b/src/vtktools.jl index 5ee500b..a1700aa 100644 --- a/src/vtktools.jl +++ b/src/vtktools.jl @@ -1,6 +1,7 @@ -# Create and return VTK grids that are ready to be filled with data (vtu version) -function build_vtk_grids(::Val{:vtu}, mesh::TreeMesh, n_visnodes, verbose, - output_directory, is_datafile, filename) +# Create and return VTK grids on enriched uniform nodes +# that are ready to be filled with reinterpolated data (vtu version) +function build_vtk_grids(::Val{:vtu}, mesh::TreeMesh, nodes, n_visnodes, verbose, + output_directory, is_datafile, filename, reinterpolate::Val{true}) coordinates, levels, center_level_0, length_level_0 = extract_mesh_information(mesh) # Extract number of spatial dimensions @@ -43,9 +44,52 @@ function build_vtk_grids(::Val{:vtu}, mesh::TreeMesh, n_visnodes, verbose, end -# Create and return VTK grids that are ready to be filled with data (vti version) -function build_vtk_grids(::Val{:vti}, mesh, n_visnodes, verbose, - output_directory, is_datafile, filename) +# Create and return VTK grids with nodes on which the raw was created +# ready to be filled with raw data (vtu version) +function build_vtk_grids(::Val{:vtu}, mesh::TreeMesh, + nodes, n_visnodes, verbose, + output_directory, is_datafile, filename, reinterpolate::Val{false}) + + @timeit "prepare coordinate information" node_coordinates = calc_node_coordinates(mesh, nodes, n_visnodes) + + # Calculate VTK points and cells + verbose && println("| Preparing VTK cells...") + if is_datafile + @timeit "prepare VTK cells (node data)" begin + vtk_points, vtk_cells = calc_vtk_points_cells(node_coordinates) + end + end + + # Prepare VTK points and cells for celldata file + @timeit "prepare VTK cells (cell data)" begin + vtk_celldata_points, vtk_celldata_cells = calc_vtk_points_cells(node_coordinates) + end + + # Determine output file names + base, _ = splitext(splitdir(filename)[2]) + vtk_filename = joinpath(output_directory, base) + vtk_celldata_filename = vtk_filename * "_celldata" + + # Open VTK files + verbose && println("| Building VTK grid...") + if is_datafile + @timeit "build VTK grid (node data)" vtk_nodedata = vtk_grid(vtk_filename, vtk_points, + vtk_cells) + else + vtk_nodedata = nothing + end + @timeit "build VTK grid (cell data)" vtk_celldata = vtk_grid(vtk_celldata_filename, + vtk_celldata_points, + vtk_celldata_cells) + + return vtk_nodedata, vtk_celldata +end + + +# Create and return VTK grids on enriched uniform nodes +# that are ready to be filled with reinterpolated data (vti version) +function build_vtk_grids(::Val{:vti}, mesh::TreeMesh, nodes, n_visnodes, verbose, + output_directory, is_datafile, filename, reinterpolate::Val{true}) coordinates, levels, center_level_0, length_level_0 = extract_mesh_information(mesh) @@ -53,8 +97,11 @@ function build_vtk_grids(::Val{:vti}, mesh, n_visnodes, verbose, ndims_ = size(coordinates, 1) # Prepare VTK points and cells for celldata file - @timeit "prepare VTK cells" vtk_celldata_points, vtk_celldata_cells = calc_vtk_points_cells( - Val(ndims_), coordinates, levels, center_level_0, length_level_0, 1) + @timeit "prepare VTK cells (cell data)" begin + vtk_celldata_points, vtk_celldata_cells = calc_vtk_points_cells(Val(ndims_), coordinates, + levels, center_level_0, + length_level_0, 1) + end # Determine output file names base, _ = splitext(splitdir(filename)[2]) @@ -67,14 +114,14 @@ function build_vtk_grids(::Val{:vti}, mesh, n_visnodes, verbose, # Determine level-wise resolution max_level = maximum(levels) resolution = n_visnodes * 2^max_level + origin = @. center_level_0 - 0.5 * length_level_0 Nx = Ny = resolution + 1 dx = dy = length_level_0/resolution - origin = center_level_0 .- 1/2 * length_level_0 spacing = (dx, dy) @timeit "build VTK grid (node data)" vtk_nodedata = vtk_grid(vtk_filename, Nx, Ny, - origin=tuple(origin...), - spacing=spacing) + origin=tuple(origin...), + spacing=spacing) else vtk_nodedata = nothing end @@ -87,12 +134,14 @@ end # Create and return VTK grids that are ready to be filled with data -# (StructuredMesh/UnstructuredMesh2D/P4estMesh version) +# (StructuredMesh/UnstructuredMesh2D/P4estMesh version). +# Routine is agnostic with respect to reinterpolation. function build_vtk_grids(::Val{:vtu}, mesh::Union{StructuredMesh, UnstructuredMesh2D, P4estMesh}, - n_visnodes, verbose, output_directory, is_datafile, filename) + nodes, n_visnodes, verbose, output_directory, is_datafile, filename, + reinterpolate::Union{Val{true}, Val{false}}) - @timeit "prepare coordinate information" node_coordinates = calc_node_coordinates(mesh, n_visnodes) + @timeit "prepare coordinate information" node_coordinates = calc_node_coordinates(mesh, nodes, n_visnodes) # Calculate VTK points and cells verbose && println("| Preparing VTK cells...") @@ -128,12 +177,24 @@ function build_vtk_grids(::Val{:vtu}, end -function calc_node_coordinates(mesh::StructuredMesh, n_visnodes) +function calc_node_coordinates(mesh::TreeMesh, nodes, n_visnodes) + coordinates, levels, _, _ = extract_mesh_information(mesh) + + # Extract number of spatial dimensions + ndims_ = size(coordinates, 1) + n_elements = length(levels) + + node_coordinates = Array{Float64, ndims_+2}(undef, ndims_, ntuple(_ -> n_visnodes, ndims_)..., n_elements) + + return calc_node_coordinates!(node_coordinates, nodes, mesh) +end + + +function calc_node_coordinates(mesh::StructuredMesh, nodes, n_visnodes) # Extract number of spatial dimensions ndims_ = ndims(mesh) n_elements = prod(size(mesh)) - nodes = range(-1, 1, length=n_visnodes) f(args...; kwargs...) = Base.invokelatest(mesh.mapping, args...; kwargs...) node_coordinates = Array{Float64, ndims_+2}(undef, ndims_, ntuple(_ -> n_visnodes, ndims_)..., n_elements) @@ -142,14 +203,11 @@ function calc_node_coordinates(mesh::StructuredMesh, n_visnodes) end -function calc_node_coordinates(mesh::UnstructuredMesh2D, n_visnodes) +function calc_node_coordinates(mesh::UnstructuredMesh2D, nodes, n_visnodes) # Extract number of spatial dimensions ndims_ = ndims(mesh) n_elements = length(mesh) - # get the uniform nodes - nodes = range(-1, 1, length=n_visnodes) - # intialize the container for the node coordinates node_coordinates = Array{Float64, ndims_+2}(undef, ndims_, ntuple(_ -> n_visnodes, ndims_)..., n_elements) @@ -173,12 +231,10 @@ function calc_node_coordinates(mesh::UnstructuredMesh2D, n_visnodes) end -function calc_node_coordinates(mesh::P4estMesh, n_visnodes) +function calc_node_coordinates(mesh::P4estMesh, nodes, n_visnodes) # Extract number of spatial dimensions ndims_ = ndims(mesh) - nodes = range(-1, 1, length=n_visnodes) - node_coordinates = Array{Float64, ndims_+2}(undef, ndims_, ntuple(_ -> n_visnodes, ndims_)..., Trixi.ncells(mesh)) @@ -187,6 +243,96 @@ function calc_node_coordinates(mesh::P4estMesh, n_visnodes) end +# Calculation of the node coordinates for `TreeMesh` in 2D +function calc_node_coordinates!(node_coordinates, nodes, mesh::TreeMesh{2}) + _, levels, _, length_level_0 = extract_mesh_information(mesh) + + # Extract number of spatial dimensions + n_elements = length(levels) + + # Set the reference length + # TODO: Is this ever different from 2? + reference_length = 2.0 + # Compute the offset of the midpoint of the 1D reference interval + # (its difference from zero) + reference_offset = first(nodes) + reference_length / 2 + + # Recompute the cell ids + cell_ids = Trixi.local_leaf_cells(mesh.tree) + + # Calculate inverse Jacobian and node coordinates + for element in 1:n_elements + # Get cell id + cell_id = cell_ids[element] + + # Get cell length + dx = length_level_0 / 2^levels[element] + + # Get Jacobian value + jacobian = dx / reference_length + + # Calculate node coordinates + # Note that the `tree_coordinates` are the midpoints of the cells. + # Hence, we need to add an offset for `nodes` with a midpoint + # different from zero. + for j in eachindex(nodes), i in eachindex(nodes) + node_coordinates[1, i, j, element] = ( + mesh.tree.coordinates[1, cell_id] + jacobian * (nodes[i] - reference_offset)) + node_coordinates[2, i, j, element] = ( + mesh.tree.coordinates[2, cell_id] + jacobian * (nodes[j] - reference_offset)) + end + end + + return node_coordinates +end + + +# Calculation of the node coordinates for `TreeMesh` in 3D +function calc_node_coordinates!(node_coordinates, nodes, mesh::TreeMesh{3}) + _, levels, _, length_level_0 = extract_mesh_information(mesh) + + # Extract number of spatial dimensions + n_elements = length(levels) + + # Set the reference length + # TODO: Is this ever different from 2? + reference_length = 2.0 + # Compute the offset of the midpoint of the 1D reference interval + # (its difference from zero) + reference_offset = first(nodes) + reference_length / 2 + + # Recompute the cell ids + cell_ids = Trixi.local_leaf_cells(mesh.tree) + + # Calculate inverse Jacobian and node coordinates + for element in 1:n_elements + # Get cell id + cell_id = cell_ids[element] + + # Get cell length + dx = length_level_0 / 2^levels[element] + + # Get Jacobian value + jacobian = dx / reference_length + + # Calculate node coordinates + # Note that the `tree_coordinates` are the midpoints of the cells. + # Hence, we need to add an offset for `nodes` with a midpoint + # different from zero. + for k in eachindex(nodes), j in eachindex(nodes), i in eachindex(nodes) + node_coordinates[1, i, j, k, element] = ( + mesh.tree.coordinates[1, cell_id] + jacobian * (nodes[i] - reference_offset)) + node_coordinates[2, i, j, k, element] = ( + mesh.tree.coordinates[2, cell_id] + jacobian * (nodes[j] - reference_offset)) + node_coordinates[3, i, j, k, element] = ( + mesh.tree.coordinates[3, cell_id] + jacobian * (nodes[k] - reference_offset)) + end + end + + return node_coordinates +end + + # Calculation of the node coordinates for `StructuredMesh` in 2D function calc_node_coordinates!(node_coordinates::AbstractArray{<:Any, 4}, f, nodes, mesh) linear_indices = LinearIndices(size(mesh)) diff --git a/test/Project.toml b/test/Project.toml index b2d49ec..05a0bad 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -2,11 +2,12 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" +ReadVTK = "dc215faf-f008-4882-a9f7-a79a826fadc3" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Trixi = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb" [compat] Documenter = "0.27" OrdinaryDiffEq = "6" +ReadVTK = "0.1" Trixi = "0.4" diff --git a/test/test_2d.jl b/test/test_2d.jl index a12ef06..8c0a0ce 100644 --- a/test/test_2d.jl +++ b/test/test_2d.jl @@ -15,169 +15,390 @@ if !isdir(artifacts_dir) mkdir(artifacts_dir) end - @testset "2D" begin @testset "TreeMesh" begin isdir(outdir) && rm(outdir, recursive=true) - run_trixi(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_advection_extended.jl"), maxiters=1) - - @timed_testset "uniform mesh" begin - if Sys.iswindows() - # This test fails on Windows due to globbing not working - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "1ec2c93c0c9c4f4992dea54afaf2a348ece0160e"), - ("solution_000000_celldata.vtu", "e396c3ba63276347966d4264cf0f52d592221830")]) - outfiles = ("solution_000000.vtu", "solution_000000_celldata.vtu") - - else - test_trixi2vtk("solution_00000*.h5", outdir, - hashes=[("solution_000000.vtu", "1ec2c93c0c9c4f4992dea54afaf2a348ece0160e"), - ("solution_000000_celldata.vtu", "e396c3ba63276347966d4264cf0f52d592221830"), - ("solution_00000.pvd", "b9e6742dc2b397b14d8d3964e90dcfadea5d98cb"), - ("solution_00000_celldata.pvd", "c12004714a980581450cd4bad16a2541c5ec8f26")]) - outfiles = ("solution_000000.vtu", "solution_000000_celldata.vtu", - "solution_00000.pvd", "solution_00000_celldata.pvd") - end - @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5")) - - # Store output files as artifacts to facilitate debugging of failing tests - testname = "2d-tree-mesh-uniform-mesh" - for outfile in outfiles - println("Copying '", abspath(joinpath(outdir, outfile)), - "' to '", abspath(joinpath(artifacts_dir, testname * "-" * outfile)), - "'...") - cp(joinpath(outdir, outfile), joinpath(artifacts_dir, testname * "-" * outfile), force=true) - end + run_trixi(joinpath(examples_dir(), "tree_2d_dgsem", "elixir_euler_sedov_blast_wave.jl"), maxiters=10) + + @timed_testset "mesh data" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "mesh_000010.h5"), output_directory=outdir) + outfilename = "mesh_000010_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-treemesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/treemesh/dgsem_sedov_amr_mesh_10.vtu" + ref_file = get_test_reference_file("dgsem_sedov_amr_mesh_10.vtu", remote_filename) + compare_cell_data(out_file, ref_file) end - @timed_testset "uniform mesh with vti output" begin - if Sys.isapple() - # This test fails on MacOS due to differing binary VTI files (even though the contents match) - test_trixi2vtk("restart_000001.h5", outdir, - format=:vti) - else - test_trixi2vtk("restart_000001.h5", outdir, - hashes=[("restart_000001.vti", "9ade067c71f1f6492242a8aa215bd0d633caf9bc"), - ("restart_000001_celldata.vtu", "e396c3ba63276347966d4264cf0f52d592221830")], - format=:vti) - end - - # Store output files as artifacts to facilitate debugging of failing tests - outfiles = ("restart_000001.vti", "restart_000001_celldata.vtu") - testname = "2d-tree-mesh-uniform-mesh-with-vti-output" - for outfile in outfiles - println("Copying '", abspath(joinpath(outdir, outfile)), - "' to '", abspath(joinpath(artifacts_dir, testname * "-" * outfile)), - "'...") - cp(joinpath(outdir, outfile), joinpath(artifacts_dir, testname * "-" * outfile), force=true) - end + @timed_testset "solution celldata" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000010.h5"), output_directory=outdir) + outfilename = "solution_000010_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-treemesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/treemesh/dgsem_sedov_amr_celldata_10.vtu" + ref_file = get_test_reference_file("dgsem_sedov_amr_celldata_10.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "reinterpolate with nonuniform data with VTU format" begin + # Create and test output with reinterpolation (default options: `reinterpolate=true, data_is_uniform=false`) + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000010.h5"), output_directory=outdir) + outfilename = "solution_000010.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-treemesh-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/treemesh/dgsem_sedov_amr_reinterp_10.vtu" + ref_file = get_test_reference_file("dgsem_sedov_amr_reinterp_10.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "reinterpolate with nonuniform data with VTI format" begin + # Create and test output with reinterpolation (default options: `reinterpolate=true, data_is_uniform=false`) + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000010.h5"), output_directory=outdir, format=:vti) + outfilename = "solution_000010.vti" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-treemesh-vti-format" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/treemesh/dgsem_sedov_amr_10.vti" + ref_file = get_test_reference_file("dgsem_sedov_amr_10.vti", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with nonuniform data" begin + # Create and test output without reinterpolation on LGL nodes + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000010.h5"), output_directory=outdir, reinterpolate=false) + outfilename = "solution_000010.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-treemesh-no-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/treemesh/dgsem_sedov_amr_no_reinterp_10.vtu" + ref_file = get_test_reference_file("dgsem_sedov_amr_no_reinterp_10.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with uniform data" begin + # Create and test output without reinterpolation on uniform nodes + # OBS! This is a dummy test just to exercise code. The resulting plot will look weird. + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000010.h5"), output_directory=outdir, reinterpolate=false, data_is_uniform=true) + outfilename = "solution_000010.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-treemesh-no-reinterp-uniform" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/treemesh/dgsem_sedov_amr_no_reinterp_uniform_10.vtu" + ref_file = get_test_reference_file("dgsem_sedov_amr_no_reinterp_uniform_10.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "attempt reinterpolate with uniform data" begin + # Purposely request a bad configuration and check that an error message gets thrown + # OBS! Only needs tested once across all mesh types and dimensions + @test_throws ArgumentError trixi2vtk(joinpath(outdir, "solution_000010.h5"), output_directory=outdir, data_is_uniform=true) end end - @testset "StructuredMesh" begin - isdir(outdir) && rm(outdir, recursive=true) - run_trixi(joinpath(examples_dir(), "structured_2d_dgsem", "elixir_advection_waving_flag.jl"), maxiters=1) - - @timed_testset "waving flag" begin - if Sys.isapple() - # This file has a different hash on macOS for some reason - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "a93dbd393647627a861d890568e65598be0062f9")]) - else - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "564701ed0a9a90230f3a67f8bddd0616c818319b")]) - end - @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5")) - - # Store output files as artifacts to facilitate debugging of failing tests - outfiles = ("solution_000000.vtu",) - testname = "2d-curved-mesh-waving-flag" - for outfile in outfiles - println("Copying '", abspath(joinpath(outdir, outfile)), - "' to '", abspath(joinpath(artifacts_dir, testname * "-" * outfile)), - "'...") - cp(joinpath(outdir, outfile), joinpath(artifacts_dir, testname * "-" * outfile), force=true) + + if !Sys.iswindows() && get(ENV, "CI", nothing) == "true" + # OBS! Only `TreeMesh` results are tested on Windows runners due to memory limits. + # All remaining mesh types are tested on Ubuntu and Mac + @testset "StructuredMesh" begin + isdir(outdir) && rm(outdir, recursive=true) + run_trixi(joinpath(examples_dir(), "structured_2d_dgsem", "elixir_advection_waving_flag.jl"), maxiters=1) + + @timed_testset "mesh data" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5"), output_directory=outdir) + outfilename = "mesh_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-structuredmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/structuredmesh/dgsem_adv_mesh_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_mesh_01.vtu", remote_filename) + compare_cell_data(out_file, ref_file) end - end - @timed_testset "waving flag (supersampling)" begin - if Sys.isapple() - # This file has a different hash on macOS for some reason - test_trixi2vtk("solution_000000.h5", outdir, nvisnodes=6, - hashes=[("solution_000000.vtu", "c6d74ab831bf4b6de2ba8cf537b6653ad611cfe7")]) - else - test_trixi2vtk("solution_000000.h5", outdir, nvisnodes=6, - hashes=[("solution_000000.vtu", "ae8c059c110aaabe2ed7dcfa8516d336c15ba618")]) - end - - # Store output files as artifacts to facilitate debugging of failing tests - outfiles = ("solution_000000.vtu",) - testname = "2d-curved-mesh-waving-flag-supersampling" - for outfile in outfiles - println("Copying '", abspath(joinpath(outdir, outfile)), - "' to '", abspath(joinpath(artifacts_dir, testname * "-" * outfile)), - "'...") - cp(joinpath(outdir, outfile), joinpath(artifacts_dir, testname * "-" * outfile), force=true) + @timed_testset "solution celldata" begin + # create the output file to be tested + # OBS! This exercises passing multiple files (in this case 2 files) to `trixi2vtk` + # that only needs to be tested once. + @test_nowarn trixi2vtk(joinpath(outdir, "solution_00000*.h5"), output_directory=outdir) + + outfilename = "solution_000001_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-structuredmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/structuredmesh/dgsem_adv_celldata_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_celldata_01.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "reinterpolate with nonuniform data" begin + # Create and test output with reinterpolation (default options: `reinterpolate=true, data_is_uniform=false`) + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir) + outfilename = "solution_000001.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-structuredmesh-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/structuredmesh/dgsem_adv_reinterp_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_reinterp_01.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with nonuniform data" begin + # Create and test output without reinterpolation on LGL nodes + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir, reinterpolate=false) + outfilename = "solution_000001.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-structuredmesh-no-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/structuredmesh/dgsem_adv_no_reinterp_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_no_reinterp_01.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with uniform data" begin + # Create and test output without reinterpolation on uniform nodes + # OBS! This is a dummy test just to exercise code. The resulting plot will look weird. + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir, reinterpolate=false, data_is_uniform=true) + outfilename = "solution_000001.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-structuredmesh-no-reinterp-uniform" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/structuredmesh/dgsem_adv_no_reinterp_uniform_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_no_reinterp_uniform_01.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "attempt VTI format on unsupportd mesh type" begin + # Purposely request a bad configuration and check that an error message gets thrown + # OBS! Only needs tested once across all mesh types and dimensions + @test_throws ArgumentError trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir, format=:vti) end end - end - @testset "UnstructuredMesh2D" begin - isdir(outdir) && rm(outdir, recursive=true) - run_trixi(joinpath(examples_dir(), "unstructured_2d_dgsem", "elixir_euler_basic.jl"), maxiters=1) - - @timed_testset "basic" begin - if Sys.isapple() - # This file has a different hash on macOS for some reason - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "0daedeea99d03d53b925ce5691bd7924abe88861")]) - else - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "acc03f295d2b7daee2cb0b4e29b90035c5e92bd7")]) - end - @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5")) - - # Store output files as artifacts to facilitate debugging of failing tests - outfiles = ("solution_000000.vtu",) - testname = "2d-unstructured-quad-basic" - for outfile in outfiles - println("Copying '", abspath(joinpath(outdir, outfile)), - "' to '", abspath(joinpath(artifacts_dir, testname * "-" * outfile)), - "'...") - cp(joinpath(outdir, outfile), joinpath(artifacts_dir, testname * "-" * outfile), force=true) + @testset "UnstructuredMesh2D" begin + isdir(outdir) && rm(outdir, recursive=true) + run_trixi(joinpath(examples_dir(), "unstructured_2d_dgsem", "elixir_shallowwater_ec.jl"), maxiters=1) + + @timed_testset "mesh data" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5"), output_directory=outdir) + outfilename = "mesh_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-unstructuredmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/unstructuredmesh/dgsem_swe_mesh_01.vtu" + ref_file = get_test_reference_file("dgsem_swe_mesh_01.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "solution celldata" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir) + outfilename = "solution_000001_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-unstructuredmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/unstructuredmesh/dgsem_swe_celldata_01.vtu" + ref_file = get_test_reference_file("dgsem_swe_celldata_01.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "reinterpolate with nonuniform data" begin + # Create and test output with reinterpolation (default options: `reinterpolate=true, data_is_uniform=false`) + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir) + outfilename = "solution_000001.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-unstructuredmesh-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/unstructuredmesh/dgsem_swe_reinterp_01.vtu" + ref_file = get_test_reference_file("dgsem_swe_reinterp_01.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with nonuniform data" begin + # Create and test output without reinterpolation on LGL nodes + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir, reinterpolate=false) + outfilename = "solution_000001.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-unstructuredmesh-no-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/unstructuredmesh/dgsem_swe_no_reinterp_01.vtu" + ref_file = get_test_reference_file("dgsem_swe_no_reinterp_01.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with uniform data" begin + # Create and test output without reinterpolation on uniform nodes + # OBS! This is a dummy test just to exercise code. The resulting plot will look weird. + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir, reinterpolate=false, data_is_uniform=true) + outfilename = "solution_000001.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-unstructuredmesh-no-reinterp-uniform" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/unstructuredmesh/dgsem_swe_no_reinterp_uniform_01.vtu" + ref_file = get_test_reference_file("dgsem_swe_no_reinterp_uniform_01.vtu", remote_filename) + compare_point_data(out_file, ref_file) end end - end - @testset "P4estMesh" begin - isdir(outdir) && rm(outdir, recursive=true) - run_trixi(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_euler_source_terms_nonconforming_unstructured_flag.jl"), initial_refinement_level=0, maxiters=1) - - @timed_testset "nonperiodic" begin - if Sys.isapple() - # This file has a different hash on macOS for some reason - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "476eaaa9c13c3a8dba826b614a68a9d3e7979e6b")]) - else - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "a80aadb353ce6ec40baa1b94d278c480f17d0419")]) - end - @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5")) - - # Store output files as artifacts to facilitate debugging of failing tests - outfiles = ("solution_000000.vtu",) - testname = "2d-p4est-mesh-nonperiodic" - for outfile in outfiles - println("Copying '", abspath(joinpath(outdir, outfile)), - "' to '", abspath(joinpath(artifacts_dir, testname * "-" * outfile)), - "'...") - cp(joinpath(outdir, outfile), joinpath(artifacts_dir, testname * "-" * outfile), force=true) + @testset "P4estMesh" begin + isdir(outdir) && rm(outdir, recursive=true) + run_trixi(joinpath(examples_dir(), "p4est_2d_dgsem", "elixir_mhd_rotor.jl"), maxiters=5) + + @timed_testset "mesh data" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "mesh_000005.h5"), output_directory=outdir) + outfilename = "mesh_000005_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-p4estmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/p4estmesh/dgsem_rotor_amr_mesh_05.vtu" + ref_file = get_test_reference_file("dgsem_rotor_amr_mesh_05.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "solution celldata" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000005.h5"), output_directory=outdir) + outfilename = "solution_000005_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-p4estmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/p4estmesh/dgsem_rotor_amr_celldata_05.vtu" + ref_file = get_test_reference_file("dgsem_rotor_amr_celldata_05.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "reinterpolate with nonuniform data" begin + # Create and test output with reinterpolation (default options: `reinterpolate=true, data_is_uniform=false`) + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000005.h5"), output_directory=outdir) + outfilename = "solution_000005.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-p4estmesh-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/p4estmesh/dgsem_rotor_amr_reinterp_05.vtu" + ref_file = get_test_reference_file("dgsem_rotor_amr_reinterp_05.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with nonuniform data" begin + # Create and test output without reinterpolation on LGL nodes + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000005.h5"), output_directory=outdir, reinterpolate=false) + outfilename = "solution_000005.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-p4estmesh-no-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/p4estmesh/dgsem_rotor_amr_no_reinterp_05.vtu" + ref_file = get_test_reference_file("dgsem_rotor_amr_no_reinterp_05.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with uniform data" begin + # Create and test output without reinterpolation on uniform nodes + # OBS! This is a dummy test just to exercise code. The resulting plot will look weird. + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000005.h5"), output_directory=outdir, reinterpolate=false, data_is_uniform=true) + outfilename = "solution_000005.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "2d-p4estmesh-no-reinterp-uniform" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "2d/p4estmesh/dgsem_rotor_amr_no_reinterp_uniform_05.vtu" + ref_file = get_test_reference_file("dgsem_rotor_amr_no_reinterp_uniform_05.vtu", remote_filename) + compare_point_data(out_file, ref_file) end end end end -# Clean up afterwards: delete Trixi output directory +# Clean up afterwards: delete Trixi output directory and reference file directory @test_nowarn rm(outdir, recursive=true) +@test_nowarn rm(TEST_REFERENCE_DIR, recursive=true) end diff --git a/test/test_3d.jl b/test/test_3d.jl index 76b9a68..e7217a8 100644 --- a/test/test_3d.jl +++ b/test/test_3d.jl @@ -15,87 +15,272 @@ if !isdir(artifacts_dir) mkdir(artifacts_dir) end - @testset "3D" begin @testset "TreeMesh" begin isdir(outdir) && rm(outdir, recursive=true) - run_trixi(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_advection_extended.jl"), maxiters=1) - - @timed_testset "uniform mesh" begin - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "6ab3aa525851187ee0839e1d670a254a66be4ad7"), - ("solution_000000_celldata.vtu", "fdfee2d4200ecdad08067b37908412813016f4e7")]) - @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5")) - - # Store output files as artifacts to facilitate debugging of failing tests - outfiles = ("solution_000000.vtu", "solution_000000_celldata.vtu") - testname = "3d-tree-mesh-uniform-mesh" - for outfile in outfiles - println("Copying '", abspath(joinpath(outdir, outfile)), - "' to '", abspath(joinpath(artifacts_dir, testname * "-" * outfile)), - "'...") - cp(joinpath(outdir, outfile), joinpath(artifacts_dir, testname * "-" * outfile), force=true) - end + run_trixi(joinpath(examples_dir(), "tree_3d_dgsem", "elixir_euler_blob_amr.jl"), maxiters=4) + + @timed_testset "mesh data" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "mesh_000004.h5"), output_directory=outdir) + outfilename = "mesh_000004_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-treemesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/treemesh/dgsem_blob_amr_mesh_04.vtu" + ref_file = get_test_reference_file("dgsem_blob_amr_mesh_04.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "solution celldata" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000004.h5"), output_directory=outdir) + outfilename = "solution_000004_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-treemesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/treemesh/dgsem_blob_amr_celldata_04.vtu" + ref_file = get_test_reference_file("dgsem_blob_amr_celldata_04.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "reinterpolate with nonuniform data" begin + # Create and test output with reinterpolation (default options: `reinterpolate=true, data_is_uniform=false`) + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000004.h5"), output_directory=outdir) + outfilename = "solution_000004.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-treemesh-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/treemesh/dgsem_blob_amr_reinterp_04.vtu" + ref_file = get_test_reference_file("dgsem_blob_amr_reinterp_04.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with nonuniform data" begin + # Create and test output without reinterpolation on LGL nodes + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000004.h5"), output_directory=outdir, reinterpolate=false) + outfilename = "solution_000004.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-treemesh-no-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/treemesh/dgsem_blob_amr_no_reinterp_04.vtu" + ref_file = get_test_reference_file("dgsem_blob_amr_no_reinterp_04.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with uniform data" begin + # Create and test output without reinterpolation on uniform nodes + # OBS! This is a dummy test just to exercise code. The resulting plot will look weird. + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000004.h5"), output_directory=outdir, reinterpolate=false, data_is_uniform=true) + outfilename = "solution_000004.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-treemesh-no-reinterp-uniform" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/treemesh/dgsem_blob_amr_no_reinterp_uniform_04.vtu" + ref_file = get_test_reference_file("dgsem_blob_amr_no_reinterp_uniform_04.vtu", remote_filename) + compare_point_data(out_file, ref_file) end end - @testset "StructuredMesh" begin - isdir(outdir) && rm(outdir, recursive=true) - run_trixi(joinpath(examples_dir(), "structured_3d_dgsem", "elixir_advection_basic.jl"), maxiters=1) - - @timed_testset "basic" begin - if Sys.isapple() - # This file has a different hash on macOS for some reason - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "58e07f981fd6c005ea17e47054bd509c2c66d771")]) - else - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "58e07f981fd6c005ea17e47054bd509c2c66d771")]) + if !Sys.iswindows() && get(ENV, "CI", nothing) == "true" + # OBS! Only `TreeMesh` results are tested on Windows runners due to memory limits. + # All remaining mesh types are tested on Ubuntu and Mac + @testset "StructuredMesh" begin + isdir(outdir) && rm(outdir, recursive=true) + run_trixi(joinpath(examples_dir(), "structured_3d_dgsem", "elixir_advection_nonperiodic_curved.jl"), maxiters=1) + + @timed_testset "mesh data" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5"), output_directory=outdir) + outfilename = "mesh_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-structuredmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/structuredmesh/dgsem_adv_mesh_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_mesh_01.vtu", remote_filename) + compare_cell_data(out_file, ref_file) end - @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5")) - - # Store output files as artifacts to facilitate debugging of failing tests - outfiles = ("solution_000000.vtu",) - testname = "3d-curved-mesh-basic" - for outfile in outfiles - println("Copying '", abspath(joinpath(outdir, outfile)), - "' to '", abspath(joinpath(artifacts_dir, testname * "-" * outfile)), - "'...") - cp(joinpath(outdir, outfile), joinpath(artifacts_dir, testname * "-" * outfile), force=true) + + @timed_testset "solution celldata" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir) + outfilename = "solution_000001_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-structuredmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/structuredmesh/dgsem_adv_celldata_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_celldata_01.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "reinterpolate with nonuniform data" begin + # Create and test output with reinterpolation (default options: `reinterpolate=true, data_is_uniform=false`) + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir) + outfilename = "solution_000001.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-structuredmesh-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/structuredmesh/dgsem_adv_reinterp_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_reinterp_01.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with nonuniform data" begin + # Create and test output without reinterpolation on LGL nodes + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir, reinterpolate=false) + outfilename = "solution_000001.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-structuredmesh-no-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/structuredmesh/dgsem_adv_no_reinterp_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_no_reinterp_01.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with uniform data" begin + # Create and test output without reinterpolation on uniform nodes + # OBS! This is a dummy test just to exercise code. The resulting plot will look weird. + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000001.h5"), output_directory=outdir, reinterpolate=false, data_is_uniform=true) + outfilename = "solution_000001.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-structuredmesh-no-reinterp-uniform" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/structuredmesh/dgsem_adv_no_reinterp_uniform_01.vtu" + ref_file = get_test_reference_file("dgsem_adv_no_reinterp_uniform_01.vtu", remote_filename) + compare_point_data(out_file, ref_file) end end - end - @testset "P4estMesh" begin - isdir(outdir) && rm(outdir, recursive=true) - run_trixi(joinpath(examples_dir(), "p4est_3d_dgsem", "elixir_advection_amr_unstructured_curved.jl"), maxiters=1) - - @timed_testset "unstructured curved" begin - if Sys.isapple() - # This file has a different hash on macOS for some reason - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "fe0f45809ef6f3f0c1b7f5331198585f406923c9")]) - else - test_trixi2vtk("solution_000000.h5", outdir, - hashes=[("solution_000000.vtu", "0fa5a099378d153aa3a1bb7dcf3559ea5d6bf9c5")]) + @testset "P4estMesh" begin + isdir(outdir) && rm(outdir, recursive=true) + run_trixi(joinpath(examples_dir(), "p4est_3d_dgsem", "elixir_advection_cubed_sphere.jl"), maxiters=2) + + @timed_testset "mesh data" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5"), output_directory=outdir) + outfilename = "mesh_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-p4estmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/p4estmesh/dgsem_adv_sphere_mesh_02.vtu" + ref_file = get_test_reference_file("dgsem_adv_sphere_mesh_02.vtu", remote_filename) + compare_cell_data(out_file, ref_file) + end + + @timed_testset "solution celldata" begin + # create the output file to be tested + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000002.h5"), output_directory=outdir) + outfilename = "solution_000002_celldata.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-p4estmesh" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/p4estmesh/dgsem_adv_sphere_celldata_02.vtu" + ref_file = get_test_reference_file("dgsem_adv_sphere_celldata_02.vtu", remote_filename) + compare_cell_data(out_file, ref_file) end - @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5")) - - # Store output files as artifacts to facilitate debugging of failing tests - outfiles = ("solution_000000.vtu",) - testname = "3d-p4est-mesh-unstructured-curved" - for outfile in outfiles - println("Copying '", abspath(joinpath(outdir, outfile)), - "' to '", abspath(joinpath(artifacts_dir, testname * "-" * outfile)), - "'...") - cp(joinpath(outdir, outfile), joinpath(artifacts_dir, testname * "-" * outfile), force=true) + + @timed_testset "reinterpolate with nonuniform data" begin + # Create and test output with reinterpolation (default options: `reinterpolate=true, data_is_uniform=false`) + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000002.h5"), output_directory=outdir) + outfilename = "solution_000002.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-p4estmesh-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/p4estmesh/dgsem_adv_sphere_reinterp_02.vtu" + ref_file = get_test_reference_file("dgsem_adv_sphere_reinterp_02.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with nonuniform data" begin + # Create and test output without reinterpolation on LGL nodes + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000002.h5"), output_directory=outdir, reinterpolate=false) + outfilename = "solution_000002.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-p4estmesh-no-reinterp" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/p4estmesh/dgsem_adv_sphere_no_reinterp_02.vtu" + ref_file = get_test_reference_file("dgsem_adv_sphere_no_reinterp_02.vtu", remote_filename) + compare_point_data(out_file, ref_file) + end + + @timed_testset "do not reinterpolate with uniform data" begin + # Create and test output without reinterpolation on uniform nodes + # OBS! This is a dummy test just to exercise code. The resulting plot will look weird. + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000002.h5"), output_directory=outdir, reinterpolate=false, data_is_uniform=true) + outfilename = "solution_000002.vtu" + out_file = joinpath(outdir, outfilename) + + # save output file to `artifacts` to facilitate debugging of failing tests + testname = "3d-p4estmesh-no-reinterp-uniform" + cp(out_file, joinpath(artifacts_dir, testname * "-" * outfilename), force=true) + + # remote file path is actually a URL so it always has the same path structure + remote_filename = "3d/p4estmesh/dgsem_adv_sphere_no_reinterp_uniform_02.vtu" + ref_file = get_test_reference_file("dgsem_adv_sphere_no_reinterp_uniform_02.vtu", remote_filename) + compare_point_data(out_file, ref_file) end end end end -# Clean up afterwards: delete Trixi output directory -@test_skip rm(outdir, recursive=true) +# Clean up afterwards: delete Trixi output directory and reference file directory +@test_nowarn rm(outdir, recursive=true) +@test_nowarn rm(TEST_REFERENCE_DIR, recursive=true) end - diff --git a/test/test_manual.jl b/test/test_manual.jl index bb05478..a051ee4 100644 --- a/test/test_manual.jl +++ b/test/test_manual.jl @@ -39,6 +39,12 @@ isdir(outdir) && rm(outdir, recursive=true) @test_nowarn trixi2vtk(joinpath(outdir, "mesh.h5"); output_directory=outdir) end + @testset "trixi2vtk set number of output nodes" begin + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000000.h5"); nvisnodes=0) + + @test_nowarn trixi2vtk(joinpath(outdir, "solution_000000.h5"); nvisnodes=5) + end + @timed_testset "pvd_filenames" begin @test Trixi2Vtk.pvd_filenames("", "manual", "out") == (joinpath("out", "manual"), joinpath("out", "manual_celldata")) @test_throws ErrorException Trixi2Vtk.pvd_filenames(("a", "b"), nothing, "out") diff --git a/test/test_trixi2vtk.jl b/test/test_trixi2vtk.jl index 6b1b3b9..b9aa8e6 100644 --- a/test/test_trixi2vtk.jl +++ b/test/test_trixi2vtk.jl @@ -1,7 +1,8 @@ -using Test: @test_nowarn, @test, @testset, @test_skip -using SHA +using Test: @test_nowarn, @test, @testset +using Downloads: download using Trixi using Trixi2Vtk +using ReadVTK function run_trixi(elixir; kwargs...) @@ -10,25 +11,159 @@ function run_trixi(elixir; kwargs...) end -function sha1file(filename) - open(filename) do f - bytes2hex(sha1(f)) +""" + get_reference_file(filename, remotename; head="main", output_directory=".", force=false) + +Retrieve a reference file from the +[`Trixi2Vtk_reference_files` repository](https://github.com/trixi-framework/Trixi2Vtk_reference_files) +at commit/branch `head` and store it in the `output_directory`. If the file already +exists locally, do not download the file again unless `force` is true. +Provide the `remotename` because the file structure in the `Trixi2Vtk_reference_files` repository +is different than what will be created locally. Return the local path to the downloaded file. +""" +function get_reference_file(filename, remotename; head="main", output_directory=".", force=false) + filepath = joinpath(output_directory, filename) + if !isfile(filepath) || force + url = ("https://github.com/trixi-framework/Trixi2Vtk_reference_files/raw/" + * head + * "/reference_files/" + * remotename) + download(url, filepath) end + + return filepath end +# Commit in the reference file repository for which the test files will be downloaded +# Note: The purpose of using a specific commit hash (instead of `main`) is to be able to tie a given +# version of Trixi2Vtk to a specific version of the test file repository. This way, also tests +# for older Trixi2Vtk releases should continue to work. +const TEST_REFERENCE_COMMIT = "c0a966b06489f9b2ee3aefeb0a5c0dae733df36f" + +# Local folder to store downloaded reference files. If you change this, also adapt `../.gitignore`! +const TEST_REFERENCE_DIR = "reference_files" + + +get_test_reference_file(filename, remotename) = get_reference_file(filename, remotename, + head=TEST_REFERENCE_COMMIT, + output_directory=TEST_REFERENCE_DIR) + + +# Start with a clean environment: remove reference file directory if it exists +isdir(TEST_REFERENCE_DIR) && rm(TEST_REFERENCE_DIR, recursive=true) +mkpath(TEST_REFERENCE_DIR) + + +""" + compare_cell_data(out_filename, ref_filename; atol=500*eps(), rtol=sqrt(eps())) + +Test values from the VTK file header and acutal (possibly interpolated) cell data. Uses +`out_filename` created during testing and compares against `ref_filename` that comes +from the +[`Trixi2Vtk_reference_files` repository](https://github.com/trixi-framework/Trixi2Vtk_reference_files). +""" +function compare_cell_data(out_filename, ref_filename; atol=500*eps(), rtol=sqrt(eps())) + ref_vtk = VTKFile(ref_filename) + vtk = VTKFile(out_filename) + + # Compare header information + @test vtk.byte_order == ref_vtk.byte_order + @test vtk.compressor == ref_vtk.compressor + @test vtk.file_type == ref_vtk.file_type + @test vtk.version == ref_vtk.version + + # check that the number of cells and points match + @test vtk.n_cells == ref_vtk.n_cells + @test vtk.n_points == ref_vtk.n_points + + # Compare data information -function test_trixi2vtk(filenames, outdir; hashes=nothing, kwargs...) - @test_nowarn trixi2vtk(joinpath(outdir, filenames); output_directory=outdir, kwargs...) + # extract the data sets + out_cell_data = get_cell_data(vtk) + ref_cell_data = get_cell_data(ref_vtk) - if !isnothing(hashes) - for (filename, hash_expected) in hashes - hash_measured = sha1file(joinpath(outdir, filename)) - @test_skip hash_expected == hash_measured - end + # check that the number of variables and their names match + @test length(out_cell_data.names) == length(ref_cell_data.names) + @test out_cell_data.names == ref_cell_data.names + + # Note!! + # Occassionally, for the last equation variable there is an issue + # that the size of the data extracted with + # [`ReadVTK.jl`](https://github.com/trixi-framework/ReadVTK.jl) + # is one byte larger than expected. Somehow this is related to + # the zlib format and/or how + # [`WriteVTK.jl`](https://github.com/jipolanco/WriteVTK.jl) created + # the file in the first place. Even though there is this access error, + # the `vtu` file is valid and can be used with ParaView or VisIt without issue. + # Therefore, we check all the variable data except for the last one, + # unless the file only has a single variable stored as cell data. + eqn_check_number = length(ref_cell_data.names) == 1 ? length(ref_cell_data.names) : length(ref_cell_data.names)-1 + + # check that the actual plot data is (approximately) the same + for i in 1:eqn_check_number + variable_name = ref_cell_data.names[i] + out_data = get_data(out_cell_data[variable_name]) + ref_data = get_data(ref_cell_data[variable_name]) + @test isapprox(out_data, ref_data, atol=atol, rtol=rtol) end end +""" + compare_point_data(out_filename, ref_filename; atol=500*eps(), rtol=sqrt(eps())) + +Test values from the VTK file header and acutal (possibly interpolated) point data. Uses +`out_filename` created during testing and compares against `ref_filename` that comes +from the +[`Trixi2Vtk_reference_files` repository](https://github.com/trixi-framework/Trixi2Vtk_reference_files). +""" +function compare_point_data(out_filename, ref_filename; atol=500*eps(), rtol=sqrt(eps())) + # Load the data from both files + ref_vtk = VTKFile(ref_filename) + vtk = VTKFile(out_filename) + + # Compare header information + @test vtk.byte_order == ref_vtk.byte_order + @test vtk.compressor == ref_vtk.compressor + @test vtk.file_type == ref_vtk.file_type + @test vtk.version == ref_vtk.version + + # check that the number of cells and points match + @test vtk.n_cells == ref_vtk.n_cells + @test vtk.n_points == ref_vtk.n_points + + # Compare data information + + # extract the data sets + out_point_data = get_point_data(vtk) + ref_point_data = get_point_data(ref_vtk) + + # check that the number of variables and their names match + @test length(out_point_data.names) == length(ref_point_data.names) + @test out_point_data.names == ref_point_data.names + + # Note!! + # Occassionally, for the last equation variable there is an issue + # that the size of the data extracted with + # [`ReadVTK.jl`](https://github.com/trixi-framework/ReadVTK.jl) + # is one byte larger than expected. Somehow this is related to + # the zlib format and/or how + # [`WriteVTK.jl`](https://github.com/jipolanco/WriteVTK.jl) created + # the file in the first place. Even though there is this access error, + # the `vtu` file is valid and can be used with ParaView or VisIt without issue. + # Therefore, we check all the variable data except for the last one. + + eqn_check_number = length(ref_point_data.names) == 1 ? length(ref_point_data.names) : length(ref_point_data.names)-1 + + # check that the actual plot data is (approximately) the same + for i in 1:eqn_check_number + variable_name = ref_point_data.names[i] + out_data = get_data(out_point_data[variable_name]) + ref_data = get_data(ref_point_data[variable_name]) + @test isapprox(out_data, ref_data, atol=atol, rtol=rtol) + end +end + """ @timed_testset "name of the testset" #= code to test #=