better xdmf support

This commit is contained in:
Jukka Aho
2016-08-02 02:51:32 +03:00
parent 62efaa292a
commit e67422fa7c
6 changed files with 270 additions and 83 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ module Testing
end
include("io.jl")
export ModelIO
export Xdmf, h5file, xmffile, has_child, get_child, new_dataitem
include("fields.jl")
export Field, DCTI, DVTI, DCTV, DVTV, CCTI, CVTI, CCTV, CVTV, Increment
+145 -28
View File
@@ -3,51 +3,168 @@
using HDF5
using LightXML
importall LightXML
importall Base
type ModelIO
function new_element(name::AbstractString, attrs::Dict)
x = new_element(name)
for (k, v) in attrs
x[k] = v
end
return x
end
function setindex!(x::XMLElement, content::Any, attr_name::AbstractString)
set_attribute(x, attr_name, content)
end
function haskey(x::XMLElement, key::AbstractString)
return has_child(x, key) || has_attribute(x, key)
end
function get_child(x::XMLElement, child_name::AbstractString)
'/' in child_name && return nothing
m = match(r"(\w+)\[(.+)\]", child_name)
if m != nothing
child_name = m[1]
end
childs = []
for child in child_elements(x)
if name(child) == child_name
push!(childs, child)
end
end
length(childs) == 0 && return nothing
m == nothing && return first(childs)
j = tryparse(Int, m[2])
isnull(j) || return childs[get(j)]
m[2] == "end" && return childs[end]
m2 = match(r"@(.+)=(.+)", m[2])
m2 == nothing && throw("Unable to parse: $(m[2])")
attr_name = m2[1]
attr_value = m2[2]
for child in childs
has_attribute(child, attr_name) || continue
if get_attribute(child, attr_name) == attr_value
return child
end
end
throw("Unable to parse: $(m[2])")
end
function has_child(x::XMLElement, child_name::AbstractString)
return get_child(x, child_name) != nothing
end
function get_attribute(x::XMLElement, attr_name::AbstractString)
attr = attribute(x, attr_name)
numeric = tryparse(Int64, attr)
isnull(numeric) && (numeric = tryparse(Float64, attr))
isnull(numeric) && return attr
return get(numeric)
end
function getindex(x::XMLElement, attr_name::AbstractString)
attr_name = strip(attr_name, '/')
child = get_child(x, attr_name)
child == nothing || return child
has_attribute(x, attr_name) && return get_attribute(x, attr_name)
if '/' in attr_name
items = split(attr_name, '/')
attr_name = first(items)
length(items) > 1 || throw(KeyError(attr_name))
haskey(x, attr_name) || throw(KeyError(attr_name))
path = join(items[2:end], '/')
new_item = getindex(x, attr_name)
return new_item[path]
else
throw(KeyError(attr_name))
end
end
function new_child(xparent::XMLElement, name::AbstractString, attrs::Dict)
x = new_child(xparent, name)
for (k, v) in attrs
x[k] = v
end
return x
end
function new_child(xparent::XMLElement, name::AbstractString, attrs::Pair...)
x = new_child(xparent, name)
for (k, v) in attrs
x[k] = v
end
return x
end
type Xdmf
name :: AbstractString
xdmf :: XMLElement
xml :: XMLElement
hdf :: HDF5File
end
function ModelIO()
return ModelIO(tempname())
function new_child(xdmf::Xdmf, args...; kwargs...)
new_child(xdmf.xml, args...; kwargs...)
end
function ModelIO(name::AbstractString)
function read(xdmf::Xdmf, path::AbstractString)
result = getindex(xdmf.xml, path)
if endswith(path, "DataItem")
format = get_attribute(result, "Format")
@assert format == "HDF"
h5file, path = map(ASCIIString, split(content(result), ':'))
h5file = dirname(xdmf.name) * "/" * h5file
isfile(h5file) || throw("Xdmf: h5 file $h5file not found!")
return read(xdmf.hdf, path)
else
return result
end
end
function Xdmf()
return Xdmf(tempname())
end
function h5file(xdmf::Xdmf)
return xdmf.name*".h5"
end
function xmffile(xdmf::Xdmf)
return xdmf.name*".xmf"
end
function Xdmf(name::AbstractString)
xdmf = new_element("Xdmf")
set_attribute(xdmf, "xmlns:xi", "http://www.w3.org/2001/XInclude")
set_attribute(xdmf, "Version", "2.1")
return ModelIO(name, xdmf)
h5file = "$name.h5"
flag = isfile(h5file) ? "r+" : "w"
hdf = h5open(h5file, flag)
return Xdmf(name, xdmf, hdf)
end
function h5file(mio::ModelIO)
return mio.name*".h5"
function save!(xdmf::Xdmf)
doc = XMLDocument()
set_root(doc, xdmf.xml)
save_file(doc, xmffile(xdmf))
end
function put!{T,N}(mio::ModelIO, path::AbstractString, data::Array{T,N})
hdf = h5file(mio)
h5write(hdf, path, data)
end
function get_dataitem{T,N}(mio::ModelIO, path::AbstractString, data::Array{T,N}; format="HDF")
function new_dataitem{T,N}(xdmf::Xdmf, path::AbstractString, data::Array{T,N}; format="HDF")
dataitem = new_element("DataItem")
n, m = size(data)
set_attribute(dataitem, "DataType", "$T")
set_attribute(dataitem, "Dimensions", "$n $m")
datatype = replace("$T", "64", "")
dimensions = join(size(data), " ")
set_attribute(dataitem, "DataType", datatype)
set_attribute(dataitem, "Dimensions", dimensions)
set_attribute(dataitem, "Format", format)
if format == "HDF"
hdf = basename(h5file(mio))
hdf = basename(h5file(xdmf))
if !exists(xdmf.hdf, path)
write(xdmf.hdf, path, data)
end
add_text(dataitem, "$hdf:$path")
elseif format == "XML"
add_text(dataitem, strip(string(data), ['[', ']']))
end
return dataitem
end
function get(mio::ModelIO, path::AbstractString)
h5read(mio.name*".h5", path)
end
function save!(mio::ModelIO)
doc = XMLDocument()
set_root(doc, mio.xdmf)
save_file(doc, mio.name*".xmf")
end
+6 -3
View File
@@ -246,9 +246,12 @@ end
function call(solver::Solver, ::Type{DataFrame}, field_name::AbstractString,
abbreviation::Symbol, time::Float64=0.0)
u = Dict()
for problem in get_problems(solver)
u = merge(u, problem(field_name, time))
fields = [problem(field_name, time) for problem in get_problems(solver)]
fields = filter(f -> f != nothing, fields)
if length(fields) != 0
u = merge(fields...)
else
u = Dict()
end
return to_dataframe(u, abbreviation)
end
-1
View File
@@ -192,4 +192,3 @@ end
function xdmf_save!(xdmf, filename)
save_file(xdmf.xdoc, filename)
end
+34 -30
View File
@@ -9,7 +9,7 @@ type Solver{S<:AbstractSolver}
problems :: Vector{Problem}
norms :: Vector{Tuple} # solution norms for convergence studies
ndofs :: Int # number of degrees of freedom in problem
io :: Nullable{ModelIO} # input/output handle
xdmf :: Nullable{Xdmf} # input/output handle
properties :: S
end
@@ -457,15 +457,17 @@ function call(solver::Solver, field_name::AbstractString, time::Float64)
return merge(fields...)
end
function get_temporal_collection(mio::ModelIO)
grid = find_element(mio.xdmf, "Grid")
if grid == nothing
function get_temporal_collection(xdmf::Xdmf)
domain = find_element(xdmf.xml, "Domain")
grid = nothing
if domain == nothing
info("Xdmf: creating new temporal collection")
domain = new_child(mio.xdmf, "Domain")
domain = new_child(xdmf.xml, "Domain")
grid = new_child(domain, "Grid")
set_attribute(grid, "CollectionType", "Temporal")
set_attribute(grid, "GridType", "Collection")
end
grid = find_element(domain, "Grid")
return grid
end
@@ -473,6 +475,7 @@ end
function update!(solver::Solver, u::Vector, la::Vector; show_info=true)
show_info && info("Updating problems ...")
t0 = Base.time()
for problem in solver.problems
assembly = get_assembly(problem)
elements = get_elements(problem)
@@ -483,27 +486,21 @@ function update!(solver::Solver, u::Vector, la::Vector; show_info=true)
end
# if io is attached to solver, update hdf / xml also
if !isnull(solver.io)
io = get(solver.io)
xdmf = io.xdmf
temporal_collection = get_temporal_collection(io)
if !isnull(solver.xdmf)
xdmf = get(solver.xdmf)
temporal_collection = get_temporal_collection(xdmf)
frame = new_child(temporal_collection, "Grid")
time_item = new_child(frame, "Time")
set_attribute(time_item, "Value", solver.time)
new_child(frame, "Time", Dict("Value" => solver.time))
# save geometry
X = solver("geometry", solver.time)
node_ids = sort(collect(keys(X)))
geometry = hcat([X[nid] for nid in node_ids]...)
put!(io, "/Node IDs", node_ids)
path = "/Geometry"
put!(io, path, geometry)
dataitem = get_dataitem(io, path, geometry)
ndim, nnodes = size(geometry)
geom_type = ndim == 2 ? "XY" : "XYZ"
geom = new_child(frame, "Geometry")
set_attribute(geom, "Type", geom_type)
dataitem = new_dataitem(xdmf, "/Node IDs", node_ids)
geom = new_child(frame, "Geometry", Dict("Type" => geom_type))
dataitem = new_dataitem(xdmf, "/Geometry", geometry)
add_child(geom, dataitem)
# save topology
@@ -511,10 +508,21 @@ function update!(solver::Solver, u::Vector, la::Vector; show_info=true)
nelements = length(all_elements)
element_types = unique(map(get_element_type, all_elements))
element_mapping = Dict(
"Quad4" => "Quadrilateral",
xdmf_element_mapping = Dict(
"Seg2" => "Polyline",
)
"Tri3" => "Triangle",
"Quad4" => "Quadrilateral",
"Tet4" => "Tetrahedron",
"Pyramid5" => "Pyramid",
"Wedge6" => "Wedge",
"Hex8" => "Hexahedron",
"Seg3" => "Edge_3",
"Tri6" => "Tri_6",
"Quad8" => "Quad_8",
"Tet10" => "Tet_10",
"Pyramid13" => "Pyramid_13",
"Wedge15" => "Wedge_15",
"Hex20" => "Hex_20")
for element_type in element_types
elements = filter_by_element_type(element_type, all_elements)
@@ -523,12 +531,10 @@ function update!(solver::Solver, u::Vector, la::Vector; show_info=true)
element_conn = map(get_connectivity, elements)
element_conn = transpose(hcat(element_conn...)) - 1
element_code = split(string(element_type), ".")[end]
put!(io, "/Topology/$element_code/Element IDs", element_ids)
path = "/Topology/$element_code/Connectivity"
put!(io, path, element_conn)
dataitem = get_dataitem(io, path, element_conn)
dataitem = new_dataitem(xdmf, "/Topology/$element_code/Element IDs", element_ids)
dataitem = new_dataitem(xdmf, "/Topology/$element_code/Connectivity", element_conn)
topology = new_child(frame, "Topology")
set_attribute(topology, "TopologyType", element_mapping[element_code])
set_attribute(topology, "TopologyType", xdmf_element_mapping[element_code])
set_attribute(topology, "NumberOfElements", length(elements))
add_child(topology, dataitem)
end
@@ -551,14 +557,12 @@ function update!(solver::Solver, u::Vector, la::Vector; show_info=true)
end
U = hcat([U[nid] for nid in node_ids]...)
unknown_field_name = ucfirst(unknown_field_name)
path = "/Results/$time/Nodal Fields/$unknown_field_name"
put!(io, path, U)
dataitem = get_dataitem(io, path, U)
dataitem = new_dataitem(xdmf, "/Results/$time/Nodal Fields/$unknown_field_name", U)
attribute = new_child(frame, "Attribute")
set_attribute(attribute, "Name", unknown_field_name)
set_attribute(attribute, "Center", field_center)
add_child(attribute, dataitem)
save!(io)
save!(xdmf)
end
t1 = round(Base.time()-t0, 2)
+84 -20
View File
@@ -3,18 +3,56 @@
using JuliaFEM
using JuliaFEM.Testing
importall Base
using LightXML
@testset "create new result" begin
r = ModelIO()
expected = """<Xdmf xmlns:xi="http://www.w3.org/2001/XInclude" Version="2.1"/>"""
@test string(r.xdmf) == expected
@testset "create new Xdmf object" begin
r = Xdmf()
expected = "<Xdmf xmlns:xi=\"http://www.w3.org/2001/XInclude\" Version=\"2.1\"/>"
@test string(r.xml) == expected
end
@testset "put and get result" begin
r = ModelIO()
put!(r, "/1/2/3", [1 2 3])
@test isapprox(get(r, "/1/2/3"), [1 2 3])
@testset "put and get to Xdmf, low level" begin
io = Xdmf()
# h5
write(io.hdf, "/Xdmf/Domain/Geometry", [1 2 3])
@test isapprox(read(io.hdf, "/Xdmf/Domain/Geometry"), [1 2 3])
# xml
obj = new_child(io.xml, "Domain")
set_attribute(obj, "Name", "Test Domain")
obj2 = find_element(io.xml, "Domain")
@test attribute(obj2, "Name") == "Test Domain"
end
@testset "put and get to xdmf" begin
xdmf = Xdmf()
domain = new_child(xdmf, "Domain")
grid = new_child(domain, "Grid")
grid["CollectionType"] = "Temporal"
grid["GridType"] = "Collection"
frame1 = new_child(grid, "Grid")
new_child(frame1, "Time", "Value" => 0.0)
X1 = new_child(frame1, "Geometry", Dict("Type" => "XY"))
frame2 = new_child(grid, "Grid", "Name" => "Frame 2")
new_child(frame2, "Time", "Value" => 1.0)
X2 = new_child(frame2, "Geometry", "Type" => "XY")
add_child(grid, frame1)
add_child(grid, frame2)
dataitem = new_dataitem(xdmf, "/Domain/Grid/Grid/2/Geometry", [1.0, 2.0])
add_child(X2, dataitem)
println(xdmf.xml)
@test has_child(xdmf.xml, "Domain")
@test isa(get_child(xdmf.xml, "Domain"), XMLElement)
@test !has_attribute(xdmf.xml, "Domain")
@test isapprox(read(xdmf, "/Domain/Grid/Grid/Time/Value"), 0.0)
@test isapprox(read(xdmf, "/Domain/Grid/Grid[2]/Time/Value"), 1.0)
@test isapprox(read(xdmf, "/Domain/Grid/Grid[end]/Time/Value"), 1.0)
@test isapprox(read(xdmf, "/Domain/Grid/Grid[@Name=Frame 2]/Time/Value"), 1.0)
@test isapprox(read(xdmf, "/Domain/Grid/Grid[2]/Geometry/DataItem"), [1.0, 2.0])
end
@testset "save results to disk" begin
@@ -26,7 +64,8 @@ end
element = Element(Quad4, [1, 2, 3, 4])
update!(element, "geometry", X)
update!(element, "temperature thermal conductivity", 6.0)
update!(element, "temperature load", 12.0)
update!(element, "temperature load", 0.0 => 12.0)
update!(element, "temperature load", 1.0 => 18.0)
problem = Problem(Heat, "one element heat problem", 1)
problem.properties.formulation = "2D"
push!(problem, element)
@@ -36,18 +75,43 @@ end
bc = Problem(Dirichlet, "fixed", 1, "temperature")
push!(bc, boundary_element)
solver = Solver(Linear, problem, bc)
solver.io = ModelIO()
solver.xdmf = Xdmf()
solver.time = 0.0
solver()
io = get(solver.io)
info("h5 file = $(io.name).h5")
E = get(io, "/Topology/Quad4/Element IDs")
C = get(io, "/Topology/Quad4/Connectivity")
N = get(io, "/Node IDs")
X = get(io, "/Geometry")
T = get(io, "/Results/Time 0.0/Nodal Fields/Temperature")
empty!(problem.assembly)
solver.time = 1.0
solver()
info(solver("temperature", 0.0))
info(solver("temperature", 1.0))
info(element("temperature load", [0.0, 0.0], 0.0))
info(element("temperature load", [0.0, 0.0], 1.0))
xdmf = get(solver.xdmf)
info("h5 file = $(h5file(xdmf))")
E = read(xdmf.hdf, "/Topology/Quad4/Element IDs")
C = read(xdmf.hdf, "/Topology/Quad4/Connectivity")
N = read(xdmf.hdf, "/Node IDs")
X = read(xdmf.hdf, "/Geometry")
T1 = read(xdmf.hdf, "/Results/Time 0.0/Nodal Fields/Temperature")
T2 = read(xdmf.hdf, "/Results/Time 1.0/Nodal Fields/Temperature")
@test isapprox(E, [-1])
@test isapprox(C, [0 1 2 3])
@test isapprox(N, [1, 2, 3, 4])
@test isapprox(X, [0.0 0.0; 1.0 0.0; 1.0 1.0; 0.0 1.0]')
@test isapprox(T, [0.0 0.0 1.0 1.0])
X_expected = [0.0 0.0; 1.0 0.0; 1.0 1.0; 0.0 1.0]'
T1_expected = [0.0 0.0 1.0 1.0]
T2_expected = [0.0 0.0 0.5 0.5]
@test isapprox(X, X_expected)
@test isapprox(T1, T1_expected)
@test isapprox(T2, T2_expected)
@test isapprox(read(xdmf, "/Domain/Grid/Grid/Time/Value"), 0.0)
@test read(xdmf, "/Domain/Grid/Grid/Geometry/Type") == "XY"
@test isapprox(read(xdmf, "/Domain/Grid/Grid/Geometry/DataItem"), X_expected)
@test isapprox(read(xdmf, "/Domain/Grid/Grid/Topology/DataItem"), [0 1 2 3])
@test isapprox(read(xdmf, "/Domain/Grid/Grid/Topology[@TopologyType=Polyline]/DataItem"), [0 1])
@test isapprox(read(xdmf, "/Domain/Grid/Grid[1]/Attribute[@Name=Temperature]/DataItem"), T1_expected)
@test isapprox(read(xdmf, "/Domain/Grid/Grid[2]/Attribute[@Name=Temperature]/DataItem"), T2_expected)
@test isapprox(read(xdmf, "/Domain/Grid/Grid[end]/Time/Value"), 1.0)
@test isapprox(read(xdmf, "/Domain/Grid/Grid[end]/Topology/DataItem"), [0 1 2 3])
end