Skip to content

Commit

Permalink
update the python interface, (#357)
Browse files Browse the repository at this point in the history
- Updated the Python interface, cleaned up the code, added dunder methods, and implemented mod operation for measurements.
- Added floor division in Python.
- Enhanced README with new shields and more documentation updates.
- Updated version and documentation; improved bool conversion in Python with tests.
- Applied auto fixes from pre-commit.com hooks.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
phlptp and pre-commit-ci[bot] authored Jan 5, 2025
1 parent 7a796be commit 9d91e0d
Show file tree
Hide file tree
Showing 13 changed files with 405 additions and 103 deletions.
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,38 @@ All notable changes to this project after the 0.2.0 release will be documented i
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.12.0][] - 2025-01-05

Python package release, documentation update, continued addition of new units and other updates and fixes

### Changed

- Updated copyright dates for 2025 [#356][]
- Python getters `value()` and `commodity()`, `multiplier()`, and `units()` are now properties and do not use the parenthesis any more. (these are read only, the classes are immutable) [#357][]

### Fixed

- fixed some code analyzer warnings [#355][]

### Added

- Added currency conversion from around the world [#355][]
- Added commodity_conversion_map file containing some additional commoditity strings [#355][]
- In python library added dunder methods for floor, ceil, round, hash, floordiv [#357][]
- Added format specifiers for measurement to allow conversion in the format string and removal of the unit string [#357][]
- Added operators for `float` and `bool` in python [#357][]
- Added mod (`%`) and `//` operator in python for both other `Measurement` and `float` [#357][]
- Added negation operator `-` in python [#357][]

### Removed

- removed specific python method `inv` - now just use inversion operator `~` [#357][]
- remove isolated `to_string` method on Unit and Measurement python classes, use `str()` [#357][]

[#355]: https://github.com/LLNL/units/pull/355
[#356]: https://github.com/LLNL/units/pull/356
[#357]: https://github.com/LLNL/units/pull/357

## [0.11.0][] -2024-12-26

Python package release, documentation update, continued addition of new units and other updates and fixes
Expand Down Expand Up @@ -130,6 +162,7 @@ A few user suggested tweaks, and support additional unit string conversions supp
[0.9.2]: https://github.com/LLNL/units/releases/tag/v0.9.2
[0.10.2]: https://github.com/LLNL/units/releases/tag/v0.10.2
[0.11.0]: https://github.com/LLNL/units/releases/tag/v0.11.0
[0.12.0]: https://github.com/LLNL/units/releases/tag/v0.12.0

## [0.7.0][] - 2022-12-17

Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ endif()
project(
${UNITS_CMAKE_PROJECT_NAME}
LANGUAGES C CXX
VERSION 0.11.0
VERSION 0.12.0
)
include(CMakeDependentOption)
include(CTest)
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
[![codecov](https://codecov.io/gh/LLNL/units/branch/main/graph/badge.svg)](https://codecov.io/gh/LLNL/units)
[![Build Status](https://dev.azure.com/phlptp/units/_apis/build/status/LLNL.units?branchName=main)](https://dev.azure.com/phlptp/units/_build/latest?definitionId=1&branchName=main)
[![CircleCI](https://circleci.com/gh/LLNL/units.svg?style=svg)](https://circleci.com/gh/LLNL/units)
[![](https://img.shields.io/pypi/pyversions/units-llnl)](https://pypi.org/project/units-llnl/)
[![](https://img.shields.io/pypi/v/units-llnl)](https://pypi.org/project/units-llnl/)
[![](https://img.shields.io/badge/License-BSD-blue.svg)](https://github.com/GMLC-TDC/HELICS-src/blob/main/LICENSE)
[![Documentation Status](https://readthedocs.org/projects/units/badge/?version=latest)](https://units.readthedocs.io/en/latest/?badge=latest)
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/LLNL/units/main.svg)](https://results.pre-commit.ci/latest/github/LLNL/units/main)
Expand All @@ -13,7 +15,7 @@

The Units library provides a means of working with units of measurement at runtime, including conversion to and from strings. It provides a small number of types for working with units and measurements and operations necessary for user input and output with units.

This software was developed for use in [LLNL/GridDyn](https://github.com/LLNL/GridDyn), and [HELICS](https://github.com/GMLC-TDC/HELICS) and is currently a work in progress (though getting close). Namespaces, function names, and code organization is subject to change though is fairly stable at this point, input is welcome. A set of [documentation](https://units.readthedocs.io/en/latest/) is available.
This software was developed for use in [LLNL/GridDyn](https://github.com/LLNL/GridDyn), and [HELICS](https://github.com/GMLC-TDC/HELICS) and is currently a work in progress (though getting close). Namespaces, function names, and code organization is subject to change though is fairly stable at this point, input is welcome. A set of [documentation](https://units.readthedocs.io/en/latest/) is available. A Python wrapper is also available through [Pypi](https://pypi.org/project/units-llnl/) that wraps a limited subset of the library for most common purposes.

## Table of contents

Expand Down
14 changes: 7 additions & 7 deletions config/cppcheck_suppressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ passedByValue:units/units.cpp:1160
passedByValue:units/units.cpp:1177
passedByValue:units/units.cpp:3067
passedByValue:units/units.hpp:413
passedByValue:units/units.hpp:583
passedByValue:units/units.hpp:1013
passedByValue:units/units.hpp:1025
passedByValue:units/units.hpp:590
passedByValue:units/units.hpp:1020
passedByValue:units/units.hpp:1032
returnByReference:units/units.hpp:363
returnByReference:units/units.hpp:569
returnByReference:units/units.hpp:1010
returnByReference:units/units.hpp:1239
returnByReference:units/units.hpp:1471
returnByReference:units/units.hpp:576
returnByReference:units/units.hpp:1017
returnByReference:units/units.hpp:1246
returnByReference:units/units.hpp:1485
2 changes: 1 addition & 1 deletion docs/user-guide/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ User Guide
The Units library user guide is an in depth look at how to use the C++ library and its functionality, covering the basic types in the library and operations with them.
The guide covers the basic types and what operations are available on them, as well as a lot of details on how to use the library.

The python package is a simplified wrapper around the C++ library and is discussed in
The python package is a simplified wrapper around the C++ library and is discussed in :ref:`python`

.. toctree::
:maxdepth: 1
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/python.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Python
==================
The Python wrapper for the units library is a simplified version of the library. It is focused on the string operations of the library and conversions between units and measurements.

The two key classes are `Unit` which encapsulates a specific unit of measure and optional commodity if desired, and `Measurement` which captures a value + `Unit`. Math operations are supported on the types.
The units library is available through a pypi_ package

.. code-block:: sh
Expand Down
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"

[project]
name = "units_llnl"
version = "0.11.0"
version = "0.12.0"
description = "Python bindings for the LLNL units library"
readme = "python/README.md"
requires-python = ">=3.10"
Expand All @@ -14,7 +14,7 @@ license-files = [
"NOTICE"
]
authors = [
{ name = "phlptp22", email = "[email protected]" },
{ name = "Philip Top", email = "[email protected]" },
]
classifiers = [
"Programming Language :: Python :: 3",
Expand All @@ -38,6 +38,10 @@ Repository = "https://github.com/llnl/units"
Issues = "https://github.com/LLNL/units/issues"
Changelog = "https://github.com/LLNL/units/blob/main/CHANGELOG.md"


[project.optional-dependencies]
test = ["pytest"]

[tool.scikit-build]
# Protect the configuration against future changes in scikit-build-core
minimum-version = "build-system.requires"
Expand Down
71 changes: 42 additions & 29 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
[![Build Status](https://dev.azure.com/phlptp/units/_apis/build/status/LLNL.units?branchName=main)](https://dev.azure.com/phlptp/units/_build/latest?definitionId=1&branchName=main)
[![CircleCI](https://circleci.com/gh/LLNL/units.svg?style=svg)](https://circleci.com/gh/LLNL/units)
[![](https://img.shields.io/badge/License-BSD-blue.svg)](https://github.com/GMLC-TDC/HELICS-src/blob/main/LICENSE)
[![](https://img.shields.io/pypi/pyversions/units-llnl)](https://pypi.org/project/units-llnl/)
[![](https://img.shields.io/pypi/v/units-llnl)](https://pypi.org/project/units-llnl/)
[![Documentation Status](https://readthedocs.org/projects/units/badge/?version=latest)](https://units.readthedocs.io/en/latest/?badge=latest)
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/LLNL/units/main.svg)](https://results.pre-commit.ci/latest/github/LLNL/units/main)

Expand All @@ -23,10 +25,12 @@ The Units library provides a means of working with units of measurement at runti
- [Unit methods](#unit-methods)
- [Constructors](#constructors)
- [Methods](#methods)
- [Properties](#properties)
- [Operators](#operators)
- [Measurements](#measurements)
- [Constructors](#constructors-1)
- [Methods](#methods-1)
- [Properties](#properties-1)
- [Operators](#operators-1)
- [Other library methods](#other-library-methods)
- [Future plans](#future-plans)
Expand Down Expand Up @@ -87,38 +91,43 @@ These operations apply the `Units` object in Python. It maps to a `precise_unit`

- `Unit(unit_str:str)` construct from a string
- `Unit(unit_str:str,commodity_str:str)` construct a unit from a unit string and commodity string
- `Unit(float multiplier, unit:Unit)` construct a unit using another unit as a base along with a multiplier

#### Methods

- `inv()->Unit` generate a new unit containing the inverse unit `Unit('m').inv()== Unit('1/m')`
- `pow(int power)->Unit` take a unit to power(NOTE: beware of limits on power representations of some units, things will always wrap so it is defined but may not produce what you expect). `power` can be negative.
- `is_exactly_the_same(other:Unit)->bool` compare two units and check for exact equivalence in both the unit_data and the multiplier
- `has_same_base(other:Unit)->bool` check if the units have the same base units
- `equivalent_non_counting(other:Unit)->bool` check if the units are equivalent ignoring the counting bases
- `is_exactly_the_same(other:Unit)->bool` compare two units and check for exact equivalence in both the unit_data and the multiplier.
- `has_same_base(other:Unit)->bool` check if the units have the same base units.
- `equivalent_non_counting(other:Unit)->bool` check if the units are equivalent ignoring the counting bases.
- `is_convertible_to(other:Unit)->bool` check if the units are convertible to each other, currently checks `equivalent_non_counting()`, but some additional conditions might be allowed in the future to better match convert.
- `convert(value:float,unit_out:Unit|str)->float` convert a value from the existing unit to another, can also be a string
- `is_per_unit()->bool` true if the unit has the per_unit flag active
- `is_equation()->bool` true if the unit has the equation flag active
- `is_valid()->bool` true if the unit is a valid unit
- `is_normal()->bool` true if the unit is a normal unit (not error, nan, or subnormal)
- `is_error()->bool` true if the unit is an error unit (e.g invalid conversion)
- `isfinite()->bool` true if the unit does not have an infinite multiplier
- `isinf()->bool` true if the unit does have an infinite multiplier
- `root(power:int)->Unit` return a new unit taken to the root power
- `sqrt()->Unit` returns a new unit which is the square root of the current unit
- `to_string()->str` returns the string representation of the unit. This string is guaranteed to produce the same unit as the current unit, but may not be the same string as was used to create it.
- `multiplier()->float` return the unit multiplier as a floating point number
- `set_multiplier(mult:float)->Unit` generate a new Unit with the set multiplier
- `commodity()->str` get the commodity of the unit
- `convert(value:float,unit_out:Unit|str)->float` convert a value from the existing unit to another, can also be a string.
- `is_per_unit()->bool` true if the unit has the per_unit flag active.
- `is_equation()->bool` true if the unit has the equation flag active.
- `is_valid()->bool` true if the unit is a valid unit.
- `is_normal()->bool` true if the unit is a normal unit (not error, nan, or subnormal).
- `is_error()->bool` true if the unit is an error unit (e.g invalid conversion).
- `isfinite()->bool` true if the unit does not have an infinite multiplier.
- `isinf()->bool` true if the unit does have an infinite multiplier.
- `root(power:int)->Unit` return a new unit taken to the root power.
- `sqrt()->Unit` returns a new unit which is the square root of the current unit.
- `set_multiplier(mult:float)->Unit` generate a new Unit with the set multiplier.
- `set_commodity(int commodity)` generate a new unit with the assigned commodity.

#### Properties

- `multiplier->float` return the unit multiplier as a floating point number
- `commodity->str` get the commodity of the unit
- `base_units`->Unit gets the base units (no multiplier) associated with a unit

#### Operators

- `*`,`/` with other units produces a new unit
- `~` produces the inverse of the unit
- `**` is an exponentiation operator and produces a new unit
- `*`, `/` with a floating point generates a `Measurement`
- `==` and `!=` produce the appropriate comparison operators
- f string formatting also works with units
- f string formatting also works with units and returns the string representation of the unit. This string is guaranteed to produce the same unit as the current unit, but may not be the same string as was used to create it.
- `str`,`bool` are defined, `bool` indicates that the unit is valid, and non-zero
- `Units` may also be used as the indexing element in a dictionary

### Measurements

Expand All @@ -129,30 +138,34 @@ These operations apply the `Units` object in Python. It maps to a `precise_unit`

#### Methods

- `inv()->Unit` generate a new unit containing the inverse unit `Unit('m').inv()== Unit('1/m')`
- `pow(int power)->Unit` take a unit to power(NOTE: beware of limits on power representations of some units, things will always wrap so it is defined but may not produce what you expect). `power` can be negative.
- `is_normal()->bool` true if the unit is a normal unit (not error, nan, or subnormal)
- `is_valid()->bool` true if the `Measurement` is a valid Measurement (not error)
- `root(power:int)->Measurement` return a new unit taken to the root power
- `sqrt()->Unit` returns a new unit which is the square root of the current unit
- `to_string()->str` returns the string representation of the `Measurement`. This string is guaranteed to produce the equivalent `Measurement` as the current `Measurement`, but may not be the same string as was used to create it.
- `value()->float` return the numerical portion of a `Measurement`
- `set_value(value:float)->Measurement` generate a new `Measurement` with the new Value
- `units()->Unit` get the `Unit` associated with a `Measurement`
- `set_units(unit:Unit|str)` generate a new `Measurement` with the new units
- `value_as(unit:Unit|str)->float` convert the value of the `Measurement` to a new `Unit`
- `convert_to(unit:Unit|str)->Measurement` create a new `Measurement` with the new units and the value converted to those units
- `convert_to_base()->Measurement` create a new `Measurement` with the units as the base measurement units
- `is_close(other:Measurement)->bool` return true if the two measurements are close (both converted to non precise measurement and compared)

#### Properties

- `value->float` return the numerical portion of a `Measurement`
- `units->Unit` get the `Unit` associated with a `Measurement`

#### Operators

- `*`,`/` with other `Measurements` produces a new Measurement
- `~` inverts the measurement equivalent to `1/measurement`
- `+`,`-` with other `Measurements` ensures the units are in the same base unit and performs the appropriate action
- `**` is an exponentiation operator and produces a new `Measurement`
- `*`, `/` with a floating point generates a `Measurement`
- `**` is an exponentiation operator and produces a new `Measurement` (NOTE: beware of limits on power representations of some units, things will always wrap so it is defined but may not produce what you expect). Can be negative.
- `*`, `/`,`%` with a floating point generates a `Measurement`
- `//` produces the floor of the resulting unit of division
- `==`,`!=`,`>`,`<`,`>=`,`<=` produce the appropriate comparison operators
- f string formatting also works with units
- `str`,`float`,`bool` are defined, `bool` indicates that the measurement is non zero and is valid
- `round`, `math.ceil`,`math.floor`, and `math.trunc` work as expected
- f string formatting also works with measurement. Some special formatters are available `f"{m1:-}"` will remove the unit and just display the value. `f"{m1:new_unit}"` will convert to a new unit before displaying. `f"{m1:-new_unit}"` will do the conversion but just display the numerical value after the conversion.

### Other library methods

Expand All @@ -164,7 +177,7 @@ These operations apply the `Units` object in Python. It maps to a `precise_unit`

### Future plans

Uncertain measurements will likely be added, along with some math operations on measurements (floor, ceil, round, etc). Also some more commodity operations, and x12 and r20 unit types.
Uncertain measurements will likely be added, potentially some trig functions on measurements. Also some more commodity operations, and x12 and r20 unit types.

## Contributions

Expand Down
Loading

0 comments on commit 9d91e0d

Please sign in to comment.