nexusLIMS.schemas.units#

Pint unit registry and utilities for NexusLIMS metadata.

This module provides a centralized Pint unit registry for handling physical quantities with units in NexusLIMS metadata. It defines preferred units for different measurement types and provides utilities for normalizing quantities to these preferred units.

The module supports three-tiered unit serialization:

  • Tier 1 (Internal): Pint Quantity objects with QUDT/EMG mappings

  • Tier 2 (XML): Clean name/value/unit separation using XSD unit attribute

  • Tier 3 (Future): Optional QUDT/EMG URIs for semantic web integration

Examples:

Create and normalize quantities:

>>> from nexusLIMS.schemas.units import ureg, normalize_quantity
>>> voltage = ureg.Quantity(10000, "volt")
>>> normalized = normalize_quantity("acceleration_voltage", voltage)
>>> print(normalized)
10.0 kilovolt

Parse from strings:

>>> from nexusLIMS.schemas.units import parse_quantity
>>> voltage = parse_quantity("acceleration_voltage", "10 kV")
>>> print(voltage)
10.0 kilovolt

Serialize for XML:

>>> from nexusLIMS.schemas.units import quantity_to_xml_parts
>>> name, value, unit = quantity_to_xml_parts("acceleration_voltage", voltage)
>>> print(f"<meta name='{name}' unit='{unit}'>{value}</meta>")
<meta name='Voltage' unit='kV'>10.0</meta>

Module Contents#

Functions#

normalize_quantity

Normalize a quantity to its preferred unit for the given field.

parse_quantity

Parse a value into a Pint Quantity and normalize to preferred units.

quantity_to_xml_parts

Convert a field name and quantity to XML serialization parts.

get_qudt_uri

Get the QUDT URI for a Pint Quantity’s unit.

serialize_quantity

Serialize a Pint Quantity to a JSON-compatible dictionary.

deserialize_quantity

Deserialize a dictionary back to a Pint Quantity.

Data#

API#

nexusLIMS.schemas.units.logger#

‘getLogger(…)’

nexusLIMS.schemas.units.ureg#

‘UnitRegistry(…)’

nexusLIMS.schemas.units.QUDT_UNIT_TTL_PATH#
nexusLIMS.schemas.units.QUDT_VERSION#

‘3.1.9’

nexusLIMS.schemas.units.QUDT_UNIT#

‘Namespace(…)’

nexusLIMS.schemas.units.PREFERRED_UNITS#
nexusLIMS.schemas.units.normalize_quantity(field_name: str, quantity: Any) Any[source]#

Normalize a quantity to its preferred unit for the given field.

Takes a Pint Quantity and converts it to the canonical unit defined in PREFERRED_UNITS for that field. If no preferred unit is defined, returns the quantity unchanged. Non-Quantity values are passed through.

Parameters:
  • field_name (str) – The metadata field name (e.g., “acceleration_voltage”, “working_distance”)

  • quantity (Any) –

    The quantity to normalize. Can be:

    • Pint Quantity object (will be converted)

    • String (returned unchanged - use parse_quantity first)

    • Numeric value (returned unchanged)

    • None (returned unchanged)

Returns:

The normalized quantity in preferred units, or the original value if not a Quantity or no preferred unit is defined

Return type:

Any

Examples:

>>> voltage = ureg.Quantity(10000, "volt")
>>> normalized = normalize_quantity("acceleration_voltage", voltage)
>>> print(normalized)
10.0 kilovolt
>>> current = ureg.Quantity(0.1, "nanoampere")
>>> normalized = normalize_quantity("beam_current", current)
>>> print(normalized)
100.0 picoampere
>>> # Non-Quantity values pass through
>>> normalize_quantity("unknown_field", "some string")
'some string'
>>> # Fields without preferred units return unchanged
>>> qty = ureg.Quantity(5.0, "furlong")
>>> normalize_quantity("custom_field", qty) == qty
True
nexusLIMS.schemas.units.parse_quantity(field_name: str, value: Any) Any[source]#

Parse a value into a Pint Quantity and normalize to preferred units.

Accepts multiple input types:

  • Pint Quantity: Normalized to preferred units

  • String: Parsed as quantity (e.g., “10 kV”, “5.2 mm”)

  • Numeric: Assumed to be in preferred units for field

  • None: Passed through unchanged

Parameters:
  • field_name (str) – The metadata field name (e.g., “acceleration_voltage”)

  • value (Any) – The value to parse. Can be Quantity, string, numeric, or None

Returns:

Pint Quantity in preferred units, or original value if unparseable

