HDF5 specification in Ubermag#

Version 0.1#

This document is valid for HDF5 files written with ubermag >= 2023.10.

attributes

Each HDF5 file written with Ubermag has three standard attributes in the root group.

  • file-creation-time-UTC: str: Date and time (UTC) when the file was created in ISO 8601 format

  • type: str: The type of data in that file, expressed in terms of which class in Ubermag it represents. Currently specified are:

    • discretisedfield.Field: hdf5 file contains a single Field

  • ubermag-hdf5-file-version: str: Version of this file format specification.

The versions of all Ubermag libraries involved in writing the file are saved as additional attributes:

  • discretisedfield.__version__ (str): the version of discretisedfield used to save the Field.

[1]:
import discretisedfield as df
import h5glance

Single field: discretisedfield#

An HDF5 file with type: discretisedfield.Field contains a single field. It can be written with field.to_file('<name>.hdf5|.h5').

The file has three nested groups, called field, mesh, and region.

Example:

[2]:
!h5glance --attrs hdf5_examples/discretisedfield-Field.h5
hdf5_examples/discretisedfield-Field.h5
├4 attributes:
│ ├discretisedfield.__version__: '0.90.0'
│ ├file-creation-time-UTC: '2023-10-20T13:00:38'
│ ├type: 'discretisedfield.Field'
│ └ubermag-hdf5-file-version: '0.1'
└field
  ├3 attributes:
  │ ├nvdim: 3
  │ ├unit: 'A/m'
  │ └vdims: ['mx' 'my' 'mz']
  ├array [float64: 24 × 10 × 4 × 3]
  ├mesh
  │ ├2 attributes:
  │ │ ├bc: 'xy'
  │ │ └n: [24 10  4]
  │ ├region
  │ │ └6 attributes:
  │ │   ├dims: ['x' 'y' 'z']
  │ │   ├ndim: 3
  │ │   ├pmax: [1.0e-07 2.5e-08 1.0e-08]
  │ │   ├pmin: [-2.0e-08 -2.5e-08 -1.0e-08]
  │ │   ├tolerance_factor: 1e-12
  │ │   └units: ['m' 'm' 'm']
  │ ├subregion_names     [UTF-8 string: 2]
  │ └subregions  [float64: 2 × 6]
  └valid [enum (FALSE, TRUE): 24 × 10 × 4]

The first group is called field. It contains:#

attributes

  • nvdim (int): Number of Value DIMensions of the field

  • unit (str): unit of the field; if not set it contains the value 'None'

  • vdims (list[str], length nvdim): names of the vdims

datasets

  • array (array[mesh.n, nvdim], numbers.Number): field data

  • valid (array[mesh.n], bool): boolean mask to mark (in)valid parts of array

groups

  • mesh (h5py.Group): mesh on which the field is defined

The second group is called mesh. It contains:#

attributes

  • bc (str): information about boundary conditions as defined in discretisedfield.Mesh.bc

  • n (list[int], length region.ndim): number of cells along the different spatial directions

datasets (optional, only present if subregions are defined)

  • subregion_names (list[str]): names of the individual subregions defined for the field, only present if subregions are defined

  • subregions (array[<number of subregions>, region.ndim*2]): coordinates of discretisedfield.Region.pmin and discretisedfield.Region.pmax, subregions and subregion names are combined based on their order, only present if subregions are defined

groups

  • region (h5py.Group): region on which the mesh is defined

The third group is called region. It contains:#

attributes

  • dims (list[str], length ndim): names of spatial dimensions

  • ndim (int): number of spatial dimensions

  • pmin (list[number], length ndim): coordinates of the minimum point discretisedfield.Region.pmin

  • pmax (list[number], length ndim): coordinates of the minimum point discretisedfield.Region.pmin

  • tolerance_factor (float): discretisedfield.Region.tolerance_factor

  • units (list[str], length ndim): units of the spatial dimensions

Examples:#

1d field with 1 vdim

[3]:
mesh = df.Mesh(p1=0, p2=20, n=20)
field_1d_scalar = df.Field(mesh, nvdim=1, value=0)
field_1d_scalar
[3]:
Field
  • Mesh
    • Region
      • pmin = [0]
      • pmax = [20]
      • dims = ['x']
      • units = ['m']
    • n = [20]
  • nvdim = 1
[4]:
field_1d_scalar.to_file("field_1d_scalar.h5")
[5]:
!h5glance --attrs field_1d_scalar.h5
field_1d_scalar.h5
├4 attributes:
│ ├discretisedfield.__version__: '0.90.0'
│ ├file-creation-time-UTC: '2023-10-20T13:03:32'
│ ├type: 'discretisedfield.Field'
│ └ubermag-hdf5-file-version: '0.1'
└field
  ├3 attributes:
  │ ├nvdim: 1
  │ ├unit: 'None'
  │ └vdims: 'None'
  ├array [float64: 20 × 1]
  ├mesh
  │ ├2 attributes:
  │ │ ├bc: ''
  │ │ └n: [20]
  │ └region
  │   └6 attributes:
  │     ├dims: ['x']
  │     ├ndim: 1
  │     ├pmax: [20]
  │     ├pmin: [0]
  │     ├tolerance_factor: 1e-12
  │     └units: ['m']
  └valid [enum (FALSE, TRUE): 20]
[6]:
df.Field.from_file("field_1d_scalar.h5") == field_1d_scalar
[6]:
True

1d field with 2 vdims

