sasdata.metadata module

Contains classes describing the metadata for a scattering run

The metadata is structures around the CANSas format version 1.1, found at https://www.cansas.org/formats/canSAS1d/1.1/doc/specification.html

Metadata from other file formats should be massaged to fit into the data classes presented here. Any useful metadata which cannot be included in these classes represent a bug in the CANSas format.

class sasdata.metadata.Aperture(*, distance: sasdata.quantities.quantity.Quantity[float] | None, size: sasdata.metadata.Vec3 | None, size_name: str | None, name: str | None, type_: str | None)

Bases: object

as_h5(group: Group)

Export data onto an HDF5 group

distance: Quantity[float] | None
static from_json(obj)
name: str | None
size: Vec3 | None
size_name: str | None
summary()
type_: str | None
class sasdata.metadata.BeamSize(*, name: str | None, size: sasdata.metadata.Vec3 | None)

Bases: object

as_h5(group: Group)

Export data onto an HDF5 group

static from_json(obj)
name: str | None
size: Vec3 | None
class sasdata.metadata.Collimation(*, length: Quantity[float] | None, apertures: list[Aperture])

Bases: object

Class to hold collimation information

apertures: list[Aperture]
as_h5(group: Group)

Export data onto an HDF5 group

static from_json(obj)
length: Quantity[float] | None
summary()
class sasdata.metadata.Detector(*, name: str | None, distance: Quantity[float] | None, offset: Vec3 | None, orientation: Rot3 | None, beam_center: Vec3 | None, pixel_size: Vec3 | None, slit_length: Quantity[float] | None)

Bases: object

Detector information

as_h5(group: Group)

Export data onto an HDF5 group

beam_center: Vec3 | None
distance: Quantity[float] | None
static from_json(obj)
name: str | None
offset: Vec3 | None
orientation: Rot3 | None
pixel_size: Vec3 | None
slit_length: Quantity[float] | None
summary()
class sasdata.metadata.Instrument(collimations: list[sasdata.metadata.Collimation], source: sasdata.metadata.Source | None, detector: list[sasdata.metadata.Detector])

Bases: object

as_h5(group: Group)

Export data onto an HDF5 group

collimations: list[Collimation]
detector: list[Detector]
static from_json(obj)
source: Source | None
summary()
class sasdata.metadata.MetaNode(*, name: str, attrs: dict[str, str], contents: str | sasdata.quantities.quantity.Quantity | numpy.ndarray | list['MetaNode'])

Bases: object

attrs: dict[str, str]
contents: str | Quantity | ndarray | list[MetaNode]
filter(name: str) list[ndarray | Quantity | str]
static from_json(obj)
name: str
to_string(header='')

Convert node to pretty printer string

class sasdata.metadata.Metadata(*, title: str | None, run: list[str], definition: str | None, process: list[sasdata.metadata.Process], sample: sasdata.metadata.Sample | None, instrument: sasdata.metadata.Instrument | None, raw: sasdata.metadata.MetaNode)

Bases: object

as_h5(f: Group)

Export data onto an HDF5 group

definition: str | None
static from_json(obj)
instrument: Instrument | None
process: list[Process]
raw: MetaNode
run: list[str]
sample: Sample | None
summary()
title: str | None
class sasdata.metadata.MetadataEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)

Bases: JSONEncoder

default(obj)

Implement this method in a subclass such that it returns a serializable object for o, or calls the base implementation (to raise a TypeError).

For example, to support arbitrary iterators, you could implement default like this:

def default(self, o):
    try:
        iterable = iter(o)
    except TypeError:
        pass
    else:
        return list(iterable)
    # Let the base class default method raise the TypeError
    return super().default(o)
class sasdata.metadata.Process(*, name: str | None, date: str | None, description: str | None, terms: dict[str, str | Quantity[float]], notes: list[str])

Bases: object

Class that holds information about the processes performed on the data.

as_h5(group: Group)

Export data onto an HDF5 group

date: str | None
description: str | None
static from_json(obj)
name: str | None
notes: list[str]
single_line_desc()

Return a single line string representing the process

summary()
terms: dict[str, str | Quantity[float]]
class sasdata.metadata.Rot3(*, roll: Quantity[float] | None, pitch: Quantity[float] | None, yaw: Quantity[float] | None)

Bases: object

A measured rotation in 3-space

as_h5(f: Group)

Export data onto an HDF5 group

static from_json(obj: dict) Quantity | None
pitch: Quantity[float] | None
roll: Quantity[float] | None
yaw: Quantity[float] | None
class sasdata.metadata.Sample(*, name: str | None, sample_id: str | None, thickness: Quantity[float] | None, transmission: float | None, temperature: Quantity[float] | None, position: Vec3 | None, orientation: Rot3 | None, details: list[str])