Return type:

Any

Examples:

>>> qty = parse_quantity("acceleration_voltage", "10 kV")
>>> print(qty)
10.0 kilovolt
>>> qty = parse_quantity("working_distance", 5.2)  # Assumes mm
>>> print(qty)
5.2 millimeter
>>> qty = parse_quantity("beam_current", ureg.Quantity(0.1, "nA"))
>>> print(qty)
100.0 picoampere
>>> parse_quantity("operator", None) is None
True
nexusLIMS.schemas.units.quantity_to_xml_parts(field_name: str, quantity: Any) tuple[str, str, str | None][source]#

Convert a field name and quantity to XML serialization parts.

Extracts the display name, numeric value, and unit string for XML serialization. This enables clean XML output like: <meta name="Voltage" unit="kV">10.0</meta>

Parameters:
  • field_name (str) – The internal field name (e.g., “acceleration_voltage”)

  • quantity (Any) – The quantity value (Pint Quantity, string, or numeric)

Returns:

A 3-tuple of (display_name, value_string, unit_string)

  • display_name: Human-readable field name for XML

  • value_string: Numeric value as string

  • unit_string: Unit abbreviation, or None if dimensionless/non-quantity

Return type:

tuple[str, str, str | None]

Examples:

>>> qty = ureg.Quantity(10.0, "kilovolt")
>>> name, value, unit = quantity_to_xml_parts("acceleration_voltage", qty)
>>> print(f"<meta name='{name}' unit='{unit}'>{value}</meta>")
<meta name='Voltage' unit='kV'>10.0</meta>
>>> qty = ureg.Quantity(5000, "dimensionless")
>>> name, value, unit = quantity_to_xml_parts("magnification", qty)
>>> print(f"<meta name='{name}'>{value}</meta>")  # No unit attr
<meta name='Magnification'>5000</meta>

Notes:

For non-Quantity values, the value is converted to string and unit is None. Display name mapping is handled by separate EM Glossary utilities.

nexusLIMS.schemas.units.get_qudt_uri(quantity: Any) str | None[source]#

Get the QUDT URI for a Pint Quantity’s unit.

Returns the QUDT (Quantities, Units, Dimensions and Data Types) ontology URI for the quantity’s unit. This enables Tier 3 semantic web integration.

The mapping is loaded dynamically from the QUDT unit vocabulary file (qudt_unit.ttl) using RDFLib.

Parameters:

quantity (Any) – A Pint Quantity object

Returns:

QUDT URI string, or None if not a Quantity or URI not found

Return type:

str or None

Examples:

>>> qty = ureg.Quantity(10, "kilovolt")
>>> get_qudt_uri(qty)
'http://qudt.org/vocab/unit/KiloV'
>>> qty = ureg.Quantity(5.2, "millimeter")
>>> get_qudt_uri(qty)
'http://qudt.org/vocab/unit/MilliM'
>>> get_qudt_uri("not a quantity")
# Returns None
nexusLIMS.schemas.units.serialize_quantity(quantity: Any) dict[str, Any][source]#

Serialize a Pint Quantity to a JSON-compatible dictionary.

Converts a Quantity to a dict with ‘value’ and ‘units’ keys. Used for internal storage or JSON export. For XML serialization, use quantity_to_xml_parts() instead.

Parameters:

quantity (Any) – A Pint Quantity object, or other value to serialize

Returns:

Dictionary with ‘value’ and ‘units’ keys if Quantity, or {‘value’: quantity} for non-Quantity values

Return type:

dict[str, Any]

Examples:

>>> qty = ureg.Quantity(10, "kilovolt")
>>> serialize_quantity(qty)
{'value': 10.0, 'units': 'kilovolt'}
>>> serialize_quantity("some string")
{'value': 'some string'}
nexusLIMS.schemas.units.deserialize_quantity(data: dict[str, Any]) Any[source]#

Deserialize a dictionary back to a Pint Quantity.

Reverses the operation of serialize_quantity(). Takes a dict with ‘value’ and ‘units’ keys and reconstructs the Quantity.

Parameters:

data (dict[str, Any]) – Dictionary with ‘value’ and ‘units’ keys, or just ‘value’ key

Returns:

Pint Quantity if dict has value/units, otherwise the ‘value’ field

Return type:

Any

Examples:

>>> data = {'value': 10.0, 'units': 'kilovolt'}
>>> qty = deserialize_quantity(data)
>>> print(qty)
10.0 kilovolt
>>> data = {'value': 'some string'}
>>> deserialize_quantity(data)
'some string'