Skip to content

Commit

Permalink
Make close actually save VTK files (#149)
Browse files Browse the repository at this point in the history
* Explicitly define Base.close (and isopen, show)

* Add finalisers to XMLDocument objects

Allow the GC to free LightXML memory when the parent object (<:VTKFile)
is destroyed.

* Make Base.close equivalent to vtk_save

* Prefer close to vtk_save (part 1)

* Prefer `close` in docs

* Fix tests?
  • Loading branch information
jipolanco authored Sep 18, 2024
1 parent dc4c34c commit f60e984
Show file tree
Hide file tree
Showing 14 changed files with 74 additions and 45 deletions.
2 changes: 1 addition & 1 deletion docs/src/grids/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ is equivalent to:
```julia
vtk = vtk_grid(filename, points..., [cells]; kws...)
# add datasets here...
saved_files = vtk_save(vtk)
saved_files = close(vtk)
```

## Data formatting options
Expand Down
2 changes: 1 addition & 1 deletion docs/src/metadata/multiblock.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ vtk = vtk_grid(yet_another_block, "my_deeply_nested_file", x4, y4, z4)
Finally, only the multiblock file needs to be saved explicitly:

``` julia
outfiles = vtk_save(vtm)
outfiles = close(vtm)
```

WriteVTK will write out a multiblock VTK file that looks like something like this (in addition to all the VTK files contained in the multiblock file):
Expand Down
4 changes: 2 additions & 2 deletions docs/src/metadata/parallel.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ pvtk_grid(

which returns a handler representing a parallel VTK file that can be
appended with cell and point data and eventually written to disk with
[`vtk_save`](@ref) as usual.
In an MPI job, `vtk_save` will cause each rank to write a serial file and just
[`close`](@ref) as usual.
In an MPI job, `close` will cause each rank to write a serial file and just
a single rank (e.g., rank 0) will write the header file.

This signature is valid for **unstructured grids**.
Expand Down
4 changes: 2 additions & 2 deletions docs/src/metadata/paraview_collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ collection_add_timestep(pvd, vtk, time)
```

Here, `time` is a real number that represents the current time (or timestep) in
the simulation. Note that both options implicitly call `vtk_save(vtk)` so adding
the simulation. Note that both options implicitly call `close(vtk)` so adding
the VTK file to the collection must be done after adding data to the file.

When all the files are added to the `pvd` file, it can be saved using:

``` julia
vtk_save(pvd)
close(pvd)
```

## Working example
Expand Down
2 changes: 1 addition & 1 deletion docs/src/tools/surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ julia> zs = @. cos(xs) + sin(ys');
julia> vtk = vtk_surface("surf", xs, ys, zs)
VTK file 'surf.vtu' (UnstructuredGrid file, open)
julia> vtk_save(vtk)
julia> close(vtk)
1-element Vector{String}:
"surf.vtu"
```
Expand Down
35 changes: 25 additions & 10 deletions src/WriteVTK.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ using FillArrays: Zeros

using Base64: base64encode

import Base: close, isopen, show

using VTKBase:
VTKBase,
VTKCellTypes, # cell type definitions as in vtkCellType.h
Expand Down Expand Up @@ -50,7 +48,7 @@ const HeaderType = UInt64 # should be UInt32 or UInt64
"""
VTKFile
Abstract type describing a VTK file that may be written using [`vtk_save`](@ref).
Abstract type describing a VTK file that may be written using [`close`](@ref).
"""
abstract type VTKFile end

Expand Down Expand Up @@ -104,8 +102,10 @@ struct DatasetFile <: VTKFile
end
end

DatasetFile(dtype, xdoc::XMLDocument, fname::AbstractString, args...; kwargs...) =
function DatasetFile(dtype, xdoc::XMLDocument, fname::AbstractString, args...; kwargs...)
finalizer(LightXML.free, xdoc)
DatasetFile(xdoc, add_extension(fname, dtype), xml_name(dtype), args...; kwargs...)
end

function data_format(vtk::DatasetFile)
if vtk.appended
Expand All @@ -117,24 +117,39 @@ function data_format(vtk::DatasetFile)
end
end

function show(io::IO, vtk::DatasetFile)
function Base.show(io::IO, vtk::DatasetFile)
open_str = isopen(vtk) ? "open" : "closed"
print(io, "VTK file '$(vtk.path)' ($(vtk.grid_type) file, $open_str)")
end

"""
close(vtk::VTKFile)
Base.close(vtk::VTKFile) -> Vector{String}
Write and close VTK file.
Returns a list of paths pointing to the written VTK files (typically just one file, but can
be more for e.g. `MultiblockFile`).
---
Base.close(vtm::MultiblockFile) -> Vector{String}
Save and close multiblock file (`.vtm`).
The VTK files included in the multiblock file are also saved.
"""
close(vtk::VTKFile) = free(vtk.xdoc)
Base.close(vtk::VTKFile) = vtk_save(vtk) # for backwards compatibility, the actual implementation is in vtk_save (which still works)

# Free LightXML memory. Note that this is also called when an xdoc object is finalised, but
# it seems to be OK to call `free` multiple times.
# After calling this, the VTK file is considered as closed (see `isopen` below).
close_xml(vtk::VTKFile) = LightXML.free(vtk.xdoc)

"""
isopen(vtk::VTKFile)
Base.isopen(vtk::VTKFile)
Check if VTK file is still being written.
"""
isopen(vtk::VTKFile) = (vtk.xdoc.ptr != C_NULL)
Base.isopen(vtk::VTKFile) = (vtk.xdoc.ptr != C_NULL)

# Add a default extension to the filename, unless the user have already given
# the correct one.
Expand Down Expand Up @@ -201,7 +216,7 @@ for func in (:vtk_grid, :pvtk_grid, :vtk_multiblock, :paraview_collection,
try
f(vtk)
finally
outfiles = vtk_save(vtk)
outfiles = close(vtk)
end
outfiles :: Vector{String}
end
Expand Down
11 changes: 7 additions & 4 deletions src/gridtypes/ParaviewCollection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ struct CollectionFile <: VTKFile
xdoc::XMLDocument
path::String
timeSteps::Vector{String}
CollectionFile(xdoc, path) = new(xdoc, path, String[])
function CollectionFile(xdoc, path)
finalizer(LightXML.free, xdoc)
new(xdoc, path, String[])
end
end

function paraview_collection(filename::AbstractString;
Expand Down Expand Up @@ -65,7 +68,7 @@ function collection_add_timestep(pvd::CollectionFile, datfile::VTKFile,
set_attribute(xDataSet, "timestep", string(time))
set_attribute(xDataSet, "part", "0")
set_attribute(xDataSet, "file", fname)
append!(pvd.timeSteps, vtk_save(datfile))
append!(pvd.timeSteps, close(datfile))
return
end

Expand All @@ -75,8 +78,8 @@ Base.setindex!(pvd::CollectionFile, datfile::VTKFile, time::Real) =
function vtk_save(pvd::CollectionFile)
outfiles = [pvd.path; pvd.timeSteps]::Vector{String}
if isopen(pvd)
save_file(pvd.xdoc, pvd.path)
close(pvd)
LightXML.save_file(pvd.xdoc, pvd.path)
close_xml(pvd)
end
return outfiles
end
24 changes: 11 additions & 13 deletions src/gridtypes/multiblock.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ struct VTKBlock
VTKBlock(xelm) = new(xelm, Union{VTKFile,VTKBlock}[])
end

Base.close(vtb::VTKBlock) = vtk_save(vtb)
xml_block_root(vtb::VTKBlock) = vtb.xelm

"""
Expand All @@ -21,7 +22,10 @@ struct MultiblockFile <: VTKFile
xdoc::XMLDocument
path::String
blocks::Vector{Union{VTKFile,VTKBlock}}
MultiblockFile(xdoc, path) = new(xdoc, path, Union{VTKFile,VTKBlock}[])
function MultiblockFile(xdoc, path)
finalizer(LightXML.free, xdoc)
new(xdoc, path, Union{VTKFile,VTKBlock}[])
end
end

function xml_block_root(vtm::MultiblockFile)
Expand All @@ -39,9 +43,9 @@ Initialise VTK multiblock file, linking multiple VTK dataset files.
Returns a handler for a multiblock file.
To recursively save the multiblock file and linked dataset files, call
[`vtk_save`](@ref) on the returned handler.
[`close`](@ref) on the returned handler.
Note that `vtk_save` is implicitly called if the optional `f` argument is passed.
Note that `close` is implicitly called if the optional `f` argument is passed.
This is in particular what happens when using the do-block syntax.
"""
function vtk_multiblock(filename::AbstractString)
Expand Down Expand Up @@ -103,20 +107,14 @@ function _generate_gridfile_basename(vtm::VTKBlock)
end
end

"""
vtk_save(vtm::MultiblockFile)
Save and close multiblock file (`.vtm`).
The VTK files included in the multiblock file are also saved.
"""
function vtk_save(vtm::MultiblockFile)
outfiles = [vtm.path]::Vector{String}
for vtk in vtm.blocks
append!(outfiles, vtk_save(vtk))
append!(outfiles, close(vtk))
end
if isopen(vtm)
save_file(vtm.xdoc, vtm.path)
close(vtm)
LightXML.save_file(vtm.xdoc, vtm.path)
close_xml(vtm)
end
outfiles
end
Expand All @@ -125,7 +123,7 @@ function vtk_save(vtm::VTKBlock)
# Saves VTKBlocks.
outfiles = String[]
for vtk in vtm.blocks
append!(outfiles, vtk_save(vtk))
append!(outfiles, close(vtk))
end
return outfiles
end
Expand Down
12 changes: 8 additions & 4 deletions src/gridtypes/pvtk_grid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ struct PVTKFile <: VTKFile
xdoc::XMLDocument
vtk::DatasetFile
path::String
function PVTKFile(args, xdoc, vtk, path)
finalizer(LightXML.free, xdoc)
new(args, xdoc, vtk, path)
end
end

# This is just to make a PVTKFile work like a DatasetFile.
Expand Down Expand Up @@ -52,9 +56,9 @@ compute_whole_extent(::Nothing) = nothing
)
Returns a handler representing a parallel VTK file, which can be
eventually written to file with `vtk_save`.
eventually written to file with [`close`](@ref).
Positional and keyword arguments in `args` and `kwargs` are passed to `vtk_grid`
Positional and keyword arguments in `args` and `kwargs` are passed to [`vtk_grid`](@ref)
verbatim.
Note that serial filenames are automatically generated from `filename` and from
the process id `part`.
Expand Down Expand Up @@ -186,8 +190,8 @@ function vtk_save(pvtk::PVTKFile)
save_file(pvtk.xdoc, pvtk.path)
push!(outfiles, pvtk.path)
end
append!(outfiles, vtk_save(pvtk.vtk))
close(pvtk)
append!(outfiles, close(pvtk.vtk))
close_xml(pvtk)
end
outfiles
end
Expand Down
4 changes: 2 additions & 2 deletions src/save_files.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ function vtk_save(vtk::DatasetFile)
save_file(vtk.xdoc, vtk.path)
end
end
if isopen(vtk) # just in case the file was closed by calls to save_* above
close(vtk)
if isopen(vtk) # just in case the XML handler was freed by calls to save_* above
close_xml(vtk)
end
return [vtk.path] :: Vector{String}
end
Expand Down
6 changes: 4 additions & 2 deletions test/pvdCollection.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env julia

using WriteVTK
using Test

using Printf: @sprintf

Expand Down Expand Up @@ -69,7 +70,8 @@ function main()
vtk["q_values"] = q
vtk["myVector"] = vec
vtk["myCellData"] = cdata
vtk_save(vtk)
close(vtk)
@test isopen(vtk) == false
pvd[float(it + 1)] = vtk
end
end
Expand All @@ -83,7 +85,7 @@ function main()
# add a vtk file
vtk_reload = vtk_grid("collection_reload", [1, 2, 3], [1, 2, 3])
pvd_reload[5.0] = vtk_reload
pvd_reload_files = vtk_save(pvd_reload)
pvd_reload_files = close(pvd_reload)
append!(outfiles, pvd_reload_files)

println("Saved: ", join(outfiles, " "))
Expand Down
3 changes: 2 additions & 1 deletion test/rectilinear.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ function main()
vtk["myCellData"] = cdata

# Save and close vtk file.
append!(outfiles, vtk_save(vtk))
append!(outfiles, close(vtk))
@test isopen(vtk) == false
end

end # dim loop
Expand Down
6 changes: 5 additions & 1 deletion test/structured.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using WriteVTK
using StaticArrays: SVector
using Test

const FloatType = Float32
const vtk_filename_noext = "structured"
Expand Down Expand Up @@ -111,8 +112,11 @@ function generate_structured(grid_format, ::Val{dim}) where {dim}
vtk["myVector.SVector"] = vs

# Save and close vtk file.
vtk_save(vtk)
files = close(vtk)
@test isopen(vtk) == false
end

files
end

function main()
Expand Down
4 changes: 3 additions & 1 deletion test/surface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ files = String[]
let
@time output = let
vtk = vtk_surface("surface_basic", xs, ys, zs)
vtk_save(vtk)
output = close(vtk)
@test isopen(vtk) == false
output
end
append!(files, output)
end
Expand Down

0 comments on commit f60e984

Please sign in to comment.