Skip to content

Commit

Permalink
update the python interface, clean up, add some dunder methods, and a…
Browse files Browse the repository at this point in the history
…dd mod operation for measurements
  • Loading branch information
phlptp committed Jan 4, 2025
1 parent 7a796be commit d12b1c6
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 65 deletions.
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.11.1
)
include(CMakeDependentOption)
include(CTest)
Expand Down
112 changes: 81 additions & 31 deletions python/units_python.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ SPDX-License-Identifier: BSD-3-Clause
#include <nanobind/stl/string.h>

#include "units/units.hpp"
#include "units/units_math.hpp"
namespace nb = nanobind;

using namespace nb::literals;
Expand Down Expand Up @@ -41,27 +42,31 @@ NB_MODULE(units_llnl_ext, mod)
units::unit_from_string(std::string(arg0)),
units::getCommodity(std::string(commodity))};
})
.def("multiplier", &units::precise_unit::multiplier)
.def(
.def_prop_ro("multiplier", &units::precise_unit::multiplier)
.def_prop_ro(
"commodity",
[](const units::precise_unit& unit) {
return units::getCommodityName(unit.commodity());
})
.def_prop_ro(
"base_units",
[](const units::precise_unit& type1) {
return units::getCommodityName(type1.commodity());
return units::precise_unit(type1.base_units());
})
.def(
"set_commodity",
[](units::precise_unit* unit, const char* commodity) {
[](const units::precise_unit* unit, const char* commodity) {
return units::precise_unit(
unit->multiplier(),
unit->base_units(),
units::getCommodity(std::string(commodity)));
})
.def(
"set_multiplier",
[](units::precise_unit* unit, double mult) {
[](const units::precise_unit* unit, double mult) {
return units::precise_unit(
mult, unit->base_units(), unit->commodity());
})
.def("inv", &units::precise_unit::inv)
.def(nb::self * nb::self)
.def(nb::self / nb::self)
.def(float() * nb::self)
Expand All @@ -72,7 +77,7 @@ NB_MODULE(units_llnl_ext, mod)
.def(nb::self != nb::self)
.def(
"__pow__",
[](const units::precise_unit& a, int pow) { return a.pow(pow); },
[](const units::precise_unit& unit, int pow) { return unit.pow(pow); },
nb::is_operator())
.def(
"is_exactly_the_same",
Expand Down Expand Up @@ -135,45 +140,48 @@ NB_MODULE(units_llnl_ext, mod)
.def("is_equation", &units::precise_unit::is_equation)
.def(
"is_valid",
[](const units::precise_unit& type) {
return units::is_valid(type);
[](const units::precise_unit& unit) {
return units::is_valid(unit);
})
.def(
"is_normal",
[](const units::precise_unit& type) {
return units::isnormal(type);
[](const units::precise_unit& unit) {
return units::isnormal(unit);
})
.def(
"is_error",
[](const units::precise_unit& type) {
return units::is_error(type);
[](const units::precise_unit& unit) {
return units::is_error(unit);
},
"return true if the unit has the error flags set or is infinite")
.def(
"isfinite",
[](const units::precise_unit& type) {
return units::isfinite(type);
[](const units::precise_unit& unit) {
return units::isfinite(unit);
})
.def(
"isinf",
[](const units::precise_unit& type) { return units::isinf(type); })
[](const units::precise_unit& unit) { return units::isinf(unit); })
.def(
"root",
[](const units::precise_unit& type, int root) {
return units::root(type, root);
[](const units::precise_unit& unit, int root) {
return units::root(unit, root);
})
.def(
"sqrt",
[](const units::precise_unit& type) {
return units::root(type, 2);
[](const units::precise_unit& unit) {
return units::root(unit, 2);
})
.def("__invert__",[](const units::precise_unit & unit){
return unit.inv();
})
.def(
"__repr__",
[](const units::precise_unit& type) {
return units::to_string(type);
[](const units::precise_unit& unit) {
return units::to_string(unit);
})
.def("to_string", [](const units::precise_unit& type) {
return units::to_string(type);
.def("__hash__",[](const units::precise_unit& unit){
return std::hash<units::precise_unit>()(unit);
});

nb::class_<units::precise_measurement>(
Expand Down Expand Up @@ -202,16 +210,16 @@ NB_MODULE(units_llnl_ext, mod)
const units::precise_unit& unit) {
new (measurement) units::precise_measurement(value, unit);
})
.def("value", &units::precise_measurement::value)
.def_prop_ro("value", [](const units::precise_measurement& measurement){return measurement.value();})
.def(
"set_value",
[](units::precise_measurement* measurement, double value) {
[](const units::precise_measurement* measurement, double value) {
return units::precise_measurement(value, measurement->units());
})
.def("units", &units::precise_measurement::units)
.def_prop_ro("units", [](const units::precise_measurement& measurement){return measurement.units();})
.def(
"set_units",
[](units::precise_measurement* measurement,
[](const units::precise_measurement* measurement,
const units::precise_unit& unit) {
return units::precise_measurement(measurement->value(), unit);
})
Expand Down Expand Up @@ -257,12 +265,14 @@ NB_MODULE(units_llnl_ext, mod)
.def("as_unit", &units::precise_measurement::as_unit)
.def(nb::self * nb::self)
.def(nb::self / nb::self)
.def(nb::self % nb::self)
.def(nb::self + nb::self)
.def(nb::self - nb::self)
.def(float() * nb::self)
.def(nb::self * float())
.def(float() / nb::self)
.def(nb::self / float())
.def(nb::self % float())
.def(nb::self == nb::self)
.def(nb::self != nb::self)
.def(nb::self > nb::self)
Expand Down Expand Up @@ -310,9 +320,49 @@ NB_MODULE(units_llnl_ext, mod)
[](const units::precise_measurement& measurement) {
return units::to_string(measurement);
})
.def("to_string", [](const units::precise_measurement& measurement) {
return units::to_string(measurement);
});
.def(
"__format__",
[](const units::precise_measurement& measurement,std::string fmt_string) {
if (fmt_string.empty())
{
return units::to_string(measurement);
}
if (fmt_string == "-")
{
return units::to_string(units::precise_measurement(measurement.value(),units::precise::one));
}
if (fmt_string.front() == '-')
{
return units::to_string(units::precise_measurement(measurement.value_as(units::unit_from_string(fmt_string.substr(1))),units::precise::one));
}
else {
return units::to_string(measurement.convert_to(units::unit_from_string(fmt_string)));
}

})
.def("__neg__",[](const units::precise_measurement & measurement){
return -measurement;
})
.def("__invert__",[](const units::precise_measurement & measurement){
return 1.0/measurement;
})
.def("__trunc__",[](const units::precise_measurement & measurement){
return trunc(measurement);
})
.def("__ceil__",[](const units::precise_measurement & measurement){
return ceil(measurement);
})
.def("__floor__",[](const units::precise_measurement & measurement){
return floor(measurement);
})
.def("__round__",[](const units::precise_measurement & measurement){
return round(measurement);
})
.def("__floordiv__",[](const units::precise_measurement & measurement,const units::precise_measurement &other){
return floor(measurement/other).value();
})
.def("__float__",[](const units::precise_measurement &measurement){return measurement.value();})
.def("__bool__",[](const units::precise_measurement &measurement){return (measurement.value()>=0.0);});

mod.def(
"convert",
Expand Down
73 changes: 56 additions & 17 deletions test/python/test_measurement.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# SPDX-License-Identifier: BSD-3-Clause

import units_llnl as u

import math

def test_basic_measurement():
m1 = u.Measurement("10 m")
Expand Down Expand Up @@ -32,7 +32,7 @@ def test_basic_measurement3():

m4 = u.Measurement(4.0, u1 / u2)
assert m1 / m2 == m4
assert m4.units() == u1 / u2
assert m4.units == u1 / u2


def test_conditions():
Expand Down Expand Up @@ -80,16 +80,16 @@ def test_comparisons():

def test_set_value():
m1 = u.Measurement("100 months")
assert m1.value() == 100
assert m1.value == 100
m2 = m1.set_value(14)
assert m2.value() == 14
assert m2.value == 14


def test_set_units():
m1 = u.Measurement("100 months")
assert m1.units() == u.Unit("month")
assert m1.units == u.Unit("month")
m2 = m1.set_units(u.Unit("day"))
assert m2.units() == u.Unit("day")
assert m2.units == u.Unit("day")


def test_value_as():
Expand All @@ -102,40 +102,68 @@ def test_value_as():
def test_convert_to():
m1 = u.Measurement("20 weeks")
m2 = m1.convert_to("day")
assert m2.value() == 20 * 7
assert m2.value == 20 * 7
u1 = u.Unit("hr")
m3 = m1.convert_to("hr")
assert m3.value() == 20 * 7 * 24
assert m3.value == 20 * 7 * 24

m4 = m1.convert_to_base()
assert m4.units() == u.Unit("s")
assert m4.units().multiplier() == 1.0
assert m4.value() == 20 * 7 * 24 * 3600
assert m4.units == u.Unit("s")
assert m4.units.multiplier == 1.0
assert m4.value == 20 * 7 * 24 * 3600


def test_as_unit():
m1 = u.Measurement("15 seconds")
m2 = u.Measurement(4, m1.as_unit())

assert m2.value() == 4
assert m2.value == 4
assert m2.value_as("s") == 60
assert float(m1)==15
assert m1


def test_add_sub():
m1 = u.Measurement("15 seconds")
m2 = u.Measurement(1, "minute")
m3 = m2 - m1
assert m3.value() == 0.75
assert m3.value == 0.75

m4 = m3 + m2 + m1
assert m4 == u.Measurement(120, "second")


def test_negation():
m1 = u.Measurement("15 seconds")
m3 = -m1
assert m3.value == -15.0


def test_mod():
m1 = u.Measurement("18 seconds")
m2 = u.Measurement("1 min")
m3 = (m2%m1).convert_to('s')
assert math.floor(m3.value) == 6
m4= m1%5
assert m4.value == 3

def test_math_func():
m1 = u.Measurement("15.78 seconds")
m2 = u.Measurement("15.48 seconds")
assert math.floor(m1).value == 15
assert math.floor(m2).value == 15
assert math.ceil(m1).value == 16
assert math.ceil(m2).value == 16
assert round(m1).value == 16
assert round(m2).value == 15
assert math.trunc(m1).value == 15
assert math.trunc(m2).value == 15


def test_mult():
m1 = u.Measurement("2 meters")
m2 = u.Measurement(3, "meters")
m3 = m2 * m1
assert m3.value() == 6
assert m3.value == 6

m4 = 3 * m3
assert m4 == u.Measurement(18, "meters squared")
Expand All @@ -148,7 +176,7 @@ def test_div():
m1 = u.Measurement("10 meters")
m2 = u.Measurement(2, "seconds")
m3 = m1 / m2
assert m3.value() == 5
assert m3.value == 5

m4 = 10 / m3
assert m4 == u.Measurement(2, "s/m")
Expand All @@ -159,9 +187,20 @@ def test_div():

def test_string():
m1 = u.Measurement("10 lb")
assert m1.to_string() == "10 lb"
assert str(m1) == "10 lb"
s3 = f"the measurement is {m1}"
assert s3 == "the measurement is 10 lb"

def test_format():
m1 = u.Measurement("9.7552 lb")
s1 = f"the measurement is {m1:kg}"
assert "kg" in s1

s2 = f"the measurement is {m1:-}"
assert s2== "the measurement is 9.7552 "

s3 = f"the measurement is {m1:-kg}"
assert "kg" not in s3


def test_close():
Expand Down
Loading

0 comments on commit d12b1c6

Please sign in to comment.