JuliaIO / HDF5.jl

Save and load data in the HDF5 file format from Julia
https://juliaio.github.io/HDF5.jl
MIT License
380 stars 138 forks source link

Writing scalar datasets of compound types #1126

Open lukas-weber opened 8 months ago

lukas-weber commented 8 months ago

Hi,

I recently noticed that datatype() now works for all kinds of Julia structs! That’s such an awesome feature, allowing me to simplify my code a lot.

There is one remaining issue for me though. If you want to write a scalar dataset, this works fine with normal numbers and Complex

using HDF5
import ForwardDiff.Dual # just an example 

f = h5open("test.h5", "w")
dual = Dual{:test}(1.0, 0.1)

# these all work
f["x1"] = 1.0
f["x2"] = 1im
f["x3"] = [dual]
f["x4"] = [complex(dual, dual)]

# these fail
f["x5"] = dual
f["x6"] = complex(dual, dual)

However, x5 and x6 fail with the error

ERROR: MethodError: no method matching unsafe_convert(::Type{Ptr{Nothing}}, ::Dual{:test, Float64, 1})

Closest candidates are:
  unsafe_convert(::Union{Type{Ptr{Nothing}}, Type{Ptr{Base.Libc.FILE}}}, ::Base.Libc.FILE)
   @ Base libc.jl:94
  unsafe_convert(::Type{Ptr{T}}, ::Base.ReshapedArray{T}) where T
   @ Base reshapedarray.jl:283
  unsafe_convert(::Type{Ptr{T}}, ::Base.Threads.Atomic{T}) where T
   @ Base atomics.jl:328
  ...

Stacktrace:
  [1] h5d_write(dataset_id::HDF5.Dataset, mem_type_id::HDF5.Datatype, mem_space_id::Int64, file_space_id::Int64, xfer_plist_id::HDF5.DatasetTransferProperties, buf::Dual{:test, Float64, 1})
    @ HDF5.API ~/.julia/packages/HDF5/Ws1wH/src/api/functions.jl:908
  [2] write_dataset(dset::HDF5.Dataset, memtype::HDF5.Datatype, x::Dual{:test, Float64, 1}, xfer::HDF5.DatasetTransferProperties)
    @ HDF5 ~/.julia/packages/HDF5/Ws1wH/src/datasets.jl:490
  [3] write_dataset(dset::HDF5.Dataset, memtype::HDF5.Datatype, x::Dual{:test, Float64, 1})
    @ HDF5 ~/.julia/packages/HDF5/Ws1wH/src/datasets.jl:490
  [4] write_dataset(parent::HDF5.File, name::String, data::Dual{:test, Float64, 1}; pv::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ HDF5 ~/.julia/packages/HDF5/Ws1wH/src/datasets.jl:320
  [5] write_dataset(parent::HDF5.File, name::String, data::Dual{:test, Float64, 1})
    @ HDF5 ~/.julia/packages/HDF5/Ws1wH/src/datasets.jl:315
  [6] write(parent::HDF5.File, name::String, data::Dual{:test, Float64, 1}; pv::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ HDF5 ~/.julia/packages/HDF5/Ws1wH/src/datasets.jl:342
  [7] write(parent::HDF5.File, name::String, data::Dual{:test, Float64, 1})
    @ HDF5 ~/.julia/packages/HDF5/Ws1wH/src/datasets.jl:342
  [8] setindex!(parent::HDF5.File, val::Dual{:test, Float64, 1}, path::String; pv::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ HDF5 ~/.julia/packages/HDF5/Ws1wH/src/highlevel.jl:107
  [9] setindex!(parent::HDF5.File, val::Dual{:test, Float64, 1}, path::String)
    @ HDF5 ~/.julia/packages/HDF5/Ws1wH/src/highlevel.jl:90
 [10] top-level scope
    @ REPL[10]:14
musm commented 8 months ago

Yeah we don't support those as ScalarTypes. I'm not sure what would be the best approach here. Technically Dual is a struct and should be a CompoundData, but since it's <: Real we can't handle it.

lukas-weber commented 8 months ago

(x5 also fails with structs that are not <: Real)

Maybe a sensible default would be to consider structs scalar unless they are AbstractArrays (since if they are not, it is not clear how to get elements from them anyway).

mkitti commented 8 months ago

Maybe anything coming in via setindex! that is not an AbstractArray or a Tuple should be considered a scalar?