Coverage for pySDC/helpers/transfer_helper.py: 100%
142 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 12:30 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-02 12:30 +0000
1# coding=utf-8
2import numpy as np
3import scipy.sparse as sprs
4from scipy.interpolate import BarycentricInterpolator
7def next_neighbors_periodic(p, ps, k):
8 """
9 Function to find the next neighbors for a periodic setup
11 This function gives for a value p the k points next to it which are found in
12 in the vector ps and the points which are found periodically.
14 Note that the period is hardcoded to one, here and in the periodic branches of the two matrix
15 constructors below. Normalise the grid before you pass it in; the interpolation weights only
16 depend on ratios of distances, so nothing is lost by doing so.
18 Args:
19 p: the current point
20 ps (np.ndarray): the grid with the potential neighbors
21 k (int): number of neighbors to find
23 Returns:
24 list: the k next neighbors
25 """
26 p_bar = p - np.floor(p / 1.0) * 1.0
27 ps = ps - ps[0]
28 distance_to_p = np.asarray(
29 list(map(lambda tk: min([np.abs(tk + 1 - p_bar), np.abs(tk - p_bar), np.abs(tk - 1 - p_bar)]), ps))
30 )
32 # zip it
33 value_index = []
34 for d, i in zip(distance_to_p, range(distance_to_p.size), strict=True):
35 value_index.append((d, i))
36 # sort by distance
37 value_index_sorted = sorted(value_index, key=lambda s: s[0])
38 # take first k indices with least distance and sort them
39 return sorted(map(lambda s: s[1], value_index_sorted[0:k]))
42def next_neighbors(p, ps, k):
43 """
44 Function to find the next neighbors for a non-periodic setup
46 This function gives for a value p the k points next to it which are found in
47 in the vector ps
49 Args:
50 p: the current point
51 ps (np.ndarray): the grid with the potential neighbors
52 k (int): number of neighbors to find
54 Returns:
55 list: the k next neighbors
56 """
57 distance_to_p = np.abs(ps - p)
58 # zip it
59 value_index = []
60 for d, i in zip(distance_to_p, range(distance_to_p.size), strict=True):
61 value_index.append((d, i))
62 # sort by distance
63 value_index_sorted = sorted(value_index, key=lambda s: s[0])
64 # take first k indices with least distance and sort them
65 return sorted(map(lambda s: s[1], value_index_sorted[0:k]))
68def continue_periodic_array(arr, nn):
69 """
70 Function to append an array for nn neighbors for periodicity
72 Args:
73 arr (np.ndarray): the input array
74 nn (list): the neighbors
76 Returns:
77 np.ndarray: the continued array
78 """
80 nn = np.asarray(nn)
81 d_nn = nn[1:] - nn[:-1]
82 if np.all(d_nn == np.ones(nn.shape[0] - 1)):
83 return arr[nn]
84 else:
85 cont_arr = [arr[nn[0]]]
86 shift = 0.0
87 for n, d in zip(nn[1:], d_nn, strict=True):
88 if d != 1:
89 shift = -1
90 cont_arr.append(arr[n] + shift)
92 return np.asarray(cont_arr)
95def restriction_matrix_1d(fine_grid, coarse_grid, k=2, periodic=False, pad=1):
96 """
97 Function to contruct the restriction matrix in 1d using barycentric interpolation
99 Args:
100 fine_grid (np.ndarray): a one dimensional 1d array containing the nodes of the fine grid
101 coarse_grid (np.ndarray): a one dimensional 1d array containing the nodes of the coarse grid
102 k (int): order of the restriction
103 periodic (bool): flag to indicate periodicity, which assumes a grid of period one
104 pad (int): padding parameter for boundaries
106 Returns:
107 sprs.csc_matrix: restriction matrix
108 """
110 n_g = coarse_grid.size
112 if periodic:
113 M = np.zeros((coarse_grid.size, fine_grid.size))
114 for i, p in zip(range(n_g), coarse_grid, strict=True):
115 nn = next_neighbors_periodic(p, fine_grid, k)
116 circulating_one = np.asarray([1.0] + [0.0] * (k - 1))
117 cont_arr = continue_periodic_array(fine_grid, nn)
118 if p > np.mean(coarse_grid) and not (cont_arr[0] <= p <= cont_arr[-1]):
119 cont_arr += 1
120 bary_pol = []
121 for l in range(k):
122 bary_pol.append(BarycentricInterpolator(cont_arr, np.roll(circulating_one, l)))
123 with np.errstate(divide='ignore'):
124 M[i, nn] = np.asarray(list(map(lambda x: x(p), bary_pol)))
125 else:
126 M = np.zeros((coarse_grid.size, fine_grid.size + 2 * pad))
127 for i, p in zip(range(n_g), coarse_grid, strict=True):
128 padded_f_grid = border_padding(fine_grid, pad, pad)
129 nn = next_neighbors(p, padded_f_grid, k)
130 # construct the lagrange polynomials for the k neighbors
131 circulating_one = np.asarray([1.0] + [0.0] * (k - 1))
132 bary_pol = []
133 for l in range(k):
134 bary_pol.append(BarycentricInterpolator(padded_f_grid[nn], np.roll(circulating_one, l)))
135 with np.errstate(divide='ignore'):
136 M[i, nn] = np.asarray(list(map(lambda x: x(p), bary_pol)))
137 if pad > 0:
138 M = M[:, pad:-pad]
140 return sprs.csc_matrix(M)
143def interpolation_matrix_1d(fine_grid, coarse_grid, k=2, periodic=False, pad=1, equidist_nested=True):
144 """
145 Function to construct the restriction matrix in 1d using barycentric interpolation
147 Args:
148 fine_grid (np.ndarray): a one dimensional 1d array containing the nodes of the fine grid
149 coarse_grid (np.ndarray): a one dimensional 1d array containing the nodes of the coarse grid
150 k (int): order of the restriction
151 periodic (bool): flag to indicate periodicity, which assumes a grid of period one
152 pad (int): padding parameter for boundaries
153 equidist_nested (bool): shortcut possible, if nodes are equidistant and nested
155 Returns:
156 sprs.csc_matrix: interpolation matrix
157 """
159 n_f = fine_grid.size
161 if periodic:
162 M = np.zeros((fine_grid.size, coarse_grid.size))
164 if equidist_nested:
165 for i, p in zip(range(n_f), fine_grid, strict=True):
166 if i % 2 == 0:
167 M[i, int(i / 2)] = 1.0
168 else:
169 nn = []
170 cpos = int(i / 2)
171 offset = int(k / 2)
172 for j in range(k):
173 nn.append(cpos - offset + 1 + j)
174 if nn[-1] < 0:
175 nn[-1] += coarse_grid.size
176 elif nn[-1] > coarse_grid.size - 1:
177 nn[-1] -= coarse_grid.size
178 nn = sorted(nn)
180 circulating_one = np.asarray([1.0] + [0.0] * (k - 1))
181 if len(nn) > 0:
182 cont_arr = continue_periodic_array(coarse_grid, nn)
183 else:
184 cont_arr = coarse_grid
186 if p > np.mean(fine_grid) and not (cont_arr[0] <= p <= cont_arr[-1]):
187 cont_arr += 1
189 bary_pol = []
190 for l in range(k):
191 bary_pol.append(BarycentricInterpolator(cont_arr, np.roll(circulating_one, l)))
192 with np.errstate(divide='ignore'):
193 M[i, nn] = np.asarray(list(map(lambda x: x(p), bary_pol)))
195 else:
196 for i, p in zip(range(n_f), fine_grid, strict=True):
197 nn = next_neighbors_periodic(p, coarse_grid, k)
198 circulating_one = np.asarray([1.0] + [0.0] * (k - 1))
199 cont_arr = continue_periodic_array(coarse_grid, nn)
201 if p > np.mean(fine_grid) and not (cont_arr[0] <= p <= cont_arr[-1]):
202 cont_arr += 1
204 bary_pol = []
205 for l in range(k):
206 bary_pol.append(BarycentricInterpolator(cont_arr, np.roll(circulating_one, l)))
207 with np.errstate(divide='ignore'):
208 M[i, nn] = np.asarray(list(map(lambda x: x(p), bary_pol)))
210 else:
211 M = np.zeros((fine_grid.size, coarse_grid.size + 2 * pad))
212 padded_c_grid = border_padding(coarse_grid, pad, pad)
214 if equidist_nested:
215 for i, p in zip(range(n_f), fine_grid, strict=True):
216 if i % 2 != 0:
217 M[i, int((i - 1) / 2) + 1] = 1.0
218 else:
219 nn = []
220 cpos = int(i / 2)
221 offset = int(k / 2)
222 for j in range(k):
223 nn.append(cpos - offset + 1 + j)
224 if nn[-1] < 0:
225 nn[-1] += k
226 elif nn[-1] > coarse_grid.size + 1:
227 nn[-1] -= k
228 nn = sorted(nn)
229 # construct the lagrange polynomials for the k neighbors
230 circulating_one = np.asarray([1.0] + [0.0] * (k - 1))
231 bary_pol = []
232 for l in range(k):
233 bary_pol.append(BarycentricInterpolator(padded_c_grid[nn], np.roll(circulating_one, l)))
234 with np.errstate(divide='ignore'):
235 M[i, nn] = np.asarray(list(map(lambda x: x(p), bary_pol)))
237 else:
238 for i, p in zip(range(n_f), fine_grid, strict=True):
239 nn = next_neighbors(p, padded_c_grid, k)
240 # construct the lagrange polynomials for the k neighbors
241 circulating_one = np.asarray([1.0] + [0.0] * (k - 1))
242 bary_pol = []
243 for l in range(k):
244 bary_pol.append(BarycentricInterpolator(padded_c_grid[nn], np.roll(circulating_one, l)))
245 with np.errstate(divide='ignore'):
246 M[i, nn] = np.asarray(list(map(lambda x: x(p), bary_pol)))
248 if pad > 0:
249 M = M[:, pad:-pad]
251 return sprs.csc_matrix(M)
254def border_padding(grid, l, r, pad_type='mirror'):
255 """
256 Function to pad/embed an array at the boundaries
258 Args:
259 grid (np.npdarray): the input array
260 l: left boundary
261 r: right boundary
262 pad_type: type of padding
264 Returns:
265 np.npdarray: the padded array
267 """
269 assert l < grid.size and r < grid.size
270 padded_arr = np.zeros(grid.size + l + r)
271 if pad_type == 'mirror':
272 for i in range(l):
273 padded_arr[i] = 2 * grid[0] - grid[l - i]
274 for j in range(r):
275 padded_arr[-j - 1] = 2 * grid[-1] - grid[-r + j - 1]
276 padded_arr[l : l + grid.size] = grid
277 return padded_arr