[7]:
mesh = df.Mesh(p1=0, p2=20, n=20)
field_1d = df.Field(mesh, nvdim=2, value=(0, 1))
field_1d
[7]:
Field
  • Mesh
    • Region
      • pmin = [0]
      • pmax = [20]
      • dims = ['x']
      • units = ['m']
    • n = [20]
  • nvdim = 2
  • vdims:
    • x
    • y
[8]:
field_1d.to_file("field_1d_vector.h5")
[9]:
!h5glance --attrs field_1d_vector.h5
field_1d_vector.h5
├4 attributes:
│ ├discretisedfield.__version__: '0.90.0'
│ ├file-creation-time-UTC: '2023-10-20T13:03:32'
│ ├type: 'discretisedfield.Field'
│ └ubermag-hdf5-file-version: '0.1'
└field
  ├3 attributes:
  │ ├nvdim: 2
  │ ├unit: 'None'
  │ └vdims: ['x' 'y']
  ├array [float64: 20 × 2]
  ├mesh
  │ ├2 attributes:
  │ │ ├bc: ''
  │ │ └n: [20]
  │ └region
  │   └6 attributes:
  │     ├dims: ['x']
  │     ├ndim: 1
  │     ├pmax: [20]
  │     ├pmin: [0]
  │     ├tolerance_factor: 1e-12
  │     └units: ['m']
  └valid [enum (FALSE, TRUE): 20]
[10]:
df.Field.from_file("field_1d_vector.h5") == field_1d
[10]:
True

3d field with 3 vdims

[11]:
region = df.Region(p1=(-20e-9, -25e-9, -10e-9), p2=(100e-9, 25e-9, 10e-9))
mesh = df.Mesh(
    region=region,
    cell=(5e-9, 5e-9, 5e-9),
    bc="xy",
    subregions={
        "sr1": df.Region(p1=(0, 0, 0), p2=(5e-9, 5e-9, 5e-9)),
        "sr2": region,
    },
)


def init_norm(p):
    x, y, _ = p
    if x > 50e-9 and abs(y) > 15e-9:
        return 0
    return 1e4


field_3d = df.Field(
    mesh,
    nvdim=3,
    value=mesh.coordinate_field(),
    norm=init_norm,
    vdims=["mx", "my", "mz"],
    valid="norm",
    unit="A/m",
)
field_3d
[11]:
Field
  • Mesh
    • Region
      • pmin = [-2e-08, -2.5e-08, -1e-08]
      • pmax = [1e-07, 2.5e-08, 1e-08]
      • dims = ['x', 'y', 'z']
      • units = ['m', 'm', 'm']
    • n = [24, 10, 4]
    • bc = xy
    • subregions:
      • Region sr1
        • pmin = [0.0, 0.0, 0.0]
        • pmax = [5e-09, 5e-09, 5e-09]
        • dims = ['x', 'y', 'z']
        • units = ['m', 'm', 'm']
      • Region sr2
        • pmin = [-2e-08, -2.5e-08, -1e-08]
        • pmax = [1e-07, 2.5e-08, 1e-08]
        • dims = ['x', 'y', 'z']
        • units = ['m', 'm', 'm']
  • nvdim = 3
  • vdims:
    • mx
    • my
    • mz
  • unit = A/m
[12]:
field_3d.to_file("field_3d.h5")
[13]:
!h5glance --attrs field_3d.h5
field_3d.h5
├4 attributes:
│ ├discretisedfield.__version__: '0.90.0'
│ ├file-creation-time-UTC: '2023-10-20T13:03:33'
│ ├type: 'discretisedfield.Field'
│ └ubermag-hdf5-file-version: '0.1'
└field
  ├3 attributes:
  │ ├nvdim: 3
  │ ├unit: 'A/m'
  │ └vdims: ['mx' 'my' 'mz']
  ├array [float64: 24 × 10 × 4 × 3]
  ├mesh
  │ ├2 attributes:
  │ │ ├bc: 'xy'
  │ │ └n: [24 10  4]
  │ ├region
  │ │ └6 attributes:
  │ │   ├dims: ['x' 'y' 'z']
  │ │   ├ndim: 3
  │ │   ├pmax: [1.0e-07 2.5e-08 1.0e-08]
  │ │   ├pmin: [-2.0e-08 -2.5e-08 -1.0e-08]
  │ │   ├tolerance_factor: 1e-12
  │ │   └units: ['m' 'm' 'm']
  │ ├subregion_names     [UTF-8 string: 2]
  │ └subregions  [float64: 2 × 6]
  └valid [enum (FALSE, TRUE): 24 × 10 × 4]
[14]:
df.Field.from_file("field_3d.h5") == field_3d
[14]:
True

Multiple fields or time series: micromagneticdata#

Currently not specified

Internal notes:#

The top-level name of the field, which for discretisedfield.Field is fixed to field, is arbitrary and has to be defined in the new code. The internal structure of the field group matches the one described above with one difference: array can have an additional variable first dimension: array[<extra-dim>, mesh.n, nvdim], which can e.g. be use for time-series information. Writing fields can be done with the private methods Field._h5_save_structure, and Field._h5_save_data. The former saves all “metadata” such as the mesh and valid and should only be called once. It requires a h5py.group for the field, which has to be defined separately, and the array shape as inputs. The latter can write data of individual Fields into parts of the array dataset. It should be called for each field in the series.

Changes#

  • HDF5 files written with ubermag<=0.66.1 have a different structure and lack significant parts of the metadata, e.g. subregion information and valid. discretisedfield can read these files with the same method.

[15]:
# cleanup
!rm field_1d_scalar.h5 field_1d_vector.h5 field_3d.h5