mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-17 17:22:10 +00:00
better xdmf support
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -192,4 +192,3 @@ end
|
||||
function xdmf_save!(xdmf, filename)
|
||||
save_file(xdmf.xdoc, filename)
|
||||
end
|
||||
|
||||
|
||||
+34
-30
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user