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.
Files changed (98) hide show
  1. feectools/__init__.py +0 -0
  2. feectools/accelerate/__init__.py +0 -0
  3. feectools/accelerate/accelerate.py +220 -0
  4. feectools/accelerate/compile_psydac.mk +52 -0
  5. feectools/api/__init__.py +0 -0
  6. feectools/api/essential_bc.py +122 -0
  7. feectools/api/fem_bilinear_form.py +2226 -0
  8. feectools/api/fem_common.py +286 -0
  9. feectools/api/fem_sum_form.py +123 -0
  10. feectools/api/settings.py +82 -0
  11. feectools/core/__init__.py +11 -0
  12. feectools/core/bsplines.py +1107 -0
  13. feectools/core/bsplines_kernels.py +1349 -0
  14. feectools/core/field_evaluation_kernels.py +5015 -0
  15. feectools/core/tests/__init__.py +0 -0
  16. feectools/core/tests/test_bsplines.py +263 -0
  17. feectools/core/tests/test_bsplines_kernel.py +40 -0
  18. feectools/core/tests/test_bsplines_pyccel.py +752 -0
  19. feectools/ddm/__init__.py +3 -0
  20. feectools/ddm/basic.py +78 -0
  21. feectools/ddm/blocking_data_exchanger.py +348 -0
  22. feectools/ddm/cart.py +1835 -0
  23. feectools/ddm/interface_data_exchanger.py +122 -0
  24. feectools/ddm/mpi.py +109 -0
  25. feectools/ddm/nonblocking_data_exchanger.py +331 -0
  26. feectools/ddm/partition.py +207 -0
  27. feectools/ddm/petsc.py +112 -0
  28. feectools/ddm/tests/__init__.py +0 -0
  29. feectools/ddm/tests/test_cart_1d.py +138 -0
  30. feectools/ddm/tests/test_cart_2d.py +164 -0
  31. feectools/ddm/tests/test_cart_3d.py +158 -0
  32. feectools/ddm/tests/test_multicart_2d.py +173 -0
  33. feectools/ddm/tests/test_partition.py +124 -0
  34. feectools/ddm/utilities.py +24 -0
  35. feectools/feec/__init__.py +0 -0
  36. feectools/feec/derivatives.py +780 -0
  37. feectools/feec/dof_kernels.py +210 -0
  38. feectools/feec/global_geometric_projectors.py +1073 -0
  39. feectools/feec/hodge.py +148 -0
  40. feectools/fem/__init__.py +0 -0
  41. feectools/fem/basic.py +465 -0
  42. feectools/fem/grid.py +181 -0
  43. feectools/fem/partitioning.py +344 -0
  44. feectools/fem/projectors.py +160 -0
  45. feectools/fem/splines.py +559 -0
  46. feectools/fem/tensor.py +1393 -0
  47. feectools/fem/tests/__init__.py +0 -0
  48. feectools/fem/tests/analytical_profiles_1d.py +100 -0
  49. feectools/fem/tests/analytical_profiles_base.py +34 -0
  50. feectools/fem/tests/splines_error_bounds.py +155 -0
  51. feectools/fem/tests/test_spline_histopolation.py +120 -0
  52. feectools/fem/tests/test_spline_interpolation.py +182 -0
  53. feectools/fem/tests/test_splines.py +184 -0
  54. feectools/fem/tests/test_splines_par.py +46 -0
  55. feectools/fem/tests/test_vector_spaces.py +150 -0
  56. feectools/fem/tests/utilities.py +47 -0
  57. feectools/fem/vector.py +729 -0
  58. feectools/linalg/__init__.py +0 -0
  59. feectools/linalg/basic.py +1386 -0
  60. feectools/linalg/block.py +1451 -0
  61. feectools/linalg/direct_solvers.py +201 -0
  62. feectools/linalg/fft.py +258 -0
  63. feectools/linalg/kernels/__init__.py +0 -0
  64. feectools/linalg/kernels/axpy_kernels.py +57 -0
  65. feectools/linalg/kernels/inner_kernels.py +100 -0
  66. feectools/linalg/kernels/matvec_kernels.py +206 -0
  67. feectools/linalg/kernels/stencil2IJV_kernels.py +227 -0
  68. feectools/linalg/kernels/stencil2coo_kernels.py +179 -0
  69. feectools/linalg/kernels/transpose_kernels.py +263 -0
  70. feectools/linalg/kron.py +911 -0
  71. feectools/linalg/solvers.py +1914 -0
  72. feectools/linalg/sparse.py +114 -0
  73. feectools/linalg/stencil.py +2923 -0
  74. feectools/linalg/stencil_dot_kernels.py +317 -0
  75. feectools/linalg/stencil_transpose_kernels.py +372 -0
  76. feectools/linalg/tests/__init__.py +0 -0
  77. feectools/linalg/tests/test_block.py +1588 -0
  78. feectools/linalg/tests/test_fft.py +106 -0
  79. feectools/linalg/tests/test_kron_stencil_matrix.py +114 -0
  80. feectools/linalg/tests/test_linalg.py +1065 -0
  81. feectools/linalg/tests/test_matrix_free.py +128 -0
  82. feectools/linalg/tests/test_solvers.py +213 -0
  83. feectools/linalg/tests/test_stencil_interface_matrix.py +379 -0
  84. feectools/linalg/tests/test_stencil_vector.py +1036 -0
  85. feectools/linalg/tests/test_stencil_vector_space.py +440 -0
  86. feectools/linalg/topetsc.py +522 -0
  87. feectools/linalg/utilities.py +200 -0
  88. feectools/utilities/__init__.py +0 -0
  89. feectools/utilities/quadratures.py +113 -0
  90. feectools/utilities/utils.py +166 -0
  91. feectools/version.py +1 -0
  92. feectools-0.1.0.dist-info/METADATA +66 -0
  93. feectools-0.1.0.dist-info/RECORD +98 -0
  94. feectools-0.1.0.dist-info/WHEEL +5 -0
  95. feectools-0.1.0.dist-info/entry_points.txt +3 -0
  96. feectools-0.1.0.dist-info/licenses/AUTHORS +22 -0
  97. feectools-0.1.0.dist-info/licenses/LICENSE +21 -0
  98. feectools-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,379 @@
