Serialization#

PyAnsys Units can save quantities and units to dictionaries, JSON, strings, and pickle files, and restore them.

Dictionaries#

to_dict() returns built-in Python types only. Array values become nested lists, and extra fields given to the Quantity constructor are kept:

from ansys.units import Quantity, Unit

q = Quantity([1.0, 2.0], "m s^-1", source="sensor")
data = q.to_dict()  # {'value': [1.0, 2.0], 'units': 'm s^-1', 'source': 'sensor'}
Quantity.from_dict(data)  # Quantity ([1. 2.], "m s^-1")

Unit("kg m^-3").to_dict()  # {'units': 'kg m^-3'}
Unit.from_dict({"units": "kg m^-3"})  # Unit("kg m^-3")

JSON#

Quantity works as a field in Pydantic models, which use the same dictionary format:

from pydantic import BaseModel, ConfigDict
from ansys.units import Quantity


class Measurement(BaseModel):
    # Keep NaN and infinity, which are written as null by default.
    model_config = ConfigDict(ser_json_inf_nan="constants")

    length: Quantity


text = Measurement(length=Quantity(2.0, "m")).model_dump_json()
Measurement.model_validate_json(text).length  # Quantity (2.0, "m")

Without Pydantic, use the json module with to_dict() and from_dict():

import json
from ansys.units import Quantity

text = json.dumps(Quantity(2.0, "m").to_dict())
Quantity.from_dict(json.loads(text))  # Quantity (2.0, "m")

Strings#

Quantity.from_string() parses the output of str(quantity):

from ansys.units import Quantity

Quantity.from_string("5.0 m s^-1")  # Quantity (5.0, "m s^-1")
Quantity.from_string(str(Quantity([1.0, 2.0], "kg")))  # Quantity ([1. 2.], "kg")

NumPy shortens the string of large arrays with .... Such strings can’t be parsed, so use to_dict() for large arrays.

Pickle#

Units are pickled by name and resolved again when loaded from a pickle. Pickle files created with earlier versions of PyAnsys Units can still be loaded.

Custom units#

Units are stored by name. Custom units registered with UnitRegistry.register_unit() are also stored with their definition, so they can be restored without the registry:

from ansys.units import Quantity, UnitRegistry

ureg = UnitRegistry()
ureg.register_unit(unit="micron", composition="m", factor=1e-6)

data = ureg.Quantity(3.0, "micron").to_dict()
# {'value': 3.0, 'units': 'micron',
#  'unit_definition': {'composition': 'm', 'factor': 1e-06}}

Quantity.from_dict(data).to("m")  # Quantity (3e-06, "m")

When loading, a unit name is looked up in this order:

  1. The registry argument of from_dict() or from_string().

  2. The registry set with set_application_registry(). Loading from a pickle also uses this registry.

  3. The built-in units.

import pickle
from ansys.units import Quantity, UnitRegistry, set_application_registry

ureg = UnitRegistry()
ureg.register_unit(unit="micron", composition="m", factor=1e-6)
set_application_registry(ureg)

Quantity.from_string("3.0 micron")  # Quantity (3.0, "micron")
pickle.loads(pickle.dumps(ureg.Quantity(3.0, "micron")))  # Quantity (3.0, "micron")

If the stored definition of a custom unit differs from the unit with the same name in the registry, loading raises a ConflictingUnitDefinition error rather than returning wrong values. A unit name that can’t be resolved raises an UnresolvableUnit error.