Skip to content

Commit f60e984

Browse files
authored
Make close actually save VTK files (#149)
* 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?
1 parent dc4c34c commit f60e984

14 files changed

Lines changed: 74 additions & 45 deletions

docs/src/grids/syntax.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ is equivalent to:
2525
```julia
2626
vtk = vtk_grid(filename, points..., [cells]; kws...)
2727
# add datasets here...
28-
saved_files = vtk_save(vtk)
28+
saved_files = close(vtk)
2929
```
3030

3131
## Data formatting options

docs/src/metadata/multiblock.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ vtk = vtk_grid(yet_another_block, "my_deeply_nested_file", x4, y4, z4)
5757
Finally, only the multiblock file needs to be saved explicitly:
5858

5959
``` julia
60-
outfiles = vtk_save(vtm)
60+
outfiles = close(vtm)
6161
```
6262

6363
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):

docs/src/metadata/parallel.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ pvtk_grid(
2121

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

2828
This signature is valid for **unstructured grids**.

docs/src/metadata/paraview_collections.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ collection_add_timestep(pvd, vtk, time)
3131
```
3232

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

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

3939
``` julia
40-
vtk_save(pvd)
40+
close(pvd)
4141
```
4242

4343
## Working example

docs/src/tools/surface.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ julia> zs = @. cos(xs) + sin(ys');
2222
julia> vtk = vtk_surface("surf", xs, ys, zs)
2323
VTK file 'surf.vtu' (UnstructuredGrid file, open)
2424
25-
julia> vtk_save(vtk)
25+
julia> close(vtk)
2626
1-element Vector{String}:
2727
"surf.vtu"
2828
```

src/WriteVTK.jl

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ using FillArrays: Zeros
2121

2222
using Base64: base64encode
2323

24-
import Base: close, isopen, show
25-
2624
using VTKBase:
2725
VTKBase,
2826
VTKCellTypes, # cell type definitions as in vtkCellType.h
@@ -50,7 +48,7 @@ const HeaderType = UInt64 # should be UInt32 or UInt64
5048
"""
5149
VTKFile
5250
53-
Abstract type describing a VTK file that may be written using [`vtk_save`](@ref).
51+
Abstract type describing a VTK file that may be written using [`close`](@ref).
5452
"""
5553
abstract type VTKFile end
5654

@@ -104,8 +102,10 @@ struct DatasetFile <: VTKFile
104102
end
105103
end
106104

107-
DatasetFile(dtype, xdoc::XMLDocument, fname::AbstractString, args...; kwargs...) =
105+
function DatasetFile(dtype, xdoc::XMLDocument, fname::AbstractString, args...; kwargs...)
106+
finalizer(LightXML.free, xdoc)
108107
DatasetFile(xdoc, add_extension(fname, dtype), xml_name(dtype), args...; kwargs...)
108+
end
109109

110110
function data_format(vtk::DatasetFile)
111111
if vtk.appended
@@ -117,24 +117,39 @@ function data_format(vtk::DatasetFile)
117117
end
118118
end
119119

120-
function show(io::IO, vtk::DatasetFile)
120+
function Base.show(io::IO, vtk::DatasetFile)
121121
open_str = isopen(vtk) ? "open" : "closed"
122122
print(io, "VTK file '$(vtk.path)' ($(vtk.grid_type) file, $open_str)")
123123
end
124124

125125
"""
126-
close(vtk::VTKFile)
126+
Base.close(vtk::VTKFile) -> Vector{String}
127127
128128
Write and close VTK file.
129+
130+
Returns a list of paths pointing to the written VTK files (typically just one file, but can
131+
be more for e.g. `MultiblockFile`).
132+
133+
---
134+
135+
Base.close(vtm::MultiblockFile) -> Vector{String}
136+
137+
Save and close multiblock file (`.vtm`).
138+
The VTK files included in the multiblock file are also saved.
129139
"""
130-
close(vtk::VTKFile) = free(vtk.xdoc)
140+
Base.close(vtk::VTKFile) = vtk_save(vtk) # for backwards compatibility, the actual implementation is in vtk_save (which still works)
141+
142+
# Free LightXML memory. Note that this is also called when an xdoc object is finalised, but
143+
# it seems to be OK to call `free` multiple times.
144+
# After calling this, the VTK file is considered as closed (see `isopen` below).
145+
close_xml(vtk::VTKFile) = LightXML.free(vtk.xdoc)
131146

132147
"""
133-
isopen(vtk::VTKFile)
148+
Base.isopen(vtk::VTKFile)
134149
135150
Check if VTK file is still being written.
136151
"""
137-
isopen(vtk::VTKFile) = (vtk.xdoc.ptr != C_NULL)
152+
Base.isopen(vtk::VTKFile) = (vtk.xdoc.ptr != C_NULL)
138153

139154
# Add a default extension to the filename, unless the user have already given
140155
# the correct one.
@@ -201,7 +216,7 @@ for func in (:vtk_grid, :pvtk_grid, :vtk_multiblock, :paraview_collection,
201216
try
202217
f(vtk)
203218
finally
204-
outfiles = vtk_save(vtk)
219+
outfiles = close(vtk)
205220
end
206221
outfiles :: Vector{String}
207222
end

src/gridtypes/ParaviewCollection.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ struct CollectionFile <: VTKFile
77
xdoc::XMLDocument
88
path::String
99
timeSteps::Vector{String}
10-
CollectionFile(xdoc, path) = new(xdoc, path, String[])
10+
function CollectionFile(xdoc, path)
11+
finalizer(LightXML.free, xdoc)
12+
new(xdoc, path, String[])
13+
end
1114
end
1215

1316
function paraview_collection(filename::AbstractString;
@@ -65,7 +68,7 @@ function collection_add_timestep(pvd::CollectionFile, datfile::VTKFile,
6568
set_attribute(xDataSet, "timestep", string(time))
6669
set_attribute(xDataSet, "part", "0")
6770
set_attribute(xDataSet, "file", fname)
68-
append!(pvd.timeSteps, vtk_save(datfile))
71+
append!(pvd.timeSteps, close(datfile))
6972
return
7073
end
7174

@@ -75,8 +78,8 @@ Base.setindex!(pvd::CollectionFile, datfile::VTKFile, time::Real) =
7578
function vtk_save(pvd::CollectionFile)
7679
outfiles = [pvd.path; pvd.timeSteps]::Vector{String}
7780
if isopen(pvd)
78-
save_file(pvd.xdoc, pvd.path)
79-
close(pvd)
81+
LightXML.save_file(pvd.xdoc, pvd.path)
82+
close_xml(pvd)
8083
end
8184
return outfiles
8285
end

src/gridtypes/multiblock.jl

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ struct VTKBlock
1010
VTKBlock(xelm) = new(xelm, Union{VTKFile,VTKBlock}[])
1111
end
1212

13+
Base.close(vtb::VTKBlock) = vtk_save(vtb)
1314
xml_block_root(vtb::VTKBlock) = vtb.xelm
1415

1516
"""
@@ -21,7 +22,10 @@ struct MultiblockFile <: VTKFile
2122
xdoc::XMLDocument
2223
path::String
2324
blocks::Vector{Union{VTKFile,VTKBlock}}
24-
MultiblockFile(xdoc, path) = new(xdoc, path, Union{VTKFile,VTKBlock}[])
25+
function MultiblockFile(xdoc, path)
26+
finalizer(LightXML.free, xdoc)
27+
new(xdoc, path, Union{VTKFile,VTKBlock}[])
28+
end
2529
end
2630

2731
function xml_block_root(vtm::MultiblockFile)
@@ -39,9 +43,9 @@ Initialise VTK multiblock file, linking multiple VTK dataset files.
3943
4044
Returns a handler for a multiblock file.
4145
To recursively save the multiblock file and linked dataset files, call
42-
[`vtk_save`](@ref) on the returned handler.
46+
[`close`](@ref) on the returned handler.
4347
44-
Note that `vtk_save` is implicitly called if the optional `f` argument is passed.
48+
Note that `close` is implicitly called if the optional `f` argument is passed.
4549
This is in particular what happens when using the do-block syntax.
4650
"""
4751
function vtk_multiblock(filename::AbstractString)
@@ -103,20 +107,14 @@ function _generate_gridfile_basename(vtm::VTKBlock)
103107
end
104108
end
105109

106-
"""
107-
vtk_save(vtm::MultiblockFile)
108-
109-
Save and close multiblock file (`.vtm`).
110-
The VTK files included in the multiblock file are also saved.
111-
"""
112110
function vtk_save(vtm::MultiblockFile)
113111
outfiles = [vtm.path]::Vector{String}
114112
for vtk in vtm.blocks
115-
append!(outfiles, vtk_save(vtk))
113+
append!(outfiles, close(vtk))
116114
end
117115
if isopen(vtm)
118-
save_file(vtm.xdoc, vtm.path)
119-
close(vtm)
116+
LightXML.save_file(vtm.xdoc, vtm.path)
117+
close_xml(vtm)
120118
end
121119
outfiles
122120
end
@@ -125,7 +123,7 @@ function vtk_save(vtm::VTKBlock)
125123
# Saves VTKBlocks.
126124
outfiles = String[]
127125
for vtk in vtm.blocks
128-
append!(outfiles, vtk_save(vtk))
126+
append!(outfiles, close(vtk))
129127
end
130128
return outfiles
131129
end

src/gridtypes/pvtk_grid.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ struct PVTKFile <: VTKFile
1212
xdoc::XMLDocument
1313
vtk::DatasetFile
1414
path::String
15+
function PVTKFile(args, xdoc, vtk, path)
16+
finalizer(LightXML.free, xdoc)
17+
new(args, xdoc, vtk, path)
18+
end
1519
end
1620

1721
# This is just to make a PVTKFile work like a DatasetFile.
@@ -52,9 +56,9 @@ compute_whole_extent(::Nothing) = nothing
5256
)
5357
5458
Returns a handler representing a parallel VTK file, which can be
55-
eventually written to file with `vtk_save`.
59+
eventually written to file with [`close`](@ref).
5660
57-
Positional and keyword arguments in `args` and `kwargs` are passed to `vtk_grid`
61+
Positional and keyword arguments in `args` and `kwargs` are passed to [`vtk_grid`](@ref)
5862
verbatim.
5963
Note that serial filenames are automatically generated from `filename` and from
6064
the process id `part`.
@@ -186,8 +190,8 @@ function vtk_save(pvtk::PVTKFile)
186190
save_file(pvtk.xdoc, pvtk.path)
187191
push!(outfiles, pvtk.path)
188192
end
189-
append!(outfiles, vtk_save(pvtk.vtk))
190-
close(pvtk)
193+
append!(outfiles, close(pvtk.vtk))
194+
close_xml(pvtk)
191195
end
192196
outfiles
193197
end

src/save_files.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ function vtk_save(vtk::DatasetFile)
66
save_file(vtk.xdoc, vtk.path)
77
end
88
end
9-
if isopen(vtk) # just in case the file was closed by calls to save_* above
10-
close(vtk)
9+
if isopen(vtk) # just in case the XML handler was freed by calls to save_* above
10+
close_xml(vtk)
1111
end
1212
return [vtk.path] :: Vector{String}
1313
end

0 commit comments

Comments
 (0)