Bases: object

Class to hold the sample description

as_h5(f: Group)

Export data onto an HDF5 group

details: list[str]
static from_json(obj)
name: str | None
orientation: Rot3 | None
position: Vec3 | None
sample_id: str | None
summary() str
temperature: Quantity[float] | None
thickness: Quantity[float] | None
transmission: float | None
class sasdata.metadata.Source(*, radiation: str | None, beam_shape: str | None, beam_size: sasdata.metadata.BeamSize | None, wavelength: sasdata.quantities.quantity.Quantity[float] | None, wavelength_min: sasdata.quantities.quantity.Quantity[float] | None, wavelength_max: sasdata.quantities.quantity.Quantity[float] | None, wavelength_spread: sasdata.quantities.quantity.Quantity[float] | None)

Bases: object

as_h5(group: Group)

Export data onto an HDF5 group

beam_shape: str | None
beam_size: BeamSize | None
static from_json(obj)
radiation: str | None
summary() str
wavelength: Quantity[float] | None
wavelength_max: Quantity[float] | None
wavelength_min: Quantity[float] | None
wavelength_spread: Quantity[float] | None
class sasdata.metadata.TagCollection(*, singular: set[str] = <factory>, variable: set[str] = <factory>)

Bases: object

The collected tags and their variability.

singular: set[str]
variable: set[str]
class sasdata.metadata.Vec3(*, x: Quantity[float] | None, y: Quantity[float] | None, z: Quantity[float] | None)

Bases: object

A three-vector of measured quantities

as_h5(f: Group)

Export data onto an HDF5 group

static from_json(obj: dict) Quantity | None
x: Quantity[float] | None
y: Quantity[float] | None
z: Quantity[float] | None
sasdata.metadata.access_meta(obj: dataclass, key: str) Any | None

Use a string accessor to locate a key from within the data object.

The basic grammar of these accessors explicitly match the python syntax for accessing the data. For example, to access the name field within the object person, you would call access_meta(person, “.name”). Similarly, lists and dicts are access with square brackets.

> assert access_meta(person, ‘.name’) == person.name > assert access_meta(person, ‘.phone.home’) == person.phone.home > assert access_meta(person, ‘.addresses[0].postal_code’) == person.address[0].postal_code > assert access_meta(person, ‘.children[“Taylor”]’) == person.children[“Taylor”]

Obviously, when the accessor is know ahead of time, access_meta provides no benefit over directly retrieving the data. However, when a data structure is loaded at runtime (e.g. the metadata of a neutron scattering file), then it isn’t possible to know in advance the location of the specific value that the user desires. access_meta allows the user to provide the location at runtime.

This function returns None when the key is not a valid address for any data within the structure. Since the leaf could be any type that is not a list, dict, or dataclass, the return type of the function is Any | None.

The list of locations within a structure is given by the meta_tags function.

sasdata.metadata.collect_tags(objs: list[dataclass]) TagCollection

Identify uniform and varying data within a groups of data objects

The resulting TagCollection contains every accessor string that is valid for every object in the objs list. For example, if obj.name is a string for every obj in objs, then the string “.name” will be present in one of the two sets in the tags collection.

To be more specific, if obj.name exists and has the same value for every obj in objs, the string “.name” will be included in the singular set. If there are at least two distinct values for obj.name, then “.name” will be in the variable set.

sasdata.metadata.from_json_quantity(obj: dict) Quantity | None
sasdata.metadata.meta_tags(obj: dataclass) list[str]

Find all leaf accessors from a data object.

The function treats the passed in object as a tree. Lists, dicts, and dataclasses are all treated as branches on the tree and any other type is treated as a leaf. The function then returns a list of strings, where each string is a “path” from the root of the tree to one leaf. The structure of the path is designed to mimic the python code to access that specific leaf value.

These accessors allow us to treat accessing entries within a structure as first class values. This list can then be presented to the user to allow them to select specific information within the larger structure. This is particularly important when plotting against a specific date value within the structure.

Example:

>@dataclass
class Thermometer:

temperature: float units: str params: list

> item = Example() > item.temperature = 273 > item.units = “K” > item.old_values = [{‘date’: ‘2025-08-12’, ‘temperature’: 300’}] > assert meta_tags(item) = [‘.temperature’, ‘.units’, ‘.old_values[0][“date”]’, ‘.old_values[0][“temperature”]’]

The actual value of the leaf object specified by a path can be retrieved with the access_meta function.