Skip to content

Instantly share code, notes, and snippets.

@mattwthompson
Created March 28, 2024 18:00
Show Gist options
  • Save mattwthompson/4e873bf0f2dc6c71f50fa9388d57dfae to your computer and use it in GitHub Desktop.
Save mattwthompson/4e873bf0f2dc6c71f50fa9388d57dfae to your computer and use it in GitHub Desktop.
Quick example using annotated types for Pint/Pydantic v2 compatibility
from openff.units import Quantity
from typing import Annotated
from pydantic import (
BeforeValidator,
AfterValidator,
BaseModel,
ValidationError,
PositiveFloat,
)
from functools import partial
def to_quantity(quantity: Quantity | str) -> Quantity:
try:
return Quantity(quantity)
except Exception as error:
raise ValueError from error
def has_compatible_dimensionality(quantity: Quantity, unit: str) -> Quantity:
if quantity.is_compatible_with(unit):
return quantity
else:
raise ValueError(
f"Dimensionality must be compatible with 'unit'",
)
is_compatible_with_length = partial(has_compatible_dimensionality, unit="angstrom")
is_compatible_with_mass = partial(has_compatible_dimensionality, unit="dalton")
is_compatible_with_temperature = partial(has_compatible_dimensionality, unit="kelvin")
is_compatible_with_pressure = partial(has_compatible_dimensionality, unit="atm")
LengthQuantity = Annotated[
Quantity,
BeforeValidator(to_quantity),
AfterValidator(is_compatible_with_length),
]
MassQuantity = Annotated[
Quantity,
BeforeValidator(to_quantity),
AfterValidator(is_compatible_with_mass),
]
TemperatureQuantity = Annotated[
Quantity,
BeforeValidator(to_quantity),
AfterValidator(is_compatible_with_temperature),
]
PressureQuantity = Annotated[
Quantity,
BeforeValidator(to_quantity),
AfterValidator(is_compatible_with_pressure),
]
"""
Downstream code would something like
"""
# from openff.somewhere import TemperatureQuantity, PressureQuantity
from pydantic import BaseModel, PositiveFloat, Field
class DownstreamBaseModel(BaseModel):
class Config:
arbitrary_types_allowed = True
# other stuff ...
class ThermoSettings(DownstreamBaseModel):
temperature: TemperatureQuantity = Field(description="Simulation temperature")
pressure: PressureQuantity = Field(description="Simulation pressure")
ph: PositiveFloat | None = Field(None, description="Simulation pH")
redox_potential: float | None = Field(
None, description="Simulation redox potential"
)
print(
ThermoSettings(
temperature=Quantity("300 K"), pressure=Quantity("1 atm")
).model_dump()
)
# {'temperature': <Quantity(300, 'kelvin')>, 'pressure': <Quantity(1, 'standard_atmosphere')>, 'ph': None, 'redox_potential': None}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment