feectools 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- feectools/__init__.py +0 -0
- feectools/accelerate/__init__.py +0 -0
- feectools/accelerate/accelerate.py +220 -0
- feectools/accelerate/compile_psydac.mk +52 -0
- feectools/api/__init__.py +0 -0
- feectools/api/essential_bc.py +122 -0
- feectools/api/fem_bilinear_form.py +2226 -0
- feectools/api/fem_common.py +286 -0
- feectools/api/fem_sum_form.py +123 -0
- feectools/api/settings.py +82 -0
- feectools/core/__init__.py +11 -0
- feectools/core/bsplines.py +1107 -0
- feectools/core/bsplines_kernels.py +1349 -0
- feectools/core/field_evaluation_kernels.py +5015 -0
- feectools/core/tests/__init__.py +0 -0
- feectools/core/tests/test_bsplines.py +263 -0
- feectools/core/tests/test_bsplines_kernel.py +40 -0
- feectools/core/tests/test_bsplines_pyccel.py +752 -0
- feectools/ddm/__init__.py +3 -0
- feectools/ddm/basic.py +78 -0
- feectools/ddm/blocking_data_exchanger.py +348 -0
- feectools/ddm/cart.py +1835 -0
- feectools/ddm/interface_data_exchanger.py +122 -0
- feectools/ddm/mpi.py +109 -0
- feectools/ddm/nonblocking_data_exchanger.py +331 -0
- feectools/ddm/partition.py +207 -0
- feectools/ddm/petsc.py +112 -0
- feectools/ddm/tests/__init__.py +0 -0
- feectools/ddm/tests/test_cart_1d.py +138 -0
- feectools/ddm/tests/test_cart_2d.py +164 -0
- feectools/ddm/tests/test_cart_3d.py +158 -0
- feectools/ddm/tests/test_multicart_2d.py +173 -0
- feectools/ddm/tests/test_partition.py +124 -0
- feectools/ddm/utilities.py +24 -0
- feectools/feec/__init__.py +0 -0
- feectools/feec/derivatives.py +780 -0
- feectools/feec/dof_kernels.py +210 -0
- feectools/feec/global_geometric_projectors.py +1073 -0
- feectools/feec/hodge.py +148 -0
- feectools/fem/__init__.py +0 -0
- feectools/fem/basic.py +465 -0
- feectools/fem/grid.py +181 -0
- feectools/fem/partitioning.py +344 -0
- feectools/fem/projectors.py +160 -0
- feectools/fem/splines.py +559 -0
- feectools/fem/tensor.py +1393 -0
- feectools/fem/tests/__init__.py +0 -0
- feectools/fem/tests/analytical_profiles_1d.py +100 -0
- feectools/fem/tests/analytical_profiles_base.py +34 -0
- feectools/fem/tests/splines_error_bounds.py +155 -0
- feectools/fem/tests/test_spline_histopolation.py +120 -0
- feectools/fem/tests/test_spline_interpolation.py +182 -0
- feectools/fem/tests/test_splines.py +184 -0
- feectools/fem/tests/test_splines_par.py +46 -0
- feectools/fem/tests/test_vector_spaces.py +150 -0
- feectools/fem/tests/utilities.py +47 -0
- feectools/fem/vector.py +729 -0
- feectools/linalg/__init__.py +0 -0
- feectools/linalg/basic.py +1386 -0
- feectools/linalg/block.py +1451 -0
- feectools/linalg/direct_solvers.py +201 -0
- feectools/linalg/fft.py +258 -0
- feectools/linalg/kernels/__init__.py +0 -0
- feectools/linalg/kernels/axpy_kernels.py +57 -0
- feectools/linalg/kernels/inner_kernels.py +100 -0
- feectools/linalg/kernels/matvec_kernels.py +206 -0
- feectools/linalg/kernels/stencil2IJV_kernels.py +227 -0
- feectools/linalg/kernels/stencil2coo_kernels.py +179 -0
- feectools/linalg/kernels/transpose_kernels.py +263 -0
- feectools/linalg/kron.py +911 -0
- feectools/linalg/solvers.py +1914 -0
- feectools/linalg/sparse.py +114 -0
- feectools/linalg/stencil.py +2923 -0
- feectools/linalg/stencil_dot_kernels.py +317 -0
- feectools/linalg/stencil_transpose_kernels.py +372 -0
- feectools/linalg/tests/__init__.py +0 -0
- feectools/linalg/tests/test_block.py +1588 -0
- feectools/linalg/tests/test_fft.py +106 -0
- feectools/linalg/tests/test_kron_stencil_matrix.py +114 -0
- feectools/linalg/tests/test_linalg.py +1065 -0
- feectools/linalg/tests/test_matrix_free.py +128 -0
- feectools/linalg/tests/test_solvers.py +213 -0
- feectools/linalg/tests/test_stencil_interface_matrix.py +379 -0
- feectools/linalg/tests/test_stencil_vector.py +1036 -0
- feectools/linalg/tests/test_stencil_vector_space.py +440 -0
- feectools/linalg/topetsc.py +522 -0
- feectools/linalg/utilities.py +200 -0
- feectools/utilities/__init__.py +0 -0
- feectools/utilities/quadratures.py +113 -0
- feectools/utilities/utils.py +166 -0
- feectools/version.py +1 -0
- feectools-0.1.0.dist-info/METADATA +66 -0
- feectools-0.1.0.dist-info/RECORD +98 -0
- feectools-0.1.0.dist-info/WHEEL +5 -0
- feectools-0.1.0.dist-info/entry_points.txt +3 -0
- feectools-0.1.0.dist-info/licenses/AUTHORS +22 -0
- feectools-0.1.0.dist-info/licenses/LICENSE +21 -0
- feectools-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1349 @@
|
|
|
1
|
+
# This file holds the pyccelisable versions of the functions in bsplines.py
|
|
2
|
+
# This will be changed once pyccel can return arrays and can get out=None arguments
|
|
3
|
+
# like Numpy functions.
|
|
4
|
+
|
|
5
|
+
from pyccel.decorators import pure
|
|
6
|
+
from numpy import shape, abs
|
|
7
|
+
import numpy as np
|
|
8
|
+
from typing import Final
|
|
9
|
+
|
|
10
|
+
# Auxiliary functions needed for the bsplines kernels.
|
|
11
|
+
@pure
|
|
12
|
+
def matmul(a: 'float[:,:]', b: 'float[:,:]', c: 'float[:,:]'):
|
|
13
|
+
"""
|
|
14
|
+
Performs the matrix-matrix product a*b and writes the result into c.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
a : array[float]
|
|
19
|
+
The first input array (matrix).
|
|
20
|
+
|
|
21
|
+
b : array[float]
|
|
22
|
+
The second input array (matrix).
|
|
23
|
+
|
|
24
|
+
c : array[float]
|
|
25
|
+
The output array (matrix) which is the result of the matrix-matrix product a.dot(b).
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
sh_a = shape(a)
|
|
29
|
+
sh_b = shape(b)
|
|
30
|
+
|
|
31
|
+
c[:, :] = 0.
|
|
32
|
+
for i in range(sh_a[0]):
|
|
33
|
+
for j in range(sh_b[1]):
|
|
34
|
+
for k in range(sh_a[1]):
|
|
35
|
+
c[i, j] += a[i, k] * b[k, j]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pure
|
|
39
|
+
def sum_vec(a: 'Final[float[:]]') -> float:
|
|
40
|
+
"""
|
|
41
|
+
Sum the elements of a 1D vector.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
a : array[float]
|
|
46
|
+
The 1d vector.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
out = 0.
|
|
50
|
+
|
|
51
|
+
sh_a = shape(a)
|
|
52
|
+
|
|
53
|
+
for i in range(sh_a[0]):
|
|
54
|
+
out += a[i]
|
|
55
|
+
|
|
56
|
+
return out
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pure
|
|
60
|
+
def min_vec(a: 'Final[float[:]]') -> float:
|
|
61
|
+
"""
|
|
62
|
+
Compute the minimum a 1D vector.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
a : array[float]
|
|
67
|
+
The 1D vector.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
out = a[0]
|
|
71
|
+
|
|
72
|
+
sh_a = shape(a)
|
|
73
|
+
|
|
74
|
+
for i in range(sh_a[0]):
|
|
75
|
+
if a[i] < out:
|
|
76
|
+
out = a[i]
|
|
77
|
+
|
|
78
|
+
return out
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@pure
|
|
82
|
+
def max_vec(a: 'Final[float[:]]') -> float:
|
|
83
|
+
"""
|
|
84
|
+
Compute the maximum a 1D vector.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
a : array[float]
|
|
89
|
+
The 1D vector.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
out = a[0]
|
|
93
|
+
|
|
94
|
+
sh_a = shape(a)
|
|
95
|
+
|
|
96
|
+
for i in range(sh_a[0]):
|
|
97
|
+
if a[i] > out:
|
|
98
|
+
out = a[i]
|
|
99
|
+
|
|
100
|
+
return out
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pure
|
|
104
|
+
def max_vec_int(a: 'Final[int[:]]') -> int:
|
|
105
|
+
"""
|
|
106
|
+
Compute the maximum a 1D vector.
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
a : array[float]
|
|
111
|
+
The 1D vector.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
out = a[0]
|
|
115
|
+
|
|
116
|
+
sh_a = shape(a)
|
|
117
|
+
|
|
118
|
+
for i in range(sh_a[0]):
|
|
119
|
+
if a[i] > out:
|
|
120
|
+
out = a[i]
|
|
121
|
+
|
|
122
|
+
return out
|
|
123
|
+
|
|
124
|
+
# =============================================================================
|
|
125
|
+
def find_span_p(knots: 'float[:]', degree: int, x: float):
|
|
126
|
+
"""
|
|
127
|
+
Determine the knot span index at location x, given the B-Splines' knot
|
|
128
|
+
sequence and polynomial degree. See Algorithm A2.1 in [1].
|
|
129
|
+
|
|
130
|
+
For a degree p, the knot span index i identifies the indices [i-p:i] of all
|
|
131
|
+
p+1 non-zero basis functions at a given location x.
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
knots : array_like
|
|
136
|
+
Knots sequence.
|
|
137
|
+
|
|
138
|
+
degree : int
|
|
139
|
+
Polynomial degree of B-splines.
|
|
140
|
+
|
|
141
|
+
x : float
|
|
142
|
+
Location of interest.
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
span : int
|
|
147
|
+
Knot span index.
|
|
148
|
+
|
|
149
|
+
References
|
|
150
|
+
----------
|
|
151
|
+
.. [1] L. Piegl and W. Tiller. The NURBS Book, 2nd ed.,
|
|
152
|
+
Springer-Verlag Berlin Heidelberg GmbH, 1997.
|
|
153
|
+
"""
|
|
154
|
+
# last knot on the left boundary
|
|
155
|
+
low = degree
|
|
156
|
+
# first knot on the right boundary
|
|
157
|
+
high = len(knots)-1-degree
|
|
158
|
+
|
|
159
|
+
# Check if point is exactly on left/right boundary, or outside domain
|
|
160
|
+
if x <= knots[low ]: return low
|
|
161
|
+
if x >= knots[high]: return high-1
|
|
162
|
+
|
|
163
|
+
# Perform binary search
|
|
164
|
+
span = (low+high)//2
|
|
165
|
+
while x < knots[span] or x >= knots[span+1]:
|
|
166
|
+
if x < knots[span]:
|
|
167
|
+
high = span
|
|
168
|
+
else:
|
|
169
|
+
low = span
|
|
170
|
+
span = (low+high)//2
|
|
171
|
+
|
|
172
|
+
return span
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# =============================================================================
|
|
176
|
+
def find_spans_p(knots: 'float[:]', degree: int, x: 'float[:]', out: 'int[:]'):
|
|
177
|
+
"""
|
|
178
|
+
Determine the knot span index at a set of locations x, given the B-Splines' knot
|
|
179
|
+
sequence and polynomial degree. See Algorithm A2.1 in [1].
|
|
180
|
+
|
|
181
|
+
For a degree p, the knot span index i identifies the indices [i-p:i] of all
|
|
182
|
+
p+1 non-zero basis functions at a given location x.
|
|
183
|
+
|
|
184
|
+
Parameters
|
|
185
|
+
----------
|
|
186
|
+
knots : array_like
|
|
187
|
+
Knots sequence.
|
|
188
|
+
|
|
189
|
+
degree : int
|
|
190
|
+
Polynomial degree of B-splines.
|
|
191
|
+
|
|
192
|
+
x : array_like of floats
|
|
193
|
+
Locations of interest.
|
|
194
|
+
|
|
195
|
+
out : array
|
|
196
|
+
The result will be inserted into this array.
|
|
197
|
+
It should be of the appropriate shape and dtype.
|
|
198
|
+
"""
|
|
199
|
+
n = x.shape[0]
|
|
200
|
+
|
|
201
|
+
for i in range(n):
|
|
202
|
+
out[i] = find_span_p(knots, degree, x[i])
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# =============================================================================
|
|
206
|
+
def basis_funs_p(knots: 'float[:]', degree: int, x: float, span: int, out: 'float[:]'):
|
|
207
|
+
"""
|
|
208
|
+
Compute the non-vanishing B-splines at a unique location.
|
|
209
|
+
|
|
210
|
+
Parameters
|
|
211
|
+
----------
|
|
212
|
+
knots : array_like of floats
|
|
213
|
+
Knots sequence.
|
|
214
|
+
|
|
215
|
+
degree : int
|
|
216
|
+
Polynomial degree of B-splines.
|
|
217
|
+
|
|
218
|
+
x : float
|
|
219
|
+
Evaluation point.
|
|
220
|
+
|
|
221
|
+
span : int
|
|
222
|
+
Knot span index.
|
|
223
|
+
|
|
224
|
+
out : array
|
|
225
|
+
The result will be inserted into this array.
|
|
226
|
+
It should be of the appropriate shape and dtype.
|
|
227
|
+
|
|
228
|
+
Notes
|
|
229
|
+
-----
|
|
230
|
+
The original Algorithm A2.2 in The NURBS Book [1] is here slightly improved
|
|
231
|
+
by using 'left' and 'right' temporary arrays that are one element shorter.
|
|
232
|
+
|
|
233
|
+
References
|
|
234
|
+
----------
|
|
235
|
+
.. [1] L. Piegl and W. Tiller. The NURBS Book, 2nd ed.,
|
|
236
|
+
Springer-Verlag Berlin Heidelberg GmbH, 1997.
|
|
237
|
+
"""
|
|
238
|
+
out[0] = 1.0
|
|
239
|
+
if degree == 0:
|
|
240
|
+
return
|
|
241
|
+
left = np.zeros(degree, dtype=float)
|
|
242
|
+
right = np.zeros(degree, dtype=float)
|
|
243
|
+
|
|
244
|
+
for j in range(degree):
|
|
245
|
+
left[j] = x - knots[span - j]
|
|
246
|
+
right[j] = knots[span + 1 + j] - x
|
|
247
|
+
saved = 0.0
|
|
248
|
+
for r in range(j + 1):
|
|
249
|
+
temp = out[r] / (right[r] + left[j - r])
|
|
250
|
+
out[r] = saved + right[r] * temp
|
|
251
|
+
saved = left[j - r] * temp
|
|
252
|
+
out[j + 1] = saved
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# =============================================================================
|
|
256
|
+
def basis_funs_array_p(knots: 'float[:]', degree: int, x: 'float[:]', span: 'int[:]', out: 'float[:,:]'):
|
|
257
|
+
"""
|
|
258
|
+
Compute the non-vanishing B-splines at locations in x, given the knot sequence,
|
|
259
|
+
polynomial degree and knot span. See Algorithm A2.2 in [1].
|
|
260
|
+
|
|
261
|
+
Parameters
|
|
262
|
+
----------
|
|
263
|
+
knots : array_like of floats
|
|
264
|
+
Knots sequence.
|
|
265
|
+
|
|
266
|
+
degree : int
|
|
267
|
+
Polynomial degree of B-splines.
|
|
268
|
+
|
|
269
|
+
x : array_like of floats
|
|
270
|
+
Evaluation points.
|
|
271
|
+
|
|
272
|
+
span : array_like of int
|
|
273
|
+
Knot span indexes.
|
|
274
|
+
|
|
275
|
+
out : array
|
|
276
|
+
The result will be inserted into this array.
|
|
277
|
+
It should be of the appropriate shape and dtype.
|
|
278
|
+
|
|
279
|
+
References
|
|
280
|
+
----------
|
|
281
|
+
.. [1] L. Piegl and W. Tiller. The NURBS Book, 2nd ed.,
|
|
282
|
+
Springer-Verlag Berlin Heidelberg GmbH, 1997.
|
|
283
|
+
"""
|
|
284
|
+
n = x.shape[0]
|
|
285
|
+
for i in range(n):
|
|
286
|
+
basis_funs_p(knots, degree, x[i], span[i], out[i, :])
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# =============================================================================
|
|
290
|
+
def basis_funs_1st_der_p(knots: 'float[:]', degree: int, x: float, span: int, out: 'float[:]'):
|
|
291
|
+
"""
|
|
292
|
+
Compute the first derivative of the non-vanishing B-splines at a location.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
knots : array_like
|
|
297
|
+
Knots sequence.
|
|
298
|
+
|
|
299
|
+
degree : int
|
|
300
|
+
Polynomial degree of B-splines.
|
|
301
|
+
|
|
302
|
+
x : float
|
|
303
|
+
Evaluation point.
|
|
304
|
+
|
|
305
|
+
span : int
|
|
306
|
+
Knot span index.
|
|
307
|
+
|
|
308
|
+
out : array
|
|
309
|
+
The result will be inserted into this array.
|
|
310
|
+
It should be of the appropriate shape and dtype.
|
|
311
|
+
|
|
312
|
+
Notes
|
|
313
|
+
-----
|
|
314
|
+
See function 's_bsplines_non_uniform__eval_deriv' in Selalib's ([2]) source file
|
|
315
|
+
'src/splines/sll_m_bsplines_non_uniform.F90'.
|
|
316
|
+
|
|
317
|
+
References
|
|
318
|
+
----------
|
|
319
|
+
.. [2] SELALIB, Semi-Lagrangian Library. http://selalib.gforge.inria.fr
|
|
320
|
+
"""
|
|
321
|
+
# For degree 0, the first derivative is constant zero
|
|
322
|
+
if degree == 0:
|
|
323
|
+
out[:] = 0
|
|
324
|
+
return
|
|
325
|
+
|
|
326
|
+
# Compute nonzero basis functions and knot differences for splines
|
|
327
|
+
# up to degree deg-1
|
|
328
|
+
values = np.zeros(degree)
|
|
329
|
+
basis_funs_p(knots, degree-1, x, span, values)
|
|
330
|
+
|
|
331
|
+
# Compute derivatives at x using formula based on difference of splines of
|
|
332
|
+
# degree deg-1
|
|
333
|
+
# -------
|
|
334
|
+
# j = 0
|
|
335
|
+
saved = degree * values[0] / (knots[span+1]-knots[span+1-degree])
|
|
336
|
+
out[0] = -saved
|
|
337
|
+
# j = 1,...,degree-1
|
|
338
|
+
for j in range(1,degree):
|
|
339
|
+
temp = saved
|
|
340
|
+
saved = degree * values[j] / (knots[span+j+1]-knots[span+j+1-degree])
|
|
341
|
+
out[j] = temp - saved
|
|
342
|
+
# j = degree
|
|
343
|
+
out[degree] = saved
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
# =============================================================================
|
|
347
|
+
def basis_funs_all_ders_p(knots: 'float[:]', degree: int, x: float, span: int, n: int, normalization: bool,
|
|
348
|
+
out: 'float[:,:]'):
|
|
349
|
+
"""
|
|
350
|
+
Evaluate value and n derivatives at x of all basis functions with
|
|
351
|
+
support in interval :math:`[x_{span-1}, x_{span}]`.
|
|
352
|
+
|
|
353
|
+
If called with normalization=True, this uses M-splines instead of B-splines.
|
|
354
|
+
|
|
355
|
+
Fills a 2D array with n+1 (from 0-th to n-th) derivatives at x
|
|
356
|
+
of all (degree+1) non-vanishing basis functions in given span.
|
|
357
|
+
|
|
358
|
+
.. math::
|
|
359
|
+
ders[i,j] = \\frac{d^i}{dx^i} B_k(x) \\, \\text{with} k=(span-degree+j),
|
|
360
|
+
\\forall (i,j), 0 \\leq i \\leq n \\, 0 \\leq j \\leq \\text{degree}+1.
|
|
361
|
+
|
|
362
|
+
Parameters
|
|
363
|
+
----------
|
|
364
|
+
knots : array_like
|
|
365
|
+
Knots sequence.
|
|
366
|
+
|
|
367
|
+
degree : int
|
|
368
|
+
Polynomial degree of B-splines.
|
|
369
|
+
|
|
370
|
+
x : float
|
|
371
|
+
Evaluation point.
|
|
372
|
+
|
|
373
|
+
span : int
|
|
374
|
+
Knot span index.
|
|
375
|
+
|
|
376
|
+
n : int
|
|
377
|
+
Max derivative of interest.
|
|
378
|
+
|
|
379
|
+
normalization: bool
|
|
380
|
+
Set to False to get B-Splines and True to get M-Splines
|
|
381
|
+
|
|
382
|
+
out : array
|
|
383
|
+
The result will be inserted into this array.
|
|
384
|
+
It should be of the appropriate shape and dtype.
|
|
385
|
+
|
|
386
|
+
Notes
|
|
387
|
+
-----
|
|
388
|
+
The original Algorithm A2.3 in The NURBS Book [1] is here improved:
|
|
389
|
+
- 'left' and 'right' arrays are 1 element shorter;
|
|
390
|
+
- inverse of knot differences are saved to avoid unnecessary divisions;
|
|
391
|
+
- innermost loops are replaced with vector operations on slices.
|
|
392
|
+
|
|
393
|
+
References
|
|
394
|
+
----------
|
|
395
|
+
.. [1] L. Piegl and W. Tiller. The NURBS Book, 2nd ed.,
|
|
396
|
+
Springer-Verlag Berlin Heidelberg GmbH, 1997.
|
|
397
|
+
"""
|
|
398
|
+
sh_a = np.empty(2)
|
|
399
|
+
sh_b = np.empty(2)
|
|
400
|
+
left = np.empty(degree)
|
|
401
|
+
right = np.empty(degree)
|
|
402
|
+
ndu = np.empty((degree+1, degree+1))
|
|
403
|
+
a = np.empty((2, degree+1))
|
|
404
|
+
temp_d = np.empty((1, 1))
|
|
405
|
+
# Number of derivatives that need to be effectively computed
|
|
406
|
+
# Derivatives higher than degree are = 0.
|
|
407
|
+
ne = min(n, degree)
|
|
408
|
+
|
|
409
|
+
# Compute nonzero basis functions and knot differences for splines
|
|
410
|
+
# up to degree, which are needed to compute derivatives.
|
|
411
|
+
# Store values in 2D temporary array 'ndu' (square matrix).
|
|
412
|
+
|
|
413
|
+
ndu[0, 0] = 1.0
|
|
414
|
+
for j in range(degree):
|
|
415
|
+
left[j] = x - knots[span-j]
|
|
416
|
+
right[j] = knots[span+1+j] - x
|
|
417
|
+
saved = 0.0
|
|
418
|
+
for r in range(j+1):
|
|
419
|
+
# compute inverse of knot differences and save them into lower triangular part of ndu
|
|
420
|
+
ndu[j + 1, r] = 1.0 / (right[r] + left[j - r])
|
|
421
|
+
# compute basis functions and save them into upper triangular part of ndu
|
|
422
|
+
temp = ndu[r, j] * ndu[j + 1, r]
|
|
423
|
+
ndu[r, j + 1] = saved + right[r] * temp
|
|
424
|
+
saved = left[j - r] * temp
|
|
425
|
+
ndu[j + 1, j + 1] = saved
|
|
426
|
+
|
|
427
|
+
# Compute derivatives in 2D output array 'out'
|
|
428
|
+
out[0, :] = ndu[:, degree]
|
|
429
|
+
|
|
430
|
+
for r in range(degree+1):
|
|
431
|
+
|
|
432
|
+
s1 = 0
|
|
433
|
+
s2 = 1
|
|
434
|
+
a[0, 0] = 1.0
|
|
435
|
+
for k in range(1, ne + 1):
|
|
436
|
+
d = 0.0
|
|
437
|
+
rk = r-k
|
|
438
|
+
pk = degree-k
|
|
439
|
+
if r >= k:
|
|
440
|
+
a[s2, 0] = a[s1, 0] * ndu[pk + 1, rk]
|
|
441
|
+
d = a[s2, 0] * ndu[rk, pk]
|
|
442
|
+
|
|
443
|
+
j1 = 1 if (rk > -1 ) else -rk
|
|
444
|
+
j2 = k-1 if (r-1 <= pk) else degree-r
|
|
445
|
+
|
|
446
|
+
a[s2, j1:j2 + 1] = (a[s1, j1:j2 + 1] - a[s1, j1 - 1:j2]) * ndu[pk + 1, rk + j1:rk + j2 + 1]
|
|
447
|
+
# temp_d[:, :] = np.matmul(a[s2:s2 + 1, j1:j2 + 1], ndu[rk + j1:rk + j2 + 1, pk: pk + 1])
|
|
448
|
+
|
|
449
|
+
sh_a[:] = shape(a[s2:s2 + 1, j1:j2 + 1])
|
|
450
|
+
sh_b[:] = shape(ndu[rk + j1:rk + j2 + 1, pk: pk + 1])
|
|
451
|
+
|
|
452
|
+
if sh_a[0] == 0 or sh_a[1] == 0 or sh_b[0] == 0 or sh_b[1] == 0:
|
|
453
|
+
temp_d[:, :] = 0.
|
|
454
|
+
else:
|
|
455
|
+
matmul(a[s2:s2 + 1, j1:j2 + 1], ndu[rk + j1:rk + j2 + 1, pk: pk + 1],temp_d[:, :])
|
|
456
|
+
d+= temp_d[0, 0]
|
|
457
|
+
if r <= pk:
|
|
458
|
+
a[s2, k] = - a[s1, k - 1] * ndu[pk + 1, r]
|
|
459
|
+
d += a[s2, k] * ndu[r, pk]
|
|
460
|
+
|
|
461
|
+
out[k, r] = d
|
|
462
|
+
j = s1
|
|
463
|
+
s1 = s2
|
|
464
|
+
s2 = j
|
|
465
|
+
|
|
466
|
+
# Multiply derivatives by correct factors
|
|
467
|
+
r = degree
|
|
468
|
+
for k in range(1, ne+1):
|
|
469
|
+
out[k, :] = out[k, :] * r
|
|
470
|
+
r = r * (degree-k)
|
|
471
|
+
|
|
472
|
+
if normalization:
|
|
473
|
+
for i in range(degree + 1):
|
|
474
|
+
out[:, i] *= (degree + 1) / (knots[i + span + 1] - knots[i + span - degree])
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
# =============================================================================
|
|
478
|
+
def basis_integrals_p(knots: 'float[:]', degree: int, out: 'float[:]'):
|
|
479
|
+
"""
|
|
480
|
+
Return the integral of each B-spline basis function over the real line:
|
|
481
|
+
|
|
482
|
+
:math: K[i] := \\int_{-\\infty}^{+\\infty} B[i](x) dx = (T[i+p+1]-T[i]) / (p+1).
|
|
483
|
+
|
|
484
|
+
This array can be used to convert B-splines to M-splines, which have unit
|
|
485
|
+
integral over the real line but no partition-of-unity property.
|
|
486
|
+
|
|
487
|
+
Parameters
|
|
488
|
+
----------
|
|
489
|
+
knots : array_like
|
|
490
|
+
Knots sequence.
|
|
491
|
+
|
|
492
|
+
degree : int
|
|
493
|
+
Polynomial degree of B-splines.
|
|
494
|
+
|
|
495
|
+
out : array
|
|
496
|
+
The result will be inserted into this array.
|
|
497
|
+
It should be of the appropriate shape and dtype.
|
|
498
|
+
|
|
499
|
+
Notes
|
|
500
|
+
-----
|
|
501
|
+
For convenience, this function does not distinguish between periodic and
|
|
502
|
+
non-periodic spaces, hence the length of the output array is always equal
|
|
503
|
+
to (len(knots)-degree-1). In the periodic case the last (degree) values in
|
|
504
|
+
the array are redundant, as they are a copy of the first (degree) values.
|
|
505
|
+
|
|
506
|
+
"""
|
|
507
|
+
T = knots
|
|
508
|
+
p = degree
|
|
509
|
+
n = len(T)-p-1
|
|
510
|
+
for i in range(n):
|
|
511
|
+
out[i] = (T[i + p + 1] - T[i])/ (p + 1)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
# =============================================================================
|
|
515
|
+
def collocation_matrix_p(knots: 'float[:]', degree: int, periodic: bool, normalization: bool, xgrid: 'float[:]',
|
|
516
|
+
out: 'float[:,:]', multiplicity : int = 1):
|
|
517
|
+
"""
|
|
518
|
+
Compute the collocation matrix :math:`C_ij = B_j(x_i)`, which contains the
|
|
519
|
+
values of each B-spline basis function :math:`B_j` at all locations :math:`x_i`.
|
|
520
|
+
|
|
521
|
+
If called with normalization=True, this uses M-splines instead of B-splines.
|
|
522
|
+
|
|
523
|
+
Parameters
|
|
524
|
+
----------
|
|
525
|
+
knots : array_like
|
|
526
|
+
Knots sequence.
|
|
527
|
+
|
|
528
|
+
degree : int
|
|
529
|
+
Polynomial degree of spline space.
|
|
530
|
+
|
|
531
|
+
periodic : bool
|
|
532
|
+
True if domain is periodic, False otherwise.
|
|
533
|
+
|
|
534
|
+
normalization : bool
|
|
535
|
+
Set to False for B-splines, and True for M-splines.
|
|
536
|
+
|
|
537
|
+
xgrid : array_like
|
|
538
|
+
Evaluation points.
|
|
539
|
+
|
|
540
|
+
out : array
|
|
541
|
+
The result will be inserted into this array.
|
|
542
|
+
It should be of the appropriate shape and dtype.
|
|
543
|
+
|
|
544
|
+
multiplicity : int
|
|
545
|
+
Multiplicity of the knots in the knot sequence, we assume that the same
|
|
546
|
+
multiplicity applies to each interior knot.
|
|
547
|
+
|
|
548
|
+
"""
|
|
549
|
+
# Number of basis functions (in periodic case remove degree repeated elements)
|
|
550
|
+
nb = len(knots)-degree-1
|
|
551
|
+
if periodic:
|
|
552
|
+
nb -= degree + 1 - multiplicity
|
|
553
|
+
|
|
554
|
+
# Number of evaluation points
|
|
555
|
+
nx = len(xgrid)
|
|
556
|
+
|
|
557
|
+
basis = np.zeros((nx, degree + 1))
|
|
558
|
+
spans = np.zeros(nx, dtype=int)
|
|
559
|
+
find_spans_p(knots, degree, xgrid, spans)
|
|
560
|
+
basis_funs_array_p(knots, degree, xgrid, spans, basis)
|
|
561
|
+
|
|
562
|
+
# Fill in non-zero matrix values
|
|
563
|
+
|
|
564
|
+
# Rescaling of B-splines, to get M-splines if needed
|
|
565
|
+
if not normalization:
|
|
566
|
+
if periodic:
|
|
567
|
+
for i in range(nx):
|
|
568
|
+
for j in range(degree + 1):
|
|
569
|
+
actual_j = (spans[i] - degree + j) % nb
|
|
570
|
+
out[i, actual_j] = basis[i, j]
|
|
571
|
+
else:
|
|
572
|
+
for i in range(nx):
|
|
573
|
+
out[i, spans[i] - degree:spans[i] + 1] = basis[i, :]
|
|
574
|
+
else:
|
|
575
|
+
integrals = np.zeros(knots.shape[0] - degree - 1)
|
|
576
|
+
basis_integrals_p(knots, degree, integrals)
|
|
577
|
+
scaling = 1.0 / integrals
|
|
578
|
+
if periodic:
|
|
579
|
+
for i in range(nx):
|
|
580
|
+
for j in range(degree + 1):
|
|
581
|
+
actual_j = (spans[i] - degree + j) % nb
|
|
582
|
+
out[i, actual_j] = basis[i, j] * scaling[spans[i] - degree + j]
|
|
583
|
+
|
|
584
|
+
else:
|
|
585
|
+
for i in range(nx):
|
|
586
|
+
local_scaling = scaling[spans[i] - degree:spans[i] + 1]
|
|
587
|
+
out[i, spans[i] - degree:spans[i] + 1] = basis[i, :] * local_scaling[:]
|
|
588
|
+
|
|
589
|
+
# Mitigate round-off errors
|
|
590
|
+
for x in range(nx):
|
|
591
|
+
for y in range(nb):
|
|
592
|
+
if abs(out[x, y]) < 1e-14:
|
|
593
|
+
out[x, y] = 0.0
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
# =============================================================================
|
|
597
|
+
def histopolation_matrix_p(knots: 'float[:]', degree: int, periodic: bool, normalization: bool, xgrid: 'float[:]',
|
|
598
|
+
check_boundary: bool, elevated_knots: 'float[:]', out: 'float[:,:]', multiplicity : int = 1):
|
|
599
|
+
"""Computes the histopolation matrix.
|
|
600
|
+
|
|
601
|
+
If called with normalization=True, this uses M-splines instead of B-splines.
|
|
602
|
+
|
|
603
|
+
Parameters
|
|
604
|
+
----------
|
|
605
|
+
knots : array_like
|
|
606
|
+
Knots sequence.
|
|
607
|
+
|
|
608
|
+
degree : int
|
|
609
|
+
Polynomial degree of spline space.
|
|
610
|
+
|
|
611
|
+
periodic : bool
|
|
612
|
+
True if domain is periodic, False otherwise.
|
|
613
|
+
|
|
614
|
+
normalization : str
|
|
615
|
+
Set to False for B-splines, and True for M-splines.
|
|
616
|
+
|
|
617
|
+
xgrid : array_like
|
|
618
|
+
Grid points.
|
|
619
|
+
|
|
620
|
+
check_boundary : bool, default=True
|
|
621
|
+
If true and ``periodic``, will check the boundaries of ``xgrid``.
|
|
622
|
+
|
|
623
|
+
out : array
|
|
624
|
+
The result will be inserted into this array.
|
|
625
|
+
It should be of the appropriate shape and dtype.
|
|
626
|
+
|
|
627
|
+
multiplicity : int
|
|
628
|
+
Multiplicity of the knots in the knot sequence, we assume that the same
|
|
629
|
+
multiplicity applies to each interior knot.
|
|
630
|
+
|
|
631
|
+
Notes
|
|
632
|
+
-----
|
|
633
|
+
The histopolation matrix :math:`H_{ij} = \\int_{x_i}^{x_{i+1}}B_j(x)\\,dx`
|
|
634
|
+
contains the integrals of each B-spline basis function :math:`B_j` between
|
|
635
|
+
two successive grid points.
|
|
636
|
+
"""
|
|
637
|
+
nb = len(knots) - degree - 1
|
|
638
|
+
if periodic:
|
|
639
|
+
nb -= degree + 1 - multiplicity
|
|
640
|
+
|
|
641
|
+
# Number of evaluation points
|
|
642
|
+
nx = len(xgrid)
|
|
643
|
+
|
|
644
|
+
# In periodic case, make sure that evaluation points include domain boundaries
|
|
645
|
+
xgrid_new = np.zeros(len(xgrid) + 2)
|
|
646
|
+
actual_len = len(xgrid)
|
|
647
|
+
if periodic:
|
|
648
|
+
if check_boundary:
|
|
649
|
+
xmin = knots[degree]
|
|
650
|
+
xmax = knots[len(knots) - 1 - degree]
|
|
651
|
+
|
|
652
|
+
if xgrid[0] > xmin and xgrid[-1] < xmax:
|
|
653
|
+
xgrid_new[0] = xmin
|
|
654
|
+
xgrid_new[1:-1] = xgrid[:]
|
|
655
|
+
xgrid_new[-1] = xmax
|
|
656
|
+
actual_len += 2
|
|
657
|
+
|
|
658
|
+
elif xgrid[0] > xmin:
|
|
659
|
+
xgrid_new[0] = xmin
|
|
660
|
+
xgrid_new[1:-1] = xgrid[:]
|
|
661
|
+
actual_len += 1
|
|
662
|
+
|
|
663
|
+
elif xgrid[-1] < xmax:
|
|
664
|
+
xgrid_new[-2] = xmax
|
|
665
|
+
xgrid_new[:-2] = xgrid
|
|
666
|
+
actual_len += 1
|
|
667
|
+
else:
|
|
668
|
+
xgrid_new[:-2] = xgrid
|
|
669
|
+
|
|
670
|
+
else:
|
|
671
|
+
xgrid_new[:-2] = xgrid
|
|
672
|
+
else:
|
|
673
|
+
xgrid_new[:-2] = xgrid
|
|
674
|
+
|
|
675
|
+
# B-splines of degree p+1: basis[i,j] := Bj(xi)
|
|
676
|
+
|
|
677
|
+
# NOTES:
|
|
678
|
+
# . cannot use M-splines in analytical formula for histopolation matrix
|
|
679
|
+
# . always use non-periodic splines to avoid circulant matrix structure
|
|
680
|
+
nb_elevated = len(elevated_knots) - (degree + 1) - 1
|
|
681
|
+
colloc = np.zeros((actual_len, nb_elevated))
|
|
682
|
+
collocation_matrix_p(elevated_knots,
|
|
683
|
+
degree + 1,
|
|
684
|
+
False,
|
|
685
|
+
False,
|
|
686
|
+
xgrid_new[:actual_len],
|
|
687
|
+
colloc,
|
|
688
|
+
multiplicity = multiplicity)
|
|
689
|
+
|
|
690
|
+
m = colloc.shape[0] - 1
|
|
691
|
+
n = colloc.shape[1] - 1
|
|
692
|
+
|
|
693
|
+
spans = np.zeros(colloc.shape[0], dtype=int)
|
|
694
|
+
for i in range(colloc.shape[0]):
|
|
695
|
+
local_span = 0
|
|
696
|
+
for j in range(colloc.shape[1]):
|
|
697
|
+
if abs(colloc[i, j]) != 0:
|
|
698
|
+
local_span = j
|
|
699
|
+
break
|
|
700
|
+
spans[i] = local_span + degree + 1
|
|
701
|
+
|
|
702
|
+
# Compute histopolation matrix from collocation matrix of higher degree
|
|
703
|
+
if periodic:
|
|
704
|
+
temp_array = np.zeros((m, n))
|
|
705
|
+
H = temp_array[:, :]
|
|
706
|
+
else:
|
|
707
|
+
H = out[:, :]
|
|
708
|
+
|
|
709
|
+
if normalization:
|
|
710
|
+
for i in range(m):
|
|
711
|
+
# Indices of first/last non-zero elements in row of collocation matrix
|
|
712
|
+
jstart = spans[i] - (degree + 1)
|
|
713
|
+
jend = min(spans[i + 1], n)
|
|
714
|
+
# Compute non-zero values of histopolation matrix
|
|
715
|
+
for j in range(1 + jstart, jend + 1):
|
|
716
|
+
# s = np.sum(colloc[i, 0:j]) - np.sum(colloc[i + 1, 0:j])
|
|
717
|
+
s = sum_vec(colloc[i, 0:j]) - sum_vec(colloc[i + 1, 0:j])
|
|
718
|
+
H[i, j - 1] = s
|
|
719
|
+
|
|
720
|
+
else:
|
|
721
|
+
integrals = np.zeros(knots.shape[0] - degree - 1)
|
|
722
|
+
basis_integrals_p(knots, degree, integrals)
|
|
723
|
+
for i in range(m):
|
|
724
|
+
# Indices of first/last non-zero elements in row of collocation matrix
|
|
725
|
+
jstart = spans[i] - (degree + 1)
|
|
726
|
+
jend = min(spans[i + 1], n)
|
|
727
|
+
# Compute non-zero values of histopolation matrix
|
|
728
|
+
for j in range(1 + jstart, jend + 1):
|
|
729
|
+
# s = np.sum(colloc[i, 0:j]) - np.sum(colloc[i + 1, 0:j])
|
|
730
|
+
s = sum_vec(colloc[i, 0:j]) - sum_vec(colloc[i + 1, 0:j])
|
|
731
|
+
H[i, j - 1] = s * integrals[j - 1]
|
|
732
|
+
|
|
733
|
+
# Mitigate round-off errors
|
|
734
|
+
for i in range(m):
|
|
735
|
+
for j in range(n):
|
|
736
|
+
if abs(H[i, j]) < 1e-14:
|
|
737
|
+
H[i, j] = 0.0
|
|
738
|
+
|
|
739
|
+
# Non periodic case: Stop here
|
|
740
|
+
if periodic:
|
|
741
|
+
# Periodic case: wrap around histopolation matrix
|
|
742
|
+
# 1. identify repeated basis functions (sum columns)
|
|
743
|
+
# 2. identify split interval (sum rows)
|
|
744
|
+
for i in range(m):
|
|
745
|
+
for j in range(n):
|
|
746
|
+
out[i % nx, j % nb] += H[i, j]
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
# =============================================================================
|
|
750
|
+
def merge_sort(a: 'float[:]'):
|
|
751
|
+
"""Performs a 'in place' merge sort of the input list
|
|
752
|
+
|
|
753
|
+
Parameters
|
|
754
|
+
----------
|
|
755
|
+
a : array_like
|
|
756
|
+
1D array/list to sort using merge sort
|
|
757
|
+
"""
|
|
758
|
+
if len(a) != 1 and len(a) != 0:
|
|
759
|
+
n = len(a)
|
|
760
|
+
|
|
761
|
+
a1 = np.zeros(n // 2)
|
|
762
|
+
a1[:] = a[:n // 2]
|
|
763
|
+
a2 = np.zeros(n - n // 2)
|
|
764
|
+
a2[:] = a[n // 2:]
|
|
765
|
+
|
|
766
|
+
merge_sort(a1)
|
|
767
|
+
merge_sort(a2)
|
|
768
|
+
|
|
769
|
+
i_a1 = 0
|
|
770
|
+
i_a2 = 0
|
|
771
|
+
for i in range(n):
|
|
772
|
+
a1_i = a1[i_a1]
|
|
773
|
+
a2_i = a2[i_a2]
|
|
774
|
+
if a1_i < a2_i:
|
|
775
|
+
a[i] = a1_i
|
|
776
|
+
i_a1 += 1
|
|
777
|
+
else:
|
|
778
|
+
a[i] = a2_i
|
|
779
|
+
i_a2 += 1
|
|
780
|
+
if i_a1 == len(a1) or i_a2 == len(a2):
|
|
781
|
+
last_i = i
|
|
782
|
+
break
|
|
783
|
+
|
|
784
|
+
for i_1 in range(n // 2 - i_a1):
|
|
785
|
+
a[last_i + 1 + i_1] = a1[i_a1 + i_1]
|
|
786
|
+
|
|
787
|
+
for i_2 in range(n - n // 2 - i_a2):
|
|
788
|
+
a[last_i + 1 + i_2] = a2[i_a2 + i_2]
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
# =============================================================================
|
|
792
|
+
def breakpoints_p(knots: 'float[:]', degree: int, out: 'float[:]', tol: float = 1e-15):
|
|
793
|
+
"""
|
|
794
|
+
Determine breakpoints' coordinates.
|
|
795
|
+
|
|
796
|
+
Parameters
|
|
797
|
+
----------
|
|
798
|
+
knots : array_like
|
|
799
|
+
Knots sequence.
|
|
800
|
+
|
|
801
|
+
degree : int
|
|
802
|
+
Polynomial degree of B-splines.
|
|
803
|
+
|
|
804
|
+
tol: float
|
|
805
|
+
If the distance between two knots is less than tol, we assume
|
|
806
|
+
that they are repeated knots which correspond to the same break point.
|
|
807
|
+
|
|
808
|
+
out : array
|
|
809
|
+
The result will be inserted into this array.
|
|
810
|
+
It should be of the appropriate shape and dtype.
|
|
811
|
+
|
|
812
|
+
Returns
|
|
813
|
+
-------
|
|
814
|
+
last_index : int
|
|
815
|
+
Last meaningful index + 1, e.g. the actual interesting result
|
|
816
|
+
is ``out[:last_index]``.
|
|
817
|
+
"""
|
|
818
|
+
# knots = np.array(knots)
|
|
819
|
+
# diff = np.append(True, abs(np.diff(knots[degree:-degree]))>tol)
|
|
820
|
+
# return knots[degree:-degree][diff]
|
|
821
|
+
|
|
822
|
+
out[0] = knots[degree]
|
|
823
|
+
i_out = 1
|
|
824
|
+
for i in range(degree, len(knots) - degree - 1):
|
|
825
|
+
if abs(knots[i] - knots[i + 1]) > tol:
|
|
826
|
+
out[i_out] = knots[i + 1]
|
|
827
|
+
i_out += 1
|
|
828
|
+
return i_out
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
# =============================================================================
|
|
832
|
+
def greville_p(knots: 'float[:]', degree: int, periodic: bool, out:'float[:]', multiplicity: int=1):
|
|
833
|
+
"""
|
|
834
|
+
Compute coordinates of all Greville points.
|
|
835
|
+
|
|
836
|
+
Parameters
|
|
837
|
+
----------
|
|
838
|
+
knots : array_like
|
|
839
|
+
Knots sequence.
|
|
840
|
+
|
|
841
|
+
degree : int
|
|
842
|
+
Polynomial degree of B-splines.
|
|
843
|
+
|
|
844
|
+
periodic : bool
|
|
845
|
+
True if domain is periodic, False otherwise.
|
|
846
|
+
|
|
847
|
+
out : array
|
|
848
|
+
The result will be inserted into this array.
|
|
849
|
+
It should be of the appropriate shape and dtype.
|
|
850
|
+
|
|
851
|
+
multiplicity : int
|
|
852
|
+
Multiplicity of the knots in the knot sequence, we assume that the same
|
|
853
|
+
multiplicity applies to each interior knot.
|
|
854
|
+
"""
|
|
855
|
+
T = knots
|
|
856
|
+
p = degree
|
|
857
|
+
n = len(T)-2*p-2 + multiplicity if periodic else len(T)-p-1
|
|
858
|
+
|
|
859
|
+
# Compute greville abscissas as average of p consecutive knot values
|
|
860
|
+
if p == multiplicity-1:
|
|
861
|
+
for i in range(n):
|
|
862
|
+
# out[i] = sum(T[i:i + p + 2]) / (p + 2)
|
|
863
|
+
out[i] = sum_vec(T[i:i + p + 2]) / (p + 2)
|
|
864
|
+
if abs(out[i]) < 1e-14:
|
|
865
|
+
out[i] = 0.0
|
|
866
|
+
else:
|
|
867
|
+
for i in range(1, 1+n):
|
|
868
|
+
# out[i - 1] = sum(T[i:i + p]) / p
|
|
869
|
+
out[i - 1] = sum_vec(T[i:i + p]) / p
|
|
870
|
+
if abs(out[i - 1]) < 1e-14:
|
|
871
|
+
out[i - 1] = 0.0
|
|
872
|
+
|
|
873
|
+
# Domain boundaries
|
|
874
|
+
a = T[p]
|
|
875
|
+
b = T[len(T) - 1 - p]
|
|
876
|
+
|
|
877
|
+
# If needed apply periodic boundary conditions, then sort array
|
|
878
|
+
if periodic:
|
|
879
|
+
out[:] = (out[:] - a) % (b-a) + a
|
|
880
|
+
merge_sort(out)
|
|
881
|
+
|
|
882
|
+
# Make sure roundoff errors don't push Greville points outside domain
|
|
883
|
+
out[0] = max(out[0], a)
|
|
884
|
+
out[-1] = min(out[-1], b)
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
# =============================================================================
|
|
888
|
+
def elements_spans_p(knots: 'float[:]', degree: int, out: 'int[:]'):
|
|
889
|
+
"""
|
|
890
|
+
Compute the index of the last non-vanishing spline on each grid element
|
|
891
|
+
(cell). The length of the returned array is the number of cells.
|
|
892
|
+
|
|
893
|
+
Parameters
|
|
894
|
+
----------
|
|
895
|
+
knots : array_like
|
|
896
|
+
Knots sequence.
|
|
897
|
+
|
|
898
|
+
degree : int
|
|
899
|
+
Polynomial degree of B-splines.
|
|
900
|
+
|
|
901
|
+
out : array
|
|
902
|
+
The result will be inserted into this array.
|
|
903
|
+
It should be of the appropriate shape and dtype.
|
|
904
|
+
|
|
905
|
+
Returns
|
|
906
|
+
-------
|
|
907
|
+
last_index : int
|
|
908
|
+
Last meaningful index + 1, e.g. the actual interesting result
|
|
909
|
+
is ``out[:last_index]``.
|
|
910
|
+
|
|
911
|
+
Notes
|
|
912
|
+
-----
|
|
913
|
+
1) Numbering of basis functions starts from 0, not 1;
|
|
914
|
+
2) This function could be written in two lines:
|
|
915
|
+
|
|
916
|
+
breaks = breakpoints( knots, degree )
|
|
917
|
+
spans = np.searchsorted( knots, breaks[:-1], side='right' ) - 1
|
|
918
|
+
"""
|
|
919
|
+
temp_array = np.zeros(len(knots))
|
|
920
|
+
|
|
921
|
+
actual_len = breakpoints_p(knots, degree, temp_array)
|
|
922
|
+
|
|
923
|
+
nk = len(knots)
|
|
924
|
+
ne = actual_len - 1
|
|
925
|
+
|
|
926
|
+
ie = 0
|
|
927
|
+
for ik in range(degree, nk-degree):
|
|
928
|
+
if abs(knots[ik + 1] - knots[ik]) >= 1e-15:
|
|
929
|
+
out[ie] = ik
|
|
930
|
+
ie += 1
|
|
931
|
+
if ie == ne:
|
|
932
|
+
break
|
|
933
|
+
|
|
934
|
+
return ne
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
# =============================================================================
|
|
938
|
+
def make_knots_p(breaks: 'float[:]', degree: int, periodic: bool, out: 'float[:]', multiplicity: int = 1):
|
|
939
|
+
"""
|
|
940
|
+
Create spline knots from breakpoints, with appropriate boundary conditions.
|
|
941
|
+
|
|
942
|
+
If domain is periodic, knot sequence is extended by periodicity to have a
|
|
943
|
+
total of (n_cells-1)*mult+2p+2 knots (all break points are repeated mult
|
|
944
|
+
time and we add p+1-mult knots by periodicity at each side).
|
|
945
|
+
|
|
946
|
+
Otherwise, knot sequence is clamped (i.e. endpoints have multiplicity p+1).
|
|
947
|
+
|
|
948
|
+
Parameters
|
|
949
|
+
----------
|
|
950
|
+
breaks : array_like
|
|
951
|
+
Coordinates of breakpoints (= cell edges); given in increasing order and
|
|
952
|
+
with no duplicates.
|
|
953
|
+
|
|
954
|
+
degree : int
|
|
955
|
+
Spline degree (= polynomial degree within each interval).
|
|
956
|
+
|
|
957
|
+
periodic : bool
|
|
958
|
+
True if domain is periodic, False otherwise.
|
|
959
|
+
|
|
960
|
+
multiplicity: int
|
|
961
|
+
Multiplicity of the knots in the knot sequence, we assume that the same
|
|
962
|
+
multiplicity applies to each interior knot.
|
|
963
|
+
|
|
964
|
+
out : array
|
|
965
|
+
The result will be inserted into this array.
|
|
966
|
+
It should be of the appropriate shape and dtype.
|
|
967
|
+
"""
|
|
968
|
+
ncells = len(breaks) - 1
|
|
969
|
+
|
|
970
|
+
for i in range(0, ncells+1):
|
|
971
|
+
out[degree + 1 + (i-1) * multiplicity :degree + 1 + i * multiplicity ] = breaks[i]
|
|
972
|
+
|
|
973
|
+
len_out = len(out)
|
|
974
|
+
|
|
975
|
+
if periodic:
|
|
976
|
+
period = breaks[-1]-breaks[0]
|
|
977
|
+
|
|
978
|
+
out[: degree + 1 - multiplicity] = out[len_out - 2 * (degree + 1 )+ multiplicity: len_out - (degree + 1)] - period
|
|
979
|
+
out[len_out - (degree + 1 - multiplicity) :] = out[degree + 1:2*(degree + 1)- multiplicity] + period
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
#
|
|
983
|
+
else:
|
|
984
|
+
out[0:degree + 1 - multiplicity] = breaks[0]
|
|
985
|
+
out[len_out - degree - 1 + multiplicity:] = breaks[-1]
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
# =============================================================================
|
|
989
|
+
def elevate_knots_p(knots: 'float[:]', degree: int, periodic: bool, out: 'float[:]',
|
|
990
|
+
multiplicity: int = 1,
|
|
991
|
+
tol: float = 1e-15):
|
|
992
|
+
"""
|
|
993
|
+
Given the knot sequence of a spline space S of degree p, compute the knot
|
|
994
|
+
sequence of a spline space S_0 of degree p+1 such that u' is in S for all
|
|
995
|
+
u in S_0.
|
|
996
|
+
|
|
997
|
+
Specifically, on bounded domains the first and last knots are repeated in
|
|
998
|
+
the sequence, and in the periodic case the knot sequence is extended by
|
|
999
|
+
periodicity.
|
|
1000
|
+
|
|
1001
|
+
Parameters
|
|
1002
|
+
----------
|
|
1003
|
+
knots : array_like
|
|
1004
|
+
Knots sequence of spline space of degree p.
|
|
1005
|
+
|
|
1006
|
+
degree : int
|
|
1007
|
+
Spline degree (= polynomial degree within each interval).
|
|
1008
|
+
|
|
1009
|
+
periodic : bool
|
|
1010
|
+
True if domain is periodic, False otherwise.
|
|
1011
|
+
|
|
1012
|
+
multiplicity : int
|
|
1013
|
+
Multiplicity of the knots in the knot sequence, we assume that the same
|
|
1014
|
+
multiplicity applies to each interior knot.
|
|
1015
|
+
|
|
1016
|
+
tol: float
|
|
1017
|
+
If the distance between two knots is less than tol, we assume
|
|
1018
|
+
that they are repeated knots which correspond to the same break point.
|
|
1019
|
+
|
|
1020
|
+
out : array
|
|
1021
|
+
The result will be inserted into this array.
|
|
1022
|
+
It should be of the appropriate shape and dtype.
|
|
1023
|
+
"""
|
|
1024
|
+
if periodic:
|
|
1025
|
+
T = knots
|
|
1026
|
+
p = degree
|
|
1027
|
+
period = T[len(knots) -1 - p] - T[p]
|
|
1028
|
+
left = T[len(knots) -2 - 2 * p + multiplicity-1] - period
|
|
1029
|
+
right = T[2 * p + 2 - multiplicity] + period
|
|
1030
|
+
|
|
1031
|
+
out[0] = left
|
|
1032
|
+
out[-1] = right
|
|
1033
|
+
out[1: - 1] = knots
|
|
1034
|
+
|
|
1035
|
+
else:
|
|
1036
|
+
out[0] = knots[0]
|
|
1037
|
+
out[1:degree + 2] = knots[:degree+1]
|
|
1038
|
+
|
|
1039
|
+
n_out = len(out)
|
|
1040
|
+
n_knots = len(knots)
|
|
1041
|
+
out[n_out - degree - 2] = knots[n_knots - 1]
|
|
1042
|
+
out[n_out - degree - 1:n_out] = knots[n_knots - degree - 1:]
|
|
1043
|
+
|
|
1044
|
+
if len(knots[degree + 1:n_knots - degree - 1]) > 0:
|
|
1045
|
+
out[degree + 2: degree + 2 + multiplicity] = knots[degree + 1]
|
|
1046
|
+
|
|
1047
|
+
unique_index = 0
|
|
1048
|
+
|
|
1049
|
+
for i in range(degree + 1, n_knots - degree - 2, 1):
|
|
1050
|
+
if knots[i + 1] - knots[i] > tol:
|
|
1051
|
+
out[degree + 2 + multiplicity * (unique_index + 1):
|
|
1052
|
+
degree + 2 + multiplicity * (unique_index + 2)] = knots[i + 1]
|
|
1053
|
+
unique_index += 1
|
|
1054
|
+
|
|
1055
|
+
else:
|
|
1056
|
+
out[degree + 2: n_out - degree - 2] = knots[degree + 1:n_knots - degree - 1]
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
# =============================================================================
|
|
1060
|
+
def quadrature_grid_p(breaks: 'float[:]', quad_rule_x: 'float[:]', quad_rule_w: 'float[:]', out1: 'float[:,:]',
|
|
1061
|
+
out2: 'float[:,:]'):
|
|
1062
|
+
"""
|
|
1063
|
+
Compute the quadrature points and weights for performing integrals over
|
|
1064
|
+
each element (interval) of the 1D domain, given a certain Gaussian
|
|
1065
|
+
quadrature rule.
|
|
1066
|
+
|
|
1067
|
+
An n-point Gaussian quadrature rule for the canonical interval :math:`[-1,+1]`
|
|
1068
|
+
and trivial weighting function :math:`\\omega(x)=1` is defined by the n abscissas
|
|
1069
|
+
:math:`x_i` and n weights :math:`w_i` that satisfy the following identity for
|
|
1070
|
+
polynomial functions :math:`f(x)` of degree :math:`2n-1` or less:
|
|
1071
|
+
|
|
1072
|
+
.. math :: \\int_{-1}^{+1} f(x) dx = \\sum_{i=0}^{n-1} w_i f(x_i)
|
|
1073
|
+
|
|
1074
|
+
Parameters
|
|
1075
|
+
----------
|
|
1076
|
+
breaks : array_like of floats
|
|
1077
|
+
Coordinates of spline breakpoints.
|
|
1078
|
+
|
|
1079
|
+
quad_rule_x : array_like of ints
|
|
1080
|
+
Coordinates of quadrature points on canonical interval [-1,1].
|
|
1081
|
+
|
|
1082
|
+
quad_rule_w : array_like of ints
|
|
1083
|
+
Weights assigned to quadrature points on canonical interval [-1,1].
|
|
1084
|
+
|
|
1085
|
+
out1 : array
|
|
1086
|
+
2D output array where the quadrature points will be inserted.
|
|
1087
|
+
It should be of the appropriate shape and dtype.
|
|
1088
|
+
|
|
1089
|
+
out2 : array
|
|
1090
|
+
2D output array where the quadrature weights will be inserted.
|
|
1091
|
+
It should be of the appropriate shape and dtype.
|
|
1092
|
+
|
|
1093
|
+
Notes
|
|
1094
|
+
-----
|
|
1095
|
+
Contents of 2D output arrays 'out1' and 'out2' are accessed with two
|
|
1096
|
+
indices (ie,iq) where:
|
|
1097
|
+
|
|
1098
|
+
- ie is the global element index;
|
|
1099
|
+
- iq is the local index of a quadrature point within the element.
|
|
1100
|
+
|
|
1101
|
+
"""
|
|
1102
|
+
ncells = len(breaks) - 1
|
|
1103
|
+
|
|
1104
|
+
# Compute location and weight of quadrature points from basic rule
|
|
1105
|
+
for ie in range(ncells):
|
|
1106
|
+
a = breaks[ie]
|
|
1107
|
+
b = breaks[ie + 1]
|
|
1108
|
+
|
|
1109
|
+
c0 = 0.5 * (a + b)
|
|
1110
|
+
c1 = 0.5 * (b - a)
|
|
1111
|
+
out1[ie, :] = c1 * quad_rule_x[:] + c0
|
|
1112
|
+
out2[ie, :] = c1 * quad_rule_w[:]
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
# =============================================================================
|
|
1116
|
+
def basis_ders_on_quad_grid_p(knots: 'float[:]', degree: int, quad_grid: 'float[:,:]', nders: int, normalization: bool,
|
|
1117
|
+
offset: int, out: 'float[:,:,:,:]'):
|
|
1118
|
+
"""
|
|
1119
|
+
Evaluate B-Splines and their derivatives on the quadrature grid.
|
|
1120
|
+
|
|
1121
|
+
If called with normalization=True, this uses M-splines instead of B-splines.
|
|
1122
|
+
|
|
1123
|
+
Parameters
|
|
1124
|
+
----------
|
|
1125
|
+
knots : array_like
|
|
1126
|
+
Knots sequence.
|
|
1127
|
+
|
|
1128
|
+
degree : int
|
|
1129
|
+
Polynomial degree of B-splines.
|
|
1130
|
+
|
|
1131
|
+
quad_grid: ndarray
|
|
1132
|
+
2D array of shape (ne, nq). Coordinates of quadrature points of
|
|
1133
|
+
each element in 1D domain, which can be given by quadrature_grid()
|
|
1134
|
+
or chosen arbitrarily.
|
|
1135
|
+
|
|
1136
|
+
nders : int
|
|
1137
|
+
Maximum derivative of interest.
|
|
1138
|
+
|
|
1139
|
+
normalization : bool
|
|
1140
|
+
Set to False for B-splines, and True for M-splines.
|
|
1141
|
+
|
|
1142
|
+
offset : int, default=0
|
|
1143
|
+
Assumes that the quadrature grid starts from cell number offset.
|
|
1144
|
+
|
|
1145
|
+
out : array
|
|
1146
|
+
The result will be inserted into this array.
|
|
1147
|
+
It should be of the appropriate shape and dtype.
|
|
1148
|
+
|
|
1149
|
+
Notes
|
|
1150
|
+
-----
|
|
1151
|
+
4D output array 'out' contains values of B-Splines and their derivatives
|
|
1152
|
+
at quadrature points in each element of 1D domain. Indices are
|
|
1153
|
+
. ie: global element (0 <= ie < ne )
|
|
1154
|
+
. il: local basis function (0 <= il <= degree)
|
|
1155
|
+
. id: derivative (0 <= id <= nders )
|
|
1156
|
+
. iq: local quadrature point (0 <= iq < nq )
|
|
1157
|
+
"""
|
|
1158
|
+
ne = quad_grid.shape[0]
|
|
1159
|
+
nq = quad_grid.shape[1]
|
|
1160
|
+
if normalization:
|
|
1161
|
+
integrals = np.zeros(knots.shape[0] - degree - 1)
|
|
1162
|
+
basis_integrals_p(knots, degree, integrals)
|
|
1163
|
+
scaling = 1.0 /integrals
|
|
1164
|
+
|
|
1165
|
+
temp_spans = np.zeros(len(knots), dtype=int)
|
|
1166
|
+
actual_index = elements_spans_p(knots, degree, temp_spans)
|
|
1167
|
+
spans = temp_spans[:actual_index]
|
|
1168
|
+
|
|
1169
|
+
ders = np.zeros((nders + 1, degree + 1))
|
|
1170
|
+
|
|
1171
|
+
for ie in range(ne):
|
|
1172
|
+
xx = quad_grid[ie, :]
|
|
1173
|
+
span = spans[ie + offset]
|
|
1174
|
+
for iq, xq in enumerate(xx):
|
|
1175
|
+
basis_funs_all_ders_p(knots, degree, xq, span, nders, False, ders)
|
|
1176
|
+
if normalization:
|
|
1177
|
+
ders *= scaling[span - degree:span + 1]
|
|
1178
|
+
for k in range(degree + 1):
|
|
1179
|
+
out[ie, k, :, iq] = ders[:, k]
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
def cell_index_p(breaks: 'float[:]', i_grid: 'float[:]', tol: float, out: 'int[:]'):
|
|
1183
|
+
"""
|
|
1184
|
+
Computes in which cells a given sorted array of locations belong.
|
|
1185
|
+
|
|
1186
|
+
Locations close to a interior breakpoint will be assumed to be
|
|
1187
|
+
present twice in the grid, once of for each cell. Boundary breakpoints are
|
|
1188
|
+
snapped to the interior of the domain.
|
|
1189
|
+
|
|
1190
|
+
Parameters
|
|
1191
|
+
----------
|
|
1192
|
+
breaks : array_like
|
|
1193
|
+
Coordinates of breakpoints (= cell edges); given in increasing order and
|
|
1194
|
+
with no duplicates.
|
|
1195
|
+
|
|
1196
|
+
i_grid : ndarray
|
|
1197
|
+
1D array of locations.
|
|
1198
|
+
|
|
1199
|
+
tol : float
|
|
1200
|
+
If the distance between a given point in ``i_grid`` and
|
|
1201
|
+
a breakpoint is less than ``tol`` then it is considered
|
|
1202
|
+
to be the breakpoint.
|
|
1203
|
+
|
|
1204
|
+
out : array
|
|
1205
|
+
The result will be inserted into this array.
|
|
1206
|
+
It should be of the appropriate shape and dtype.
|
|
1207
|
+
|
|
1208
|
+
Returns
|
|
1209
|
+
-------
|
|
1210
|
+
status : int
|
|
1211
|
+
0 if everything worked as intended, 1 if not.
|
|
1212
|
+
|
|
1213
|
+
"""
|
|
1214
|
+
nx = len(i_grid)
|
|
1215
|
+
|
|
1216
|
+
nbk = len(breaks)
|
|
1217
|
+
|
|
1218
|
+
# Check if there are points outside the domain
|
|
1219
|
+
if np.min(i_grid) < breaks[0] - tol/2: return -1
|
|
1220
|
+
if np.max(i_grid) > breaks[nbk - 1] + tol/2: return -1
|
|
1221
|
+
|
|
1222
|
+
current_index = 0
|
|
1223
|
+
while current_index < nx:
|
|
1224
|
+
x = i_grid[current_index]
|
|
1225
|
+
|
|
1226
|
+
# Binary search
|
|
1227
|
+
low, high = 0, nbk - 1
|
|
1228
|
+
i_cell = (low + high)//2
|
|
1229
|
+
it = 0
|
|
1230
|
+
max_it = 2*(high-low) # this number of iterations should not be reached
|
|
1231
|
+
while it < max_it and (x < breaks[i_cell] - tol or x >= breaks[i_cell + 1] + tol):
|
|
1232
|
+
if x < breaks[i_cell]:
|
|
1233
|
+
high = i_cell
|
|
1234
|
+
else:
|
|
1235
|
+
low = i_cell
|
|
1236
|
+
i_cell = (low + high)//2
|
|
1237
|
+
it += 1
|
|
1238
|
+
if it >= max_it:
|
|
1239
|
+
return -2
|
|
1240
|
+
|
|
1241
|
+
# Check were we landed with the binary search
|
|
1242
|
+
# Case 1: x is the left breakpoint
|
|
1243
|
+
if abs(x - breaks[i_cell]) < tol:
|
|
1244
|
+
# Check if x is the left boundary
|
|
1245
|
+
if i_cell == 0:
|
|
1246
|
+
out[current_index] = 0
|
|
1247
|
+
current_index += 1
|
|
1248
|
+
else:
|
|
1249
|
+
out[current_index] = i_cell - 1
|
|
1250
|
+
current_index +=1
|
|
1251
|
+
# Check if x is the last point
|
|
1252
|
+
if current_index == nx:
|
|
1253
|
+
break
|
|
1254
|
+
# Check if the next point is also the left breakpoint
|
|
1255
|
+
# if yes put it in the next cell and move up one more point
|
|
1256
|
+
# in i_grid
|
|
1257
|
+
if abs(i_grid[current_index] - breaks[i_cell]) < tol:
|
|
1258
|
+
out[current_index] = i_cell
|
|
1259
|
+
current_index += 1
|
|
1260
|
+
|
|
1261
|
+
# Case 2: x is the right breakpoint
|
|
1262
|
+
elif abs(x - breaks[i_cell + 1]) < tol:
|
|
1263
|
+
# Check if x is the right boundary
|
|
1264
|
+
if i_cell + 1 == nbk - 1:
|
|
1265
|
+
out[current_index] = (nbk - 1) - 1
|
|
1266
|
+
current_index +=1
|
|
1267
|
+
else:
|
|
1268
|
+
out[current_index] = i_cell
|
|
1269
|
+
current_index += 1
|
|
1270
|
+
# Check if x is the last point
|
|
1271
|
+
if current_index == nx:
|
|
1272
|
+
break
|
|
1273
|
+
# Check if the next point is also the right breakpoint
|
|
1274
|
+
# if yes put it in the next cell and move up one more point
|
|
1275
|
+
# in i_grid
|
|
1276
|
+
if abs(i_grid[current_index] - breaks[i_cell + 1]) < tol:
|
|
1277
|
+
out[current_index] = i_cell + 1
|
|
1278
|
+
current_index +=1
|
|
1279
|
+
# Case 3: x is in the cell
|
|
1280
|
+
else:
|
|
1281
|
+
out[current_index] = i_cell
|
|
1282
|
+
current_index += 1
|
|
1283
|
+
return 0
|
|
1284
|
+
|
|
1285
|
+
|
|
1286
|
+
def basis_ders_on_irregular_grid_p(knots: 'float[:]', degree: int,
|
|
1287
|
+
i_grid: 'float[:]', cell_index: 'int[:]',
|
|
1288
|
+
nders: int, normalization: bool,
|
|
1289
|
+
out: 'float[:,:,:]'):
|
|
1290
|
+
"""
|
|
1291
|
+
Evaluate B-Splines and their derivatives on an irregular_grid.
|
|
1292
|
+
|
|
1293
|
+
If called with normalization=True, this uses M-splines instead of B-splines.
|
|
1294
|
+
|
|
1295
|
+
Parameters
|
|
1296
|
+
----------
|
|
1297
|
+
knots : array_like
|
|
1298
|
+
Knots sequence.
|
|
1299
|
+
|
|
1300
|
+
degree : int
|
|
1301
|
+
Polynomial degree of B-splines.
|
|
1302
|
+
|
|
1303
|
+
i_grid : ndarray
|
|
1304
|
+
1D array of all of the points on which to evaluate the
|
|
1305
|
+
basis functions. The points do not need to be sorted
|
|
1306
|
+
|
|
1307
|
+
cell_index : ndarray
|
|
1308
|
+
1D array of the same shape as ``i_grid``.
|
|
1309
|
+
``cell_index[i]`` is the index of the cell in which
|
|
1310
|
+
``i_grid[i]`` belong.
|
|
1311
|
+
|
|
1312
|
+
nders : int
|
|
1313
|
+
Maximum derivative of interest.
|
|
1314
|
+
|
|
1315
|
+
normalization : bool
|
|
1316
|
+
Set to False for B-splines, and True for M-splines.
|
|
1317
|
+
|
|
1318
|
+
out : array
|
|
1319
|
+
The result will be inserted into this array.
|
|
1320
|
+
It should be of the appropriate shape and dtype.
|
|
1321
|
+
|
|
1322
|
+
Notes
|
|
1323
|
+
-----
|
|
1324
|
+
3D output array 'out' contains values of B-Splines and their derivatives
|
|
1325
|
+
at quadrature points in each element of 1D domain. Indices are
|
|
1326
|
+
. ie: location (0 <= ie < nx )
|
|
1327
|
+
. il: local basis function (0 <= il <= degree)
|
|
1328
|
+
. id: derivative (0 <= id <= nders )
|
|
1329
|
+
"""
|
|
1330
|
+
nx = i_grid.shape[0]
|
|
1331
|
+
if normalization:
|
|
1332
|
+
scaling = np.zeros(knots.shape[0] - degree - 1)
|
|
1333
|
+
basis_integrals_p(knots, degree, scaling)
|
|
1334
|
+
scaling = 1.0 / scaling
|
|
1335
|
+
|
|
1336
|
+
ders = np.zeros((nders + 1, degree + 1))
|
|
1337
|
+
|
|
1338
|
+
temp_spans = np.zeros(len(knots), dtype=int)
|
|
1339
|
+
actual_index = elements_spans_p(knots, degree, temp_spans)
|
|
1340
|
+
spans = temp_spans[:actual_index]
|
|
1341
|
+
|
|
1342
|
+
for ie in range(nx):
|
|
1343
|
+
xx = i_grid[ie]
|
|
1344
|
+
span = spans[cell_index[ie]]
|
|
1345
|
+
basis_funs_all_ders_p(knots, degree, xx, span, nders, False, ders)
|
|
1346
|
+
if normalization:
|
|
1347
|
+
ders *= scaling[span - degree:span + 1]
|
|
1348
|
+
for k in range(degree + 1):
|
|
1349
|
+
out[ie, k, :] = ders[:, k]
|