Skip to content

Commit

Permalink
Added sanity check for addition operator, to make sure the two spline…
Browse files Browse the repository at this point in the history
…s added share the same underlying triangle, and the same degree.
  • Loading branch information
qTipTip committed Jan 31, 2020
1 parent 5e14741 commit 24d562a
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 15 deletions.
36 changes: 21 additions & 15 deletions SSplines/spline_function.py
@@ -1,16 +1,19 @@
import numpy as np

from SSplines.constants import UY, UX
from SSplines.helper_functions import coefficients_linear, coefficients_quadratic, coefficients_quadratic_alternative, coefficients_cubic, \
barycentric_coordinates, determine_sub_triangle, evaluate_non_zero_basis_splines, evaluate_non_zero_basis_derivatives, \
from SSplines.helper_functions import coefficients_linear, coefficients_quadratic, coefficients_quadratic_alternative, \
coefficients_cubic, \
barycentric_coordinates, determine_sub_triangle, evaluate_non_zero_basis_splines, \
evaluate_non_zero_basis_derivatives, \
directional_coordinates


class SplineFunction(object):
"""
Represents a single callable spline function of degree 0/1/2/3 over the PS12-split of given triangle.
"""

def __init__(self, triangle, degree, coefficients, alternative_basis = False):
def __init__(self, triangle, degree, coefficients, alternative_basis=False):
self.triangle = np.array(triangle)
self.degree = int(degree)
self.coefficients = np.array(coefficients)
Expand All @@ -23,18 +26,18 @@ def _non_zero_coefficients(self, k):
:return : indices
"""
if self.degree == 0:
return np.take(np.append(self.coefficients,0), k)
return np.take(np.append(self.coefficients, 0), k)
elif self.degree == 1:
return np.take(np.append(self.coefficients,0), coefficients_linear(k))
return np.take(np.append(self.coefficients, 0), coefficients_linear(k))
elif self.degree == 2:
if self.alternative_basis:
return np.take(np.append(self.coefficients,0), coefficients_quadratic_alternative(k))
return np.take(np.append(self.coefficients, 0), coefficients_quadratic_alternative(k))
else:
return np.take(np.append(self.coefficients,0), coefficients_quadratic(k))
return np.take(np.append(self.coefficients, 0), coefficients_quadratic(k))
elif self.degree == 3:
return np.take(np.append(self.coefficients,0), coefficients_cubic(k))
return np.take(np.append(self.coefficients, 0), coefficients_cubic(k))

def __call__(self, x, barycentric = False, exact = False):
def __call__(self, x, barycentric=False, exact=False):
"""
Evaluates the spline function at point(s) x.
:param x: set of points
Expand All @@ -43,10 +46,11 @@ def __call__(self, x, barycentric = False, exact = False):
if barycentric:
b = x
else:
b = barycentric_coordinates(self.triangle, x, exact = exact)
b = barycentric_coordinates(self.triangle, x, exact=exact)

k = determine_sub_triangle(b)
z = evaluate_non_zero_basis_splines(b=b, d=self.degree, k=k, exact = exact, alternative_basis = self.alternative_basis)
z = evaluate_non_zero_basis_splines(b=b, d=self.degree, k=k, exact=exact,
alternative_basis=self.alternative_basis)
c = self._non_zero_coefficients(k)

return np.einsum('...i,...i->...', z, c) # broadcast the dot product to compute all values at once.
Expand Down Expand Up @@ -132,15 +136,16 @@ def lapl(self, x):
"""
return self.ddx(x) + self.ddy(x)

# MATHEMATICAL OPERATORS
# TODO: Check degree and triangle for each of these operations

def __add__(self, other):
"""
Addition of two SplineFunctions.
:param other: Spline Function
:return: SplineFunction
"""

if self.degree != other.degree or not np.allclose(self.triangle, other.triangle):
raise ValueError('The splines are not compatible, check the degree and the underlying triangle')

return SplineFunction(self.triangle, self.degree, self.coefficients + other.coefficients)

def __mul__(self, scalar):
Expand All @@ -149,4 +154,5 @@ def __mul__(self, scalar):
:param scalar: real number
:return: SplineFunction
"""
return SplineFunction(self.triangle, self.degree, scalar * self.coefficients)

return SplineFunction(self.triangle, self.degree, scalar * self.coefficients)
39 changes: 39 additions & 0 deletions tests/test_spline_operators.py
@@ -0,0 +1,39 @@
import pytest
import numpy as np

from SSplines import SplineFunction, SplineSpace


def test_spline_function_add_validation():
t1 = np.array([
[0, 0],
[1, 0],
[0.5, np.sqrt(3) / 2]
])
t2 = np.array([
[0, 0],
[1, 0],
[0.6, np.sqrt(3) / 2]
])

c1 = np.ones(12)
c2 = np.ones(12)
c3 = np.ones(10)
d1 = 2
d2 = 2
d3 = 1

s1 = SplineFunction(t1, d1, c1)
s2 = SplineFunction(t2, d2, c2)
s3 = SplineFunction(t2, d3, c3)

with pytest.raises(ValueError):
s1 + s2

with pytest.raises(ValueError):
s2 + s3

try:
s1 + s1
except ValueError:
pytest.fail('An error is raised when the splines are compatible.')

0 comments on commit 24d562a

Please sign in to comment.