Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 23 additions & 21 deletions pytac/units.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,18 +221,18 @@ def __init__(self, x, y, post_eng_to_phys=unit_function,
is raised.

Raises:
ValueError: An error occured when the given y coefficients are
neither in increasing or decreasing order.
ValueError: if coefficients are not appropriately monotonic.
"""
super(self.__class__, self).__init__(post_eng_to_phys, pre_phys_to_eng)
self.x = x
self.y = y
self.pp = PchipInterpolator(x, y)

diff = numpy.diff(y)
if not ((numpy.all(diff > 0)) or (numpy.all((diff < 0)))):
raise ValueError("Given coefficients must be monotonically"
"decreasing.")
# Note that the x coefficients are checked by the PchipInterpolator
# constructor.
y_diff = numpy.diff(y)
if not ((numpy.all(y_diff > 0)) or (numpy.all((y_diff < 0)))):
raise ValueError("y coefficients must be monotonically"
"increasing or decreasing.")

def _raw_eng_to_phys(self, eng_value):
"""Convert between engineering and physics units.
Expand All @@ -249,6 +249,9 @@ def _raw_eng_to_phys(self, eng_value):
def _raw_phys_to_eng(self, physics_value):
"""Convert between physics and engineering units.

This expects there to be exactly one solution for x within the
range of the x values in self.x, otherwise a UnitsException is raised.

Args:
physics_value (float): The engineering value to be converted to the
engineering value.
Expand All @@ -258,24 +261,23 @@ def _raw_phys_to_eng(self, physics_value):
value.

Raises:
UnitsException: An error occured when there exist no or more than
one roots.
UnitsException: if there is not exactly one solution.
"""
y = [val - physics_value for val in self.y]
new_pp = PchipInterpolator(self.x, y)
roots = new_pp.roots()

solution_within_bounds = False
unique_root = None
for root in roots:
if root <= self.x[-1] and root >= self.x[0]:
if not solution_within_bounds:
solution_within_bounds = True
correct_root = root
if self.x[0] <= root <= self.x[-1]:
if unique_root is None:
unique_root = root
else:
raise UnitsException('The function {} is not invertible.'
.format(new_pp))
if solution_within_bounds:
return correct_root
else:
raise UnitsException("The function {} does not have a solution "
"within bounds.")
# I believe this should not happen because of the
# requirement for self.y to be monotonically increasing.
raise UnitsException(
"More than one solution within Pchip bounds"
)
if unique_root is None:
raise UnitsException("No solution within Pchip bounds.")
return unique_root
18 changes: 15 additions & 3 deletions test/test_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,26 @@ def test_pp_conversion_to_machine_2_points():
assert pchip_uc.phys_to_eng(1.5) == 1.5


def test_pp_not_monotonically_increasing_error():
def test_PchipInterpolator_raises_ValueError_if_x_not_monotonically_increasing():
with pytest.raises(ValueError):
PchipUnitConv([1, 2, 3], [1, 3, 2])

PchipUnitConv([1, 3, 2], [1, 2, 3])
with pytest.raises(ValueError):
PchipUnitConv([-1, -2, -3], [-1, -2, -3])


def test_PchipInterpolator_raises_ValueError_if_y_not_monotonic():
with pytest.raises(ValueError):
PchipUnitConv([1, 2, 3], [1, 3, 2])


def test_PchipUnitConv_with_solution_outside_bounds_raises_UnitsException():
# This is a linear relationship, but the root is 0, outside of the
# range of measurements.
pchip_uc = PchipUnitConv((1, 2, 3), (1, 2, 3))
with pytest.raises(UnitsException):
pchip_uc.phys_to_eng(0)


def test_PchipUnitConv_with_additional_function():
pchip_uc = PchipUnitConv([2, 4], [2, 4], f1, f2)
assert pchip_uc.eng_to_phys(2) == 4.0
Expand Down