Source code for splipy.utils.refinement

from __future__ import division

__doc__ = 'Implementation of various refinement schemes.'

from splipy.utils import ensure_listlike, check_direction
from math import atan, pi
import numpy as np

# TODO: put control over these tolerances somewhere. Modstate in splipy seems
#       to be the place for it, but we can't let splipy.utils influence the
#       structure of splipy.
def knot_exists(existing_knots, new_knot):
    return np.any(np.isclose(existing_knots, new_knot, atol=1e-7, rtol=1e-10))


[docs]def geometric_refine(obj, alpha, n, direction=0, reverse=False): """geometric_refine(obj, alpha, n, [direction=0], [reverse=False]) Refine a spline object by making a geometric distribution of element sizes. :param obj: The object to refine :type obj: :class:`splipy.SplineObject` :param float alpha: The length ratio between two sequential knot segments :param int n: The number of knots to insert :param direction: The direction to refine in :param bool reverse: Set to `True` to refine towards the other end """ # some error tests on input if n<=0: raise ValueError('n should be greater than 0') direction = check_direction(direction, obj.pardim) if reverse: obj.reverse(direction) # fetch knots knots = obj.knots() knot_start = knots[direction][0] knot_end = knots[direction][-1] dk = knot_end - knot_start # evaluate the factors n = n+1 # redefine n to be knot spans instead of new internal knots totProd = 1.0 totSum = 0.0 for i in range(n): totSum += totProd totProd *= alpha d1 = 1.0 / totSum knot = d1 # compute knot locations new_knots = [] for i in range(n-1): k = knot_start + knot*dk if not knot_exists(knots[direction], k): new_knots.append(k) knot += alpha*d1 d1 *= alpha # do the actual knot insertion obj.insert_knot(new_knots, direction) if reverse: obj.reverse(direction) return obj
[docs]def edge_refine(obj, S, n, direction=0): """edge_refine(obj, S, n, [direction=0]) Refine an object towards both edges in a direction, by sampling an arctan function. :param obj: The object to refine :type obj: :class:`splipy.SplineObject` :param float S: The slope of the arctan function. :param int n: The number of knots to insert :param direction: The direction to refine in """ # some error tests on input if n<=0: raise ValueError('n should be greater than 0') direction = check_direction(direction, obj.pardim) # fetch knots knots = obj.knots() knot_start = knots[direction][0] knot_end = knots[direction][-1] dk = knot_end - knot_start # compute knot locations new_knots = [] max_atan = atan(S) for i in range(1,n+1): xi = -1.0 + 2.0*i/(n+1) xi *= S k = knot_start + (atan(xi)+max_atan)/2/max_atan*dk if not knot_exists(knots[direction], k): new_knots.append(k) # do the actual knot insertion obj.insert_knot(new_knots, direction) return obj
def _splitvector(len, parts): delta = len // parts sizes = [delta for i in range(parts)] remainder = len-parts*delta for i in range(parts-remainder+1, parts): sizes[i] = sizes[i]+1 result = [0] for i in range(1,parts): result.append(sizes[i]+result[i-1]) return result
[docs]def subdivide(objs, n): """Subdivide a list of objects by splitting them up along existing knot lines. The resulting partition will roughly the same number of elements on all pieces. By splitting along *n* lines, we generate *n* + 1 new blocks. The number of subdivisions can be a list of integers: one for each direction, or a single integer for uniform sudivision. :param objs: Objects to split :param n: Number of subdivisions to perform :type n: int or [int] :return: New objects :rtype: [:class:`splipy.SplineObject`] """ pardim = objs[0].pardim # 1 for curves, 2 for surfaces, 3 for volumes n = ensure_listlike(n, pardim) result = objs for d in range(pardim): # split all objects so far along direction d new_results = [] for obj in result: splitting_points = [obj.knots(d)[i] for i in _splitvector(len(obj.knots(d)), n[d]+1)] new_results += obj.split(splitting_points[1:], d) # only keep the smallest pieces in our result list result = new_results return result