1
+ # -*- coding: UTF-8 -*-
2
+
3
+ import pytest
4
+ import numpy as np
5
+ from random import random
6
+
7
+ from feectools.linalg.stencil import StencilVectorSpace, StencilVector, StencilMatrix, StencilInterfaceMatrix
8
+ from feectools.api.settings import *
9
+ from feectools.ddm.cart import DomainDecomposition, CartDecomposition
10
+
11
+ #===============================================================================
12
+ def compute_global_starts_ends(domain_decomposition, npts, pads):
13
+ ndims = len(npts)
14
+ global_starts = [None]*ndims
15
+ global_ends = [None]*ndims
16
+
17
+ for axis in range(ndims):
18
+ ee = domain_decomposition.global_element_ends [axis]
19
+
20
+ global_ends [axis] = ee.copy()
21
+ global_ends [axis][-1] = npts[axis]-1
22
+ global_starts[axis] = np.array([0] + (global_ends[axis][:-1]+1).tolist())
23
+
24
+ for s, e, p in zip(global_starts, global_ends, pads):
25
+ assert all(e - s + 1 >= p)
26
+
27
+ return tuple(global_starts), tuple(global_ends)
28
+
29
+ #===============================================================================
30
+ def get_minus_starts_ends(plus_starts, plus_ends, minus_npts, plus_npts, minus_axis, plus_axis,
31
+ minus_ext, plus_ext, minus_pads, plus_pads, minus_shifts, plus_shifts,
32
+ diff):
33
+ """
34
+ Compute the coefficients needed by the minus patch in a given interface.
35
+ """
36
+ starts = [max(0,s-m*p) for s,m,p in zip(plus_starts, minus_shifts, minus_pads)]
37
+ ends = [min(n,e+m*p) for e,n,m,p in zip(plus_ends, minus_npts, minus_shifts, minus_pads)]
38
+ starts[minus_axis] = 0 if minus_ext == -1 else ends[minus_axis]-minus_pads[minus_axis]
39
+ ends[minus_axis] = ends[minus_axis] if minus_ext == 1 else minus_pads[minus_axis]
40
+ return starts, ends
41
+
42
+ #------------------------------------------------------------------------------
43
+ def get_plus_starts_ends(minus_starts, minus_ends, minus_npts, plus_npts, minus_axis, plus_axis,
44
+ minus_ext, plus_ext, minus_pads, plus_pads, minus_shifts, plus_shifts,
45
+ diff):
46
+ """
47
+ Compute the coefficients needed by the plus patch in a given interface.
48
+ """
49
+ starts = [max(0,s-m*p) for s,m,p in zip(minus_starts, plus_shifts, plus_pads)]
50
+ ends = [min(n,e+m*p) for e,n,m,p in zip(minus_ends, plus_npts, plus_shifts, plus_pads)]
51
+ starts[plus_axis] = 0 if plus_ext == -1 else ends[plus_axis]-plus_pads[plus_axis]
52
+ ends[plus_axis] = ends[plus_axis] if plus_ext == 1 else plus_pads[plus_axis]
53
+ return starts, ends
54
+
55
+ # TODO : Add test about dot, div, transpose, toarray, tosparse, copy, basic operation, max, exchange_assembly_data, set_backend
56
+ # ===============================================================================
57
+ # SERIAL TESTS
58
+ # ===============================================================================
59
+
60
+ @pytest.mark.parametrize('dtype', [float, complex])
61
+ @pytest.mark.parametrize('axis', [0])
62
+ @pytest.mark.parametrize('ext', [-1, 1])
63
+ @pytest.mark.parametrize('n1', [7, 15])
64
+ @pytest.mark.parametrize('p1', [2, 4])
65
+ @pytest.mark.parametrize('s1', [1, 2])
66
+ def test_stencil_interface_matrix_1d_serial_init(dtype, n1, p1, s1, axis, ext, P1=True):
67
+ # Create domain decomposition
68
+ D = DomainDecomposition([n1], periods=[P1])
69
+
70
+ # Partition the points
71
+ npts = [n1]
72
+ global_starts, global_ends = compute_global_starts_ends(D, npts, [p1])
73
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1], shifts=[s1])
74
+
75
+ # Create a vector space V and a matrix M from V to V
76
+ V = StencilVectorSpace(cart, dtype=dtype)
77
+ V.set_interface(axis, ext, cart)
78
+ W = StencilVectorSpace(cart, dtype=dtype)
79
+ W.set_interface(axis, -ext, cart)
80
+ M = StencilInterfaceMatrix(V, W, global_starts, global_starts, axis, axis, ext, -ext)
81
+
82
+ # Check properties of this matrix
83
+ assert M.domain == V
84
+ assert M.codomain == W
85
+ assert M.dtype == dtype
86
+ assert M.domain_axis == axis
87
+ assert M.codomain_axis == axis
88
+ assert M.domain_ext == ext
89
+ assert M.codomain_ext == -ext
90
+ assert M.dim == 1
91
+ assert M.domain_start == (0,) * M.dim
92
+ assert M.codomain_start == (0,) * M.dim
93
+ assert M.flip == (1,) * M.dim
94
+ assert np.array_equal(M.permutation, [0])
95
+ assert M.pads == (p1,)
96
+ assert M.backend == None
97
+ assert M._data.shape == (p1 + 1 + 2 * p1 * s1, 1 + 2 * p1)
98
+ assert M.shape == (n1, n1)
99
+
100
+ # ===============================================================================
101
+
102
+ @pytest.mark.parametrize('dtype', [float, complex])
103
+ @pytest.mark.parametrize('axis1', [0, 1])
104
+ @pytest.mark.parametrize('axis2', [0, 1])
105
+ @pytest.mark.parametrize('ext1', [-1, 1])
106
+ @pytest.mark.parametrize('ext2', [-1, 1])
107
+ @pytest.mark.parametrize('n1', [7, 15])
108
+ @pytest.mark.parametrize('n2', [7, 15])
109
+ @pytest.mark.parametrize('p1', [2, 3])
110
+ @pytest.mark.parametrize('p2', [2, 3])
111
+ @pytest.mark.parametrize('s1', [1, 2])
112
+ @pytest.mark.parametrize('s2', [1, 2])
113
+ def test_stencil_interface_matrix_2d_serial_init(dtype, n1, n2, p1, p2, s1, s2, axis1, axis2, ext1, ext2, P1=True,
114
+ P2=True):
115
+ # Create domain decomposition
116
+ D = DomainDecomposition([n1, n2], periods=[P1, P2])
117
+
118
+ # Partition the points
119
+ npts = [n1, n2]
120
+ global_starts, global_ends = compute_global_starts_ends(D, npts, [p1, p2])
121
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1, p2], shifts=[s1, s2])
122
+
123
+ # Create a vector space V and a matrix M from V to V
124
+ V = StencilVectorSpace(cart, dtype=dtype)
125
+ V.set_interface(axis1, ext1, cart)
126
+ W = StencilVectorSpace(cart, dtype=dtype)
127
+ W.set_interface(axis2, ext2, cart)
128
+ M = StencilInterfaceMatrix(V, W, global_starts, global_starts, axis1, axis2, ext1, ext2)
129
+
130
+ # Check properties of this matrix
131
+ assert M.domain == V
132
+ assert M.codomain == W
133
+ assert M.dtype == dtype
134
+ assert M.domain_axis == axis1
135
+ assert M.codomain_axis == axis2
136
+ assert M.domain_ext == ext1
137
+ assert M.codomain_ext == ext2
138
+ assert M.dim == 2
139
+ assert M.domain_start == (0,) * M.dim
140
+ assert M.codomain_start == (0,) * M.dim
141
+ assert M.flip == (1,) * M.dim
142
+ assert M.pads == (p1, p2)
143
+ assert M.backend == None
144
+ if axis2 == 0:
145
+ assert M._data.shape == (p1 + 1 + 2 * p1 * s1, n2 + 2 * p2 * s2, 1 + 2 * p1, 1 + 2 * p2)
146
+ elif axis2 == 1:
147
+ assert M._data.shape == (n1 + 2 * p1 * s1, p2 + 1 + 2 * p2 * s2, 1 + 2 * p1, 1 + 2 * p2)
148
+ if axis1 == axis2:
149
+ assert np.array_equal(M.permutation, [0, 1])
150
+ else:
151
+ assert np.array_equal(M.permutation, [1, 0])
152
+ assert M.shape == (n1 * n2, n1 * n2)
153
+
154
+ # ===============================================================================
155
+
156
+ @pytest.mark.parametrize('dtype', [float, complex])
157
+ @pytest.mark.parametrize('axis1', [0, 1, 2])
158
+ @pytest.mark.parametrize('axis2', [0, 1, 2])
159
+ @pytest.mark.parametrize('ext1', [-1, 1])
160
+ @pytest.mark.parametrize('ext2', [-1, 1])
161
+ @pytest.mark.parametrize('n1', [7, 15])
162
+ @pytest.mark.parametrize('n2', [7, 15])
163
+ @pytest.mark.parametrize('n3', [6])
164
+ @pytest.mark.parametrize('p1', [2, 3])
165
+ @pytest.mark.parametrize('p2', [2, 3])
166
+ @pytest.mark.parametrize('p3', [1])
167
+ @pytest.mark.parametrize('s1', [1, 2])
168
+ @pytest.mark.parametrize('s2', [1, 2])
169
+ @pytest.mark.parametrize('s3', [3])
170
+ def test_stencil_interface_matrix_3d_serial_init(dtype, n1, n2, n3, p1, p2, p3, s1, s2, s3, axis1, axis2,
171
+ ext1, ext2, P1=True, P2=True, P3=False):
172
+ # Create domain decomposition
173
+ D = DomainDecomposition([n1, n2, n3], periods=[P1, P2, P3])
174
+
175
+ # Partition the points
176
+ npts = [n1, n2, n3]
177
+ global_starts, global_ends = compute_global_starts_ends(D, npts, [p1, p2, p3])
178
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1, p2, p3], shifts=[s1, s2, s3])
179
+
180
+ # Create a vector space V and a matrix M from V to V
181
+ V = StencilVectorSpace(cart, dtype=dtype)
182
+ V.set_interface(axis1, ext1, cart)
183
+ W = StencilVectorSpace(cart, dtype=dtype)
184
+ W.set_interface(axis2, ext2, cart)
185
+ M = StencilInterfaceMatrix(V, W, global_starts, global_starts, axis1, axis2, ext1, ext2)
186
+
187
+ # Check properties of this matrix
188
+ assert M.domain == V
189
+ assert M.codomain == W
190
+ assert M.dtype == dtype
191
+ assert M.domain_axis == axis1
192
+ assert M.codomain_axis == axis2
193
+ assert M.domain_ext == ext1
194
+ assert M.codomain_ext == ext2
195
+ assert M.dim == 3
196
+ assert M.domain_start == (0,) * M.dim
197
+ assert M.codomain_start == (0,) * M.dim
198
+ assert M.flip == (1,) * M.dim
199
+ assert M.pads == (p1, p2, p3)
200
+ assert M.backend == None
201
+ if axis2 == 0:
202
+ assert M._data.shape == (
203
+ p1 + 1 + 2 * p1 * s1, n2 + 2 * p2 * s2, n3 + 2 * p3 * s3, 1 + 2 * p1, 1 + 2 * p2, 1 + 2 * p3)
204
+ elif axis2 == 1:
205
+ assert M._data.shape == (
206
+ n1 + 2 * p1 * s1, p2 + 1 + 2 * p2 * s2, n3 + 2 * p3 * s3, 1 + 2 * p1, 1 + 2 * p2, 1 + 2 * p3)
207
+ elif axis2 == 2:
208
+ assert M._data.shape == (
209
+ n1 + 2 * p1 * s1, n2 + 2 * p2 * s2, p3 + 1 + 2 * p3 * s3, 1 + 2 * p1, 1 + 2 * p2, 1 + 2 * p3)
210
+ if axis1 == axis2:
211
+ assert np.array_equal(M.permutation, [0, 1, 2])
212
+ else:
213
+ permutation = [0, 1, 2]
214
+ permutation[axis1], permutation[axis2] = permutation[axis2], permutation[axis1]
215
+ assert np.array_equal(M.permutation, permutation)
216
+ assert M.shape == (n1 * n2 * n3, n1 * n2 * n3)
217
+ #===============================================================================
218
+ # Parallel TESTS
219
+ #===============================================================================
220
+ @pytest.mark.parametrize("n1,n2,p1,p2,expected", [(8,8,1,1, 827301207168.0),
221
+ (8,8,2,2, 4824719287396.0),
222
+ (8,8,3,3, 13615010842712.0),
223
+ (12,12,1,1, 3023467041788.0),
224
+ (12,12,2,2, 19555497680544.0),
225
+ (12,12,3,3, 62573623909332.0)])
226
+ @pytest.mark.parallel
227
+ def test_stencil_interface_matrix_2d_parallel_dot(n1, n2, p1, p2, expected):
228
+
229
+ from feectools.ddm.mpi import mpi as MPI
230
+ from feectools.ddm.cart import MultiPatchDomainDecomposition, CartDecomposition, create_interfaces_cart
231
+ from feectools.linalg.block import BlockVectorSpace, BlockVector, BlockLinearOperator
232
+
233
+ # Number of patches
234
+ N = 2
235
+
236
+ nc = [[n1-p1,n2-p2] for i in range(N)]
237
+
238
+ # Periodicity
239
+ P = [[False, False] for i in range(N)]
240
+
241
+ axis = 0
242
+ connectivity = {(0,1):((axis,1),(axis,-1))}
243
+
244
+ comm = MPI.COMM_WORLD
245
+ # Parallel info
246
+
247
+ domain_decomposition = MultiPatchDomainDecomposition(nc, P, comm=comm)
248
+
249
+ # Number of elements
250
+ n = [[n1,n2] for i in range(N)]
251
+
252
+ # Padding ('thickness' of ghost region)
253
+ p = [[p1,p2] for i in range(N)]
254
+
255
+ carts = []
256
+ for i in range(N):
257
+ global_starts = [None]*2
258
+ global_ends = [None]*2
259
+ for j in range(2):
260
+ es = domain_decomposition.domains[i].global_element_starts[j]
261
+ ee = domain_decomposition.domains[i].global_element_ends [j]
262
+
263
+ global_ends [j] = ee.copy()
264
+ global_ends [j][-1] = n[i][j]-1
265
+ global_starts[j] = np.array([0] + (global_ends[j][:-1]+1).tolist())
266
+
267
+ carts.append(CartDecomposition(
268
+ domain_decomposition = domain_decomposition.domains[i],
269
+ npts = n[i],
270
+ global_starts = global_starts,
271
+ global_ends = global_ends,
272
+ pads = p[i],
273
+ shifts = [1,1]))
274
+ carts = tuple(carts)
275
+
276
+ communication_info = (get_minus_starts_ends, get_plus_starts_ends)
277
+ interface_carts = create_interfaces_cart(domain_decomposition, carts, connectivity, communication_info=communication_info)
278
+
279
+ # Create vector spaces
280
+ Vs = [StencilVectorSpace( ci ) for ci in carts]
281
+
282
+ # Create the interface spaces
283
+ for i,j in connectivity:
284
+
285
+ if not carts[i].is_comm_null and not carts[j].is_comm_null:
286
+ cart_minus = carts[i]
287
+ cart_plus = carts[j]
288
+ elif (i,j) in interface_carts:
289
+ cart_minus = interface_carts[i,j]
290
+ cart_plus = interface_carts[i,j]
291
+ else:
292
+ continue
293
+
294
+ ((axis_minus, ext_minus), (axis_plus , ext_plus)) = connectivity[i, j]
295
+ # set interface space for the minus space
296
+
297
+ Vs[i].set_interface(axis_minus, ext_minus, cart_minus)
298
+
299
+ # set interface space for the plus space
300
+ Vs[j].set_interface(axis_plus , ext_plus, cart_plus)
301
+
302
+ V = BlockVectorSpace(*Vs, connectivity=connectivity)
303
+
304
+
305
+ # ...
306
+ # Fill in vector with some values, then update ghost regions
307
+ x = BlockVector( V )
308
+ for i,ci in enumerate(carts):
309
+ if ci.is_comm_null:continue
310
+ s1,s2 = ci.starts
311
+ e1,e2 = ci.ends
312
+ for i1 in range(s1,e1+1):
313
+ for i2 in range(s2,e2+1):
314
+ x[i][i1,i2] = 1
315
+
316
+ # ...
317
+ # Fill in the Matrix with some values
318
+ A = BlockLinearOperator( V, V )
319
+
320
+ # Fill-in pattern
321
+ fill_in = lambda i, i1, i2, k1, k2: 10000*i + 1000*i1 + 100*i2 + 10*abs(k1) + abs(k2)
322
+
323
+ for i,ci in enumerate(carts):
324
+ if ci.is_comm_null:continue
325
+ Aii = StencilMatrix( Vs[i], Vs[i])
326
+ s1,s2 = ci.starts
327
+ e1,e2 = ci.ends
328
+ # Fill in stencil matrix
329
+ for i1 in range(s1, e1+1):
330
+ for i2 in range(s2, e2+1):
331
+ for k1 in range(-p1, p1+1):
332
+ for k2 in range(-p2, p2+1):
333
+ Aii[i1, i2, k1, k2] = fill_in( i, i1, i2, k1, k2 )
334
+
335
+ Aii.remove_spurious_entries()
336
+ A[i,i] = Aii
337
+
338
+ # Fill in stencil interface matrix if the process is on the boundary
339
+ if not carts[0].is_comm_null and (not carts[1].is_comm_null or not interface_carts[0,1].is_comm_null):
340
+ s_d = 0
341
+ s_c = n[0][axis]-p[0][axis]-1-Vs[0].starts[axis]
342
+ A01 = StencilInterfaceMatrix(Vs[1], Vs[0], s_d, s_c, d_axis=axis, c_axis=axis, d_ext=-1, c_ext=1)
343
+
344
+ s1,s2 = carts[0].starts
345
+ e1,e2 = carts[0].ends
346
+ p1,p2 = Vs[0].pads
347
+
348
+ for i2 in range(s2, e2+1):
349
+ A01._data[2*p1,i2+p2-s2,0,p2] = -i2-1
350
+
351
+ A[0,1] = A01
352
+
353
+ if not carts[1].is_comm_null and (not carts[0].is_comm_null or not interface_carts[0,1].is_comm_null):
354
+ s_d = n[0][axis]-p[0][axis]-1-Vs[0].starts[axis]
355
+ s_c = 0
356
+ A10 = StencilInterfaceMatrix(Vs[0], Vs[1], s_d, s_c, d_axis=axis, c_axis=axis, d_ext=1, c_ext=-1)
357
+
358
+ s1,s2 = carts[1].starts
359
+ e1,e2 = carts[1].ends
360
+ p1,p2 = Vs[1].pads
361
+ for i2 in range(s2, e2+1):
362
+ A10._data[p1,i2+p2-s2,2*p1,p2] = -i2-1
363
+
364
+ A[1,0] = A10
365
+
366
+ A = A.T.T
367
+ # Updateh ghost regions and compute matrix-vector product
368
+ x.update_ghost_regions()
369
+ y = A.dot(x)
370
+
371
+ # Check the results
372
+ assert y.inner(y) == expected
373
+
374
+ #===============================================================================
375
+ # SCRIPT FUNCTIONALITY
376
+ #===============================================================================
377
+ if __name__ == "__main__":
378
+ import sys
379
+ pytest.main( sys.argv )