Skip to content

Commit

Permalink
Merge branch 'master' into ecosystem
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewgsavage authored May 13, 2024
2 parents 4bd86d2 + ad11d3a commit be714f2
Show file tree
Hide file tree
Showing 24 changed files with 200 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
numpy: [null, "numpy>=1.23,<2.0.0"]
numpy: [null, "numpy>=1.23,<2.0.0", "numpy>=2.0.0rc1"]
uncertainties: [null, "uncertainties==3.1.6", "uncertainties>=3.1.6,<4.0.0"]
extras: [null]
include:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ MANIFEST
.mypy_cache
pip-wheel-metadata
pint/testsuite/dask-worker-space
venv
.envrc

# WebDAV file system cache files
.DAV/
Expand Down
17 changes: 17 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,28 @@ Pint Changelog
0.24 (unreleased)
-----------------

- Fix detection of invalid conversion between offset and delta units. (PR #1905)
- Added dBW, decibel Watts, which is used in RF high power applications
- NumPy 2.0 support
(PR #1985, #1971)
- Implement numpy roll (Related to issue #981)
- Add `dim_sort` function to _formatter_helpers.
- Add `dim_order` and `default_sort_func` properties to FullFormatter.
(PR #1926, fixes Issue #1841)
- Documented packages using pint.
(PR #1960)
- Fixed bug causing operations between arrays of quantity scalars and quantity holding
array resulting in incorrect units.
(PR #1677)
- Fix LaTeX siuntix formatting when using non_int_type=decimal.Decimal.
- Added refractive index units.
(PR #1816)
- Fix converting to offset units of higher dimension e.g. gauge pressure
(PR #1949)
- Fix unhandled TypeError when auto_reduce_dimensions=True and non_int_type=Decimal
(PR #1853)
- Improved error message in `get_dimensionality()` when non existent units are passed.
(PR #1874, Issue #1716)


0.23 (2023-12-08)
Expand Down
6 changes: 4 additions & 2 deletions docs/advanced/pitheorem.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ Which can be pretty printed using the `Pint` formatter:

>>> from pint import formatter
>>> result = pi_theorem({'V': '[length]/[time]', 'T': '[time]', 'L': '[length]'})
>>> print(formatter(result[0].items()))
T * V / L
>>> numerator = [item for item in result[0].items() if item[1]>0]
>>> denominator = [item for item in result[0].items() if item[1]<0]
>>> print(formatter(numerator, denominator))
V * T / L

You can also apply the Buckingham π theorem associated to a Registry. In this case,
you can use derived dimensions such as speed:
Expand Down
2 changes: 1 addition & 1 deletion docs/api/facets.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The default UnitRegistry inherits from all of them.
:members:
:exclude-members: Quantity, Unit, Measurement, Group, Context, System

.. automodule:: pint.facets.formatting
.. automodule:: pint.delegates.formatter
:members:
:exclude-members: Quantity, Unit, Measurement, Group, Context, System

Expand Down
8 changes: 4 additions & 4 deletions docs/getting/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ If Babel_ is installed you can translate unit names to any language
.. doctest::

>>> ureg.formatter.format_quantity(accel, locale='fr_FR')
'1,3 mètres/secondes²'
'1,3 mètres par seconde²'

You can also specify the format locale at the registry level either at creation:

Expand All @@ -449,11 +449,11 @@ and by doing that, string formatting is now localized:
>>> ureg.default_format = 'P'
>>> accel = 1.3 * ureg.parse_units('meter/second**2')
>>> str(accel)
'1,3 mètres/secondes²'
'1,3 mètres par seconde²'
>>> "%s" % accel
'1,3 mètres/secondes²'
'1,3 mètres par seconde²'
>>> "{}".format(accel)
'1,3 mètres/secondes²'
'1,3 mètres par seconde²'

If you want to customize string formatting, take a look at :ref:`formatting`.

Expand Down
2 changes: 1 addition & 1 deletion docs/user/angular_frequency.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


Angles and Angular Frequency
=================
=============================

Angles
------
Expand Down
2 changes: 1 addition & 1 deletion docs/user/defining-quantities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ For example, the units of
.. doctest::

>>> Q_('3 l / 100 km')
<Quantity(0.03, 'kilometer * liter')>
<Quantity(0.03, 'liter * kilometer')>

may be unexpected at first but, are a consequence of applying this rule. Use
brackets to get the expected result:
Expand Down
5 changes: 3 additions & 2 deletions docs/user/formatting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ formats:
... def format_unit_simple(unit, registry, **options):
... return " * ".join(f"{u} ** {p}" for u, p in unit.items())
>>> f"{q:Z}"
'2.3e-06 meter ** 3 * second ** -2 * kilogram ** -1'
'2.3e-06 kilogram ** -1 * meter ** 3 * second ** -2'

where ``unit`` is a :py:class:`dict` subclass containing the unit names and
their exponents.
Expand All @@ -111,10 +111,11 @@ following methods: `format_magnitude`, `format_unit`, `format_quantity`, `format
...
... default_format = ""
...
... def format_unit(self, unit, uspec: str = "", **babel_kwds) -> str:
... def format_unit(self, unit, uspec, sort_func, **babel_kwds) -> str:
... return "ups!"
...
>>> ureg.formatter = MyFormatter()
>>> ureg.formatter._registry = ureg
>>> str(q)
'2.3e-06 ups!'

Expand Down
5 changes: 5 additions & 0 deletions pint/default_en.txt
Original file line number Diff line number Diff line change
Expand Up @@ -494,12 +494,17 @@ buckingham = debye * angstrom
bohr_magneton = e * hbar / (2 * m_e) = µ_B = mu_B
nuclear_magneton = e * hbar / (2 * m_p) = µ_N = mu_N

# Refractive index
[refractive_index] = []
refractive_index_unit = [] = RIU

# Logaritmic Unit Definition
# Unit = scale; logbase; logfactor
# x_dB = [logfactor] * log( x_lin / [scale] ) / log( [logbase] )

# Logaritmic Units of dimensionless quantity: [ https://en.wikipedia.org/wiki/Level_(logarithmic_quantity) ]

decibelwatt = watt; logbase: 10; logfactor: 10 = dBW
decibelmilliwatt = 1e-3 watt; logbase: 10; logfactor: 10 = dBm
decibelmicrowatt = 1e-6 watt; logbase: 10; logfactor: 10 = dBu

Expand Down
4 changes: 2 additions & 2 deletions pint/delegates/formatter/latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ def siunitx_format_unit(
) -> str:
"""Returns LaTeX code for the unit that can be put into an siunitx command."""

def _tothe(power: int | float) -> str:
if isinstance(power, int) or (isinstance(power, float) and power.is_integer()):
def _tothe(power) -> str:
if power == int(power):
if power == 1:
return ""
elif power == 2:
Expand Down
14 changes: 12 additions & 2 deletions pint/facets/nonmultiplicative/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def _is_multiplicative(self, unit_name: str) -> bool:
Raises
------
UndefinedUnitError
If the unit is not in the registyr.
If the unit is not in the registry.
"""
if unit_name in self._units:
return self._units[unit_name].is_multiplicative
Expand Down Expand Up @@ -192,7 +192,7 @@ def _add_ref_of_log_or_offset_unit(
self, offset_unit: str, all_units: UnitsContainer
) -> UnitsContainer:
slct_unit = self._units[offset_unit]
if slct_unit.is_logarithmic or (not slct_unit.is_multiplicative):
if slct_unit.is_logarithmic:
# Extract reference unit
slct_ref = slct_unit.reference

Expand All @@ -204,6 +204,11 @@ def _add_ref_of_log_or_offset_unit(
(u, e) = [(u, e) for u, e in slct_ref.items()].pop()
# Add it back to the unit list
return all_units.add(u, e)

if not slct_unit.is_multiplicative: # is offset unit
# Extract reference unit
return slct_unit.reference

# Otherwise, return the units unmodified
return all_units

Expand Down Expand Up @@ -249,6 +254,7 @@ def _convert(
src, dst, extra_msg=f" - In destination units, {ex}"
)

# convert if no offset units are present
if not (src_offset_unit or dst_offset_unit):
return super()._convert(value, src, dst, inplace)

Expand All @@ -262,13 +268,17 @@ def _convert(

# clean src from offset units by converting to reference
if src_offset_unit:
if any(u.startswith("delta_") for u in dst):
raise DimensionalityError(src, dst)
value = self._units[src_offset_unit].converter.to_reference(value, inplace)
src = src.remove([src_offset_unit])
# Add reference unit for multiplicative section
src = self._add_ref_of_log_or_offset_unit(src_offset_unit, src)

# clean dst units from offset units
if dst_offset_unit:
if any(u.startswith("delta_") for u in src):
raise DimensionalityError(src, dst)
dst = dst.remove([dst_offset_unit])
# Add reference unit for multiplicative section
dst = self._add_ref_of_log_or_offset_unit(dst_offset_unit, dst)
Expand Down
24 changes: 22 additions & 2 deletions pint/facets/numpy/numpy_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ def _is_sequence_with_quantity_elements(obj):
-------
True if obj is a sequence and at least one element is a Quantity; False otherwise
"""
if np is not None and isinstance(obj, np.ndarray) and not obj.dtype.hasobject:
# If obj is a numpy array, avoid looping on all elements
# if dtype does not have objects
return False
return (
iterable(obj)
and sized(obj)
Expand Down Expand Up @@ -284,6 +288,17 @@ def implement_func(func_type, func_str, input_units=None, output_unit=None):

@implements(func_str, func_type)
def implementation(*args, **kwargs):
if func_str in ["multiply", "true_divide", "divide", "floor_divide"] and any(
[
not _is_quantity(arg) and _is_sequence_with_quantity_elements(arg)
for arg in args
]
):
# the sequence may contain different units, so fall back to element-wise
return np.array(
[func(*func_args) for func_args in zip(*args)], dtype=object
)

first_input_units = _get_first_input_units(args, kwargs)
if input_units == "all_consistent":
# Match all input args/kwargs to same units
Expand Down Expand Up @@ -413,6 +428,7 @@ def implementation(*args, **kwargs):
"take",
"trace",
"transpose",
"roll",
"ceil",
"floor",
"hypot",
Expand Down Expand Up @@ -740,22 +756,25 @@ def _base_unit_if_needed(a):
raise OffsetUnitCalculusError(a.units)


# NP2 Can remove trapz wrapping when we only support numpy>=2
@implements("trapz", "function")
@implements("trapezoid", "function")
def _trapz(y, x=None, dx=1.0, **kwargs):
trapezoid = np.trapezoid if hasattr(np, "trapezoid") else np.trapz
y = _base_unit_if_needed(y)
units = y.units
if x is not None:
if hasattr(x, "units"):
x = _base_unit_if_needed(x)
units *= x.units
x = x._magnitude
ret = np.trapz(y._magnitude, x, **kwargs)
ret = trapezoid(y._magnitude, x, **kwargs)
else:
if hasattr(dx, "units"):
dx = _base_unit_if_needed(dx)
units *= dx.units
dx = dx._magnitude
ret = np.trapz(y._magnitude, dx=dx, **kwargs)
ret = trapezoid(y._magnitude, dx=dx, **kwargs)

return y.units._REGISTRY.Quantity(ret, units)

Expand Down Expand Up @@ -850,6 +869,7 @@ def implementation(*args, **kwargs):
("median", "a", True),
("nanmedian", "a", True),
("transpose", "a", True),
("roll", "a", True),
("copy", "a", True),
("average", "a", True),
("nanmean", "a", True),
Expand Down
4 changes: 2 additions & 2 deletions pint/facets/plain/qto.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def to_preferred(
>>> (1*ureg.acre).to_preferred([ureg.meters])
<Quantity(4046.87261, 'meter ** 2')>
>>> (1*(ureg.force_pound*ureg.m)).to_preferred([ureg.W])
<Quantity(4.44822162, 'second * watt')>
<Quantity(4.44822162, 'watt * second')>
"""

units = _get_preferred(quantity, preferred_units)
Expand All @@ -204,7 +204,7 @@ def ito_preferred(
>>> (1*ureg.acre).to_preferred([ureg.meters])
<Quantity(4046.87261, 'meter ** 2')>
>>> (1*(ureg.force_pound*ureg.m)).to_preferred([ureg.W])
<Quantity(4.44822162, 'second * watt')>
<Quantity(4.44822162, 'watt * second')>
"""

units = _get_preferred(quantity, preferred_units)
Expand Down
2 changes: 1 addition & 1 deletion pint/facets/plain/quantity.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class PlainQuantity(Generic[MagnitudeT], PrettyIPython, SharedRegistryObject):
def ndim(self) -> int:
if isinstance(self.magnitude, numbers.Number):
return 0
if str(self.magnitude) == "<NA>":
if str(type(self.magnitude)) == "NAType":
return 0
return self.magnitude.ndim

Expand Down
7 changes: 6 additions & 1 deletion pint/facets/plain/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,12 @@ def _get_dimensionality_recurse(
for key in ref:
exp2 = exp * ref[key]
if _is_dim(key):
reg = self._dimensions[key]
try:
reg = self._dimensions[key]
except KeyError:
raise ValueError(
f"{key} is not defined as dimension in the pint UnitRegistry"
)
if isinstance(reg, DerivedDimensionDefinition):
self._get_dimensionality_recurse(reg.reference, exp2, accumulator)
else:
Expand Down
17 changes: 12 additions & 5 deletions pint/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,31 +69,38 @@ class UnitRegistry(GenericUnitRegistry[Quantity, Unit]):
----------
filename :
path of the units definition file to load or line-iterable object.
Empty to load the default definition file.
Empty string to load the default definition file. (default)
None to leave the UnitRegistry empty.
force_ndarray : bool
convert any input, scalar or not to a numpy.ndarray.
(Default: False)
force_ndarray_like : bool
convert all inputs other than duck arrays to a numpy.ndarray.
(Default: False)
default_as_delta :
In the context of a multiplication of units, interpret
non-multiplicative units as their *delta* counterparts.
(Default: False)
autoconvert_offset_to_baseunit :
If True converts offset units in quantities are
converted to their plain units in multiplicative
context. If False no conversion happens.
context. If False no conversion happens. (Default: False)
on_redefinition : str
action to take in case a unit is redefined.
'warn', 'raise', 'ignore'
'warn', 'raise', 'ignore' (Default: 'raise')
auto_reduce_dimensions :
If True, reduce dimensionality on appropriate operations.
(Default: False)
autoconvert_to_preferred :
If True, converts preferred units on appropriate operations.
(Default: False)
preprocessors :
list of callables which are iteratively ran on any input expression
or unit string
or unit string or None for no preprocessor.
(Default=None)
fmt_locale :
locale identifier string, used in `format_babel`. Default to None
locale identifier string, used in `format_babel` or None.
(Default=None)
case_sensitive : bool, optional
Control default case sensitivity of unit parsing. (Default: True)
cache_folder : str or pathlib.Path or None, optional
Expand Down
3 changes: 3 additions & 0 deletions pint/testsuite/benchmarks/test_10_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ def test_load_definitions_stage_1(benchmark, cache_folder, use_cache_folder):
benchmark(pint.UnitRegistry, None, cache_folder=use_cache_folder)


@pytest.mark.skip(
"Test failing ValueError: Group USCSLengthInternational already present in registry"
)
@pytest.mark.parametrize("use_cache_folder", (None, True))
def test_load_definitions_stage_2(benchmark, cache_folder, use_cache_folder):
"""empty registry creation + parsing default files + definition object loading"""
Expand Down
10 changes: 10 additions & 0 deletions pint/testsuite/test_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,13 @@ def test_pickle_definition_syntax_error(self, subtests):

with pytest.raises(PintError):
raise ex

def test_dimensionality_error_message(self):
ureg = UnitRegistry(system="SI")
with pytest.raises(ValueError) as error:
ureg.get_dimensionality("[bilbo]")

assert (
str(error.value)
== "[bilbo] is not defined as dimension in the pint UnitRegistry"
)
Loading

0 comments on commit be714f2

Please sign in to comment.