Skip to content

Commit

Permalink
multiple refs for dimensions
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewgsavage committed Apr 21, 2024
1 parent 21cac2b commit 04ede94
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 34 deletions.
3 changes: 1 addition & 2 deletions pint/default_en.txt
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,7 @@ atmosphere_liter = atmosphere * liter = atm_l
foot_pound = foot * force_pound = ft_lb = footpound

# Power
# TODO: implement multiple relations for kinds
[power] = [energy] / [time]# = [torque] * [angular_frequency] = W
[power] = [energy] / [time] = [torque] * [angular_frequency] = [volumetric_flow_rate] * [pressure] = W
watt = joule / second = W
volt_ampere = volt * ampere = VA
horsepower = 550 * foot * force_pound / second = hp = UK_horsepower = hydraulic_horsepower
Expand Down
39 changes: 19 additions & 20 deletions pint/delegates/txt_defparser/plain.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,32 +235,31 @@ def from_string_and_config(
if not (s.startswith("[") and "=" in s):
return None

name, value, *aliases = (p.strip() for p in s.split("="))
name, value, *alt_refs = (p.strip() for p in s.split("="))

preferred_unit = None
if aliases:
if aliases[0] == "_":
aliases = aliases[1:]
else:
preferred_unit, *aliases = aliases

aliases = tuple(alias for alias in aliases if alias not in ("", "_"))
if alt_refs:
if "[" not in alt_refs[-1]:
preferred_unit = alt_refs[-1]
alt_refs = alt_refs[:-1]
else:
alt_refs = []

if aliases:
return common.DefinitionSyntaxError(
"Derived dimensions cannot have aliases."
)
def to_dim_container(string):
try:
reference = config.to_dimension_container(string)
except common.DefinitionSyntaxError as exc:
return common.DefinitionSyntaxError(
f"In {name} derived dimensions must only be referenced "
f"to dimensions. {exc}"
)
return reference

try:
reference = config.to_dimension_container(value)
except common.DefinitionSyntaxError as exc:
return common.DefinitionSyntaxError(
f"In {name} derived dimensions must only be referenced "
f"to dimensions. {exc}"
)
reference = to_dim_container(value)
alternate_references = tuple([to_dim_container(ref) for ref in alt_refs])

try:
return cls(name.strip(), reference, preferred_unit)
return cls(name.strip(), reference, preferred_unit, alternate_references)
except Exception as exc:
return common.DefinitionSyntaxError(str(exc))

Expand Down
21 changes: 13 additions & 8 deletions pint/facets/plain/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,22 +236,19 @@ class DerivedDimensionDefinition(DimensionDefinition):
reference: UnitsContainer
# preferred unit
preferred_unit: str | None = None
alternate_references: ty.Tuple[UnitsContainer, ...] = ()

@property
def is_base(self) -> bool:
return False

def __post_init__(self):
if not errors.is_valid_dimension_name(self.name):
raise self.def_err(errors.MSG_INVALID_DIMENSION_NAME)

if not all(map(errors.is_dim, self.reference.keys())):
return self.def_err(
def _validate_reference(self, reference: UnitsContainer) -> None:
if not all(map(errors.is_dim, reference.keys())):
raise self.def_err(
"derived dimensions must only reference other dimensions"
)

invalid = tuple(
itertools.filterfalse(errors.is_valid_dimension_name, self.reference.keys())
itertools.filterfalse(errors.is_valid_dimension_name, reference.keys())
)

if invalid:
Expand All @@ -260,6 +257,14 @@ def __post_init__(self):
+ errors.MSG_INVALID_DIMENSION_NAME
)

def __post_init__(self):
if not errors.is_valid_dimension_name(self.name):
raise self.def_err(errors.MSG_INVALID_DIMENSION_NAME)

self._validate_reference(self.reference)
for ref in self.alternate_references:
self._validate_reference(ref)


@dataclass(frozen=True)
class AliasDefinition(errors.WithDefErr):
Expand Down
14 changes: 11 additions & 3 deletions pint/facets/plain/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,12 +565,20 @@ def _add_derived_dimension(self, definition: DerivedDimensionDefinition) -> None
self._add_dimension(DimensionDefinition(dim_name))
self._helper_adder(definition, self._dimensions, None)

for kind, container in self._rearrange_dimension_definition(definition):
for kind, container in self._rearrange_dimension_definition(
definition.name, definition.reference
):
self._kind_relations.setdefault(kind, set()).add(container)

def _rearrange_dimension_definition(self, definition: DimensionDefinition):
for alt_ref in definition.alternate_references:
for kind, container in self._rearrange_dimension_definition(
definition.name, alt_ref
):
self._kind_relations.setdefault(kind, set()).add(container)

def _rearrange_dimension_definition(self, name, reference):
# rearrange to give a UnitContainer that is dimensionless
dimensionless = definition.reference * UnitsContainer({definition.name: -1})
dimensionless = reference * UnitsContainer({name: -1})
return [
(kind, (dimensionless / UnitsContainer({kind: exp})) ** (-1 / exp))
for kind, exp in dimensionless.items()
Expand Down
1 change: 0 additions & 1 deletion pint/testsuite/test_kind.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,6 @@ def test_kindunit(self):
# assert Kind("[force]") in newton.compatible_kinds()
assert "[force]" in newton.compatible_kinds()

@pytest.mark.xfail(reason="Need to implement multiple relations for a kind")
def test_waterpump(self):
ureg = UnitRegistry()
Q_ = ureg.Quantity
Expand Down

0 comments on commit 04ede94

Please sign in to comment.