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,1588 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ import pytest
4
+ import numpy as np
5
+ from scipy.sparse import csr_matrix
6
+ from random import random, seed
7
+
8
+ from feectools.ddm.mpi import mpi as MPI
9
+ from feectools.linalg.direct_solvers import SparseSolver
10
+ from feectools.linalg.stencil import StencilVectorSpace, StencilVector, StencilMatrix
11
+ from feectools.linalg.block import BlockVectorSpace, BlockVector
12
+ from feectools.linalg.block import BlockLinearOperator
13
+ from feectools.linalg.utilities import array_to_psydac, petsc_to_psydac
14
+ from feectools.linalg.sparse import SparseMatrixLinearOperator
15
+ from feectools.api.settings import PSYDAC_BACKEND_GPYCCEL
16
+ from feectools.ddm.cart import DomainDecomposition, CartDecomposition
17
+
18
+ #===============================================================================
19
+ def compute_global_starts_ends(domain_decomposition, npts):
20
+ global_starts = [None]*len(npts)
21
+ global_ends = [None]*len(npts)
22
+
23
+ for axis in range(len(npts)):
24
+ es = domain_decomposition.global_element_starts[axis]
25
+ ee = domain_decomposition.global_element_ends [axis]
26
+
27
+ global_ends [axis] = ee.copy()
28
+ global_ends [axis][-1] = npts[axis]-1
29
+ global_starts[axis] = np.array([0] + (global_ends[axis][:-1]+1).tolist())
30
+
31
+ return global_starts, global_ends
32
+
33
+ #===============================================================================
34
+ # SERIAL TESTS
35
+ #===============================================================================
36
+ @pytest.mark.parametrize( 'dtype', [float] )
37
+ @pytest.mark.parametrize( 'n1', [8, 16] )
38
+ @pytest.mark.parametrize( 'n2', [8, 12] )
39
+ @pytest.mark.parametrize( 'p1', [1, 2] )
40
+ @pytest.mark.parametrize( 'p2', [1, 3] )
41
+ @pytest.mark.parametrize( 'P1', [True, False] )
42
+ @pytest.mark.parametrize( 'P2', [True] )
43
+
44
+ def test_2D_block_vector_space_serial_init( dtype, n1, n2, p1, p2, P1, P2 ):
45
+ # set seed for reproducibility
46
+ seed(n1*n2*p1*p2)
47
+
48
+ D = DomainDecomposition([n1,n2], periods=[P1,P2])
49
+
50
+ # Partition the points
51
+ npts = [n1,n2]
52
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
53
+
54
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
55
+
56
+ # Create vector spaces, stencil matrices, and stencil vectors
57
+ V = StencilVectorSpace( cart, dtype=dtype )
58
+ W = BlockVectorSpace(V, V)
59
+
60
+ assert W.dimension == 2*n1*n2
61
+ assert W.dtype == dtype
62
+ assert W.spaces == (V,V)
63
+ assert W.parallel == False
64
+ assert W.starts == [(0,0),(0,0)]
65
+ assert W.ends == [(n1-1,n2-1),(n1-1,n2-1)]
66
+ assert W.pads == (p1,p2)
67
+ assert W.n_blocks == 2
68
+ assert W.connectivity== {}
69
+ #===============================================================================
70
+ @pytest.mark.parametrize( 'dtype', [float] )
71
+ @pytest.mark.parametrize( 'n1', [8, 16] )
72
+ @pytest.mark.parametrize( 'n2', [8, 12] )
73
+ @pytest.mark.parametrize( 'p1', [1, 2] )
74
+ @pytest.mark.parametrize( 'p2', [1, 3] )
75
+ @pytest.mark.parametrize( 'P1', [True, False] )
76
+ @pytest.mark.parametrize( 'P2', [True] )
77
+
78
+ def test_2D_block_vector_serial_init( dtype, n1, n2, p1, p2, P1, P2 ):
79
+ # set seed for reproducibility
80
+ seed(n1*n2*p1*p2)
81
+
82
+ D = DomainDecomposition([n1,n2], periods=[P1,P2])
83
+
84
+ # Partition the points
85
+ npts = [n1,n2]
86
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
87
+
88
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
89
+
90
+ # Create vector spaces, stencil matrices, and stencil vectors
91
+ V = StencilVectorSpace( cart, dtype=dtype )
92
+ x1 = StencilVector( V )
93
+ x2 = StencilVector( V )
94
+
95
+ W = BlockVectorSpace(V, V)
96
+ x = BlockVector(W, blocks=[x1,x2])
97
+ assert x.dtype == dtype
98
+ assert x.space == W
99
+ assert x.n_blocks == 2
100
+ assert x.blocks == (x1, x2)
101
+ #===============================================================================
102
+ @pytest.mark.parametrize( 'dtype', [float] )
103
+ @pytest.mark.parametrize( 'n1', [8, 16] )
104
+ @pytest.mark.parametrize( 'n2', [8, 12] )
105
+ @pytest.mark.parametrize( 'p1', [1, 2] )
106
+ @pytest.mark.parametrize( 'p2', [1, 3] )
107
+ @pytest.mark.parametrize( 'P1', [True, False] )
108
+ @pytest.mark.parametrize( 'P2', [True] )
109
+
110
+ def test_2D_block_linear_operator_serial_init( dtype, n1, n2, p1, p2, P1, P2 ):
111
+ # set seed for reproducibility
112
+ seed(n1*n2*p1*p2)
113
+
114
+ D = DomainDecomposition([n1,n2], periods=[P1,P2])
115
+
116
+ # Partition the points
117
+ npts = [n1,n2]
118
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
119
+
120
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
121
+
122
+ # Create vector spaces, stencil matrices, and stencil vectors
123
+ V = StencilVectorSpace( cart, dtype=dtype )
124
+ M1 = StencilMatrix( V, V)
125
+ M2 = StencilMatrix( V, V )
126
+ M3 = StencilMatrix( V, V )
127
+ x1 = StencilVector( V )
128
+ x2 = StencilVector( V )
129
+
130
+ # Fill in stencil matrices based on diagonal index
131
+ if dtype==complex:
132
+ f=lambda k1,k2: 10j*k1+k2
133
+ else:
134
+ f=lambda k1,k2: 10*k1+k2
135
+
136
+ for k1 in range(-p1,p1+1):
137
+ for k2 in range(-p2,p2+1):
138
+ M1[:,:,k1,k2] = f(k1,k2)
139
+ M2[:,:,k1,k2] = f(k1,k2)+2.
140
+ M3[:,:,k1,k2] = f(k1,k2)+5.
141
+
142
+ M1.remove_spurious_entries()
143
+ M2.remove_spurious_entries()
144
+ M3.remove_spurious_entries()
145
+
146
+ # Fill in vector with random values, then update ghost regions
147
+ for i1 in range(n1):
148
+ for i2 in range(n2):
149
+ x1[i1,i2] = 2.0*random() - 1.0
150
+ x2[i1,i2] = 5.0*random() - 1.0
151
+ x1.update_ghost_regions()
152
+ x2.update_ghost_regions()
153
+
154
+ W = BlockVectorSpace(V, V)
155
+
156
+ # Construct a BlockLinearOperator object containing M1, M2, M, using 3 ways
157
+ # |M1 M2|
158
+ # L = | |
159
+ # |M3 0 |
160
+
161
+ dict_blocks = {(0,0):M1, (0,1):M2, (1,0):M3}
162
+ list_blocks = [[M1, M2], [M3, None]]
163
+
164
+ L1 = BlockLinearOperator( W, W, blocks=dict_blocks )
165
+ assert L1.domain == W
166
+ assert L1.codomain == W
167
+ assert L1.dtype == dtype
168
+ assert L1.blocks == ((M1,M2),(M3,None))
169
+ assert L1.n_block_rows == 2
170
+ assert L1.n_block_cols == 2
171
+ assert L1.nonzero_block_indices == ((0,0),(0,1),(1,0))
172
+ assert L1.backend()==None
173
+
174
+ # Test copy method with an out
175
+ L4 = BlockLinearOperator( W, W )
176
+ L1.copy(out=L4)
177
+ assert L1.domain == W
178
+ assert L1.codomain == W
179
+ assert L1.dtype == dtype
180
+ assert L1.n_block_rows == 2
181
+ assert L1.n_block_cols == 2
182
+ assert L1.nonzero_block_indices == ((0,0),(0,1),(1,0))
183
+ assert L1.backend()==None
184
+
185
+ L2 = BlockLinearOperator( W, W, blocks=list_blocks )
186
+ L3 = BlockLinearOperator( W, W )
187
+
188
+ L3[0,0] = M1
189
+ L3[0,1] = M2
190
+ L3[1,0] = M3
191
+
192
+ # Convert L1, L2, L3 and L4 to COO form
193
+ coo1 = L1.tosparse().tocoo()
194
+ coo2 = L2.tosparse().tocoo()
195
+ coo3 = L3.tosparse().tocoo()
196
+ coo4 = L4.tosparse().tocoo()
197
+
198
+ # Check if the data are in the same place
199
+ assert np.array_equal( coo1.col , coo2.col )
200
+ assert np.array_equal( coo1.row , coo2.row )
201
+ assert np.array_equal( coo1.data, coo2.data )
202
+
203
+ assert np.array_equal( coo1.col , coo3.col )
204
+ assert np.array_equal( coo1.row , coo3.row )
205
+ assert np.array_equal( coo1.data, coo3.data )
206
+
207
+ assert np.array_equal( coo1.col , coo4.col )
208
+ assert np.array_equal( coo1.row , coo4.row )
209
+ assert np.array_equal( coo1.data, coo4.data )
210
+
211
+ dict_blocks = {(0,0):M1, (0,1):M2}
212
+
213
+ L1 = BlockLinearOperator( W, W, blocks=dict_blocks )
214
+
215
+ # Test transpose
216
+ LT1 = L1.transpose()
217
+ LT2 = BlockLinearOperator( W, W )
218
+ L1.transpose(out=LT2)
219
+
220
+ assert LT1.domain == W
221
+ assert LT1.codomain == W
222
+ assert LT1.dtype == dtype
223
+ assert LT1.n_block_rows == 2
224
+ assert LT1.n_block_cols == 2
225
+ assert LT1.nonzero_block_indices == ((0,0),(1,0))
226
+ assert LT1.backend()==None
227
+
228
+ assert LT2.domain == W
229
+ assert LT2.codomain == W
230
+ assert LT2.dtype == dtype
231
+ assert LT2.n_block_rows == 2
232
+ assert LT2.n_block_cols == 2
233
+ assert LT2.nonzero_block_indices == ((0,0),(1,0))
234
+ assert LT2.backend()==None
235
+
236
+ #convert to scipy for tests
237
+ LT1_sp = LT1.tosparse()
238
+ LT2_sp = LT2.tosparse()
239
+ L1_spT = L1.tosparse().T
240
+
241
+ # Check if the data are in the same place
242
+ assert abs(LT1_sp - L1_spT).max()< 1e-14
243
+ assert abs(LT2_sp - L1_spT).max()< 1e-14
244
+
245
+ #===============================================================================
246
+ @pytest.mark.parametrize( 'dtype', [float] )
247
+ @pytest.mark.parametrize( 'ndim', [1, 2, 3] )
248
+ @pytest.mark.parametrize( 'p', [1, 2] )
249
+ @pytest.mark.parametrize( 'P1', [True, False] )
250
+ @pytest.mark.parametrize( 'P2', [True, False] )
251
+ @pytest.mark.parametrize( 'P3', [True] )
252
+ def test_block_serial_dimension( ndim, p, P1, P2, P3, dtype ):
253
+
254
+ if ndim == 1:
255
+ npts = [12]
256
+ ps = [p]
257
+ Ps = [P1]
258
+ shifts = [1]
259
+
260
+ elif ndim == 2:
261
+ npts =[12, 15]
262
+ ps = [p, p]
263
+ Ps = [P1, P2]
264
+ shifts = [1, 1]
265
+
266
+ else:
267
+ npts = [12, 15, 9]
268
+ ps = [p, p, p]
269
+ Ps = [P1, P2, P3]
270
+ shifts = [1, 1, 1]
271
+
272
+ # set seed for reproducibility
273
+ D = DomainDecomposition(npts, periods=Ps)
274
+
275
+ # Partition the points
276
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
277
+
278
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=ps, shifts=shifts)
279
+
280
+ # Create vector spaces, stencil matrices, and stencil vectors
281
+ V = StencilVectorSpace( cart, dtype=dtype)
282
+ if dtype==complex:
283
+ cst=1j
284
+ else:
285
+ cst=1
286
+
287
+ x1 = StencilVector( V )
288
+ x2 = StencilVector( V )
289
+ y1 = StencilVector( V )
290
+ y2 = StencilVector( V )
291
+
292
+ W = BlockVectorSpace(V, V)
293
+
294
+
295
+ # Fill in vector with random values, then update ghost regions
296
+ if ndim==1:
297
+ x1[:] = cst*2.0*np.random.random((npts[0]+2*p))
298
+ x2[:] = cst*5.0*np.random.random((npts[0]+2*p))
299
+ y1[:] = cst*2.0*np.random.random((npts[0]+2*p))
300
+ y2[:] = cst*3.0*np.random.random((npts[0]+2*p))
301
+
302
+ elif ndim==2:
303
+ x1[:,:] = cst*2.0*np.random.random((npts[0]+2*p,npts[1]+2*p))
304
+ x2[:,:] = cst*5.0*np.random.random((npts[0]+2*p,npts[1]+2*p))
305
+ y1[:,:] = cst*2.0*np.random.random((npts[0]+2*p,npts[1]+2*p))
306
+ y2[:,:] = cst*3.0*np.random.random((npts[0]+2*p,npts[1]+2*p))
307
+
308
+ else:
309
+ x1[:,:,:] = cst*2.0*np.random.random((npts[0]+2*p,npts[1]+2*p,npts[2]+2*p))
310
+ x2[:,:,:] = cst*5.0*np.random.random((npts[0]+2*p,npts[1]+2*p,npts[2]+2*p))
311
+ y1[:,:,:] = cst*2.0*np.random.random((npts[0]+2*p,npts[1]+2*p,npts[2]+2*p))
312
+ y2[:,:,:] = cst*3.0*np.random.random((npts[0]+2*p,npts[1]+2*p,npts[2]+2*p))
313
+
314
+ x1.update_ghost_regions()
315
+ x2.update_ghost_regions()
316
+ y1.update_ghost_regions()
317
+ y2.update_ghost_regions()
318
+
319
+ # Construct a BlockVector object containing x1 and x2
320
+ # |x1|
321
+ # X = | |
322
+ # |x2|
323
+
324
+ X = BlockVector(W)
325
+ X[0] = x1
326
+ X[1] = x2
327
+
328
+ Y = BlockVector(W)
329
+ Y[0] = y1
330
+ Y[1] = y2
331
+
332
+ # Test dot product
333
+ exact_inner = V.inner(x1, y1) + V.inner(x2, y2)
334
+
335
+ assert X.dtype == dtype
336
+ assert np.allclose(W.inner(X, Y), exact_inner, rtol=1e-14, atol=1e-14 )
337
+
338
+ # Test axpy product
339
+ axpy_exact = X + np.pi * cst * Y
340
+ X.mul_iadd(np.pi * cst, Y)
341
+ assert np.allclose(X[0]._data, axpy_exact[0]._data, rtol=1e-10, atol=1e-10 )
342
+ assert np.allclose(X[1]._data, axpy_exact[1]._data, rtol=1e-10, atol=1e-10 )
343
+
344
+ M1 = StencilMatrix(V, V)
345
+ M2 = StencilMatrix(V, V)
346
+ M3 = StencilMatrix(V, V)
347
+
348
+ # Fill in stencil matrices based on diagonal index
349
+ if ndim==1:
350
+ f = lambda k1: 10 * k1
351
+ for k1 in range(-ps[0], ps[0] + 1):
352
+ M1[:, k1] = f(k1)
353
+ M2[:, k1] = f(k1) + 2.
354
+ M3[:, k1] = f(k1) + 5.
355
+ if ndim==2:
356
+ f = lambda k1,k2: 10 * k1 + 100*k2
357
+ for k1 in range(-ps[0], ps[0] + 1):
358
+ for k2 in range(-ps[1], ps[1] + 1):
359
+ M1[:, k1] = f(k1,k2)
360
+ M2[:, k1] = f(k1,k2) + 2.
361
+ M3[:, k1] = f(k1,k2) + 5.
362
+ if ndim==3:
363
+ f = lambda k1, k2, k3: 10 * k1 + 100*k2+1000*k3
364
+ for k1 in range(-ps[0], ps[0] + 1):
365
+ for k2 in range(-ps[1], ps[1] + 1):
366
+ for k3 in range(-ps[1], ps[1] + 1):
367
+ M1[:, k1] = f(k1,k2,k3)
368
+ M2[:, k1] = f(k1,k2,k3) + 2.
369
+ M3[:, k1] = f(k1,k2,k3) + 5.
370
+
371
+ M1.remove_spurious_entries()
372
+ M2.remove_spurious_entries()
373
+ M3.remove_spurious_entries()
374
+
375
+ M = BlockLinearOperator(W, W, blocks=[[M1, M2], [M3, None]])
376
+
377
+ Y[0] = M1.dot(x1) + M2.dot(x2)
378
+ Y[1] = M3.dot(x1)
379
+
380
+ assert M.dtype == dtype
381
+ assert np.allclose((M.dot(X)).toarray(), Y.toarray(), rtol=1e-14, atol=1e-14 )
382
+
383
+ #===============================================================================
384
+ @pytest.mark.parametrize( 'dtype', [float] )
385
+ @pytest.mark.parametrize( 'npts', [[6, 8, 9]] )
386
+ @pytest.mark.parametrize( 'p', [[1,1,1], [2,3,4]] )
387
+ @pytest.mark.parametrize( 'P1', [True, False] )
388
+ @pytest.mark.parametrize( 'P2', [True, False] )
389
+ @pytest.mark.parametrize( 'P3', [True] )
390
+ def test_3D_block_serial_basic_operator( dtype, npts, p, P1, P2, P3 ):
391
+
392
+ # set seed for reproducibility
393
+ D = DomainDecomposition(npts, periods=[P1,P2,P3])
394
+
395
+ # Partition the points
396
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
397
+
398
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=p, shifts=[1,1,1])
399
+
400
+ # Create vector spaces, stencil matrices, and stencil vectors
401
+ V = StencilVectorSpace( cart, dtype=dtype)
402
+
403
+ x1 = StencilVector( V )
404
+ x2 = StencilVector( V )
405
+ y1 = StencilVector( V )
406
+ y2 = StencilVector( V )
407
+
408
+ W = BlockVectorSpace(V, V)
409
+ if dtype==complex:
410
+ x1[:,:,:] = 2.0*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1],npts[2]+2*p[2]))+1j*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1],npts[2]+2*p[2]))
411
+ x2[:,:,:] = 5.0*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1],npts[2]+2*p[2]))+2j*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1],npts[2]+2*p[2]))
412
+ else:
413
+ x1[:,:,:] = 2.0*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1],npts[2]+2*p[2]))
414
+ x2[:,:,:] = 5.0*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1],npts[2]+2*p[2]))
415
+
416
+
417
+ x1.update_ghost_regions()
418
+ x2.update_ghost_regions()
419
+ y1.update_ghost_regions()
420
+ y2.update_ghost_regions()
421
+
422
+ # Construct a BlockVector object containing x1 and x2
423
+ # |x1|
424
+ # X = | |
425
+ # |x2|
426
+
427
+ X = BlockVector(W)
428
+ X[0] = x1
429
+ X[1] = x2
430
+
431
+ Y = BlockVector(W)
432
+
433
+ Y +=X
434
+ assert Y.dtype == dtype
435
+ assert np.allclose(Y.blocks[0]._data, (x1)._data, rtol=1e-14, atol=1e-14 )
436
+ assert np.allclose(Y.blocks[1]._data, (x2)._data, rtol=1e-14, atol=1e-14 )
437
+
438
+ Y -=2*X
439
+ assert np.allclose(Y.blocks[0]._data, -(x1)._data, rtol=1e-14, atol=1e-14 )
440
+ assert np.allclose(Y.blocks[1]._data, -(x2)._data, rtol=1e-14, atol=1e-14 )
441
+
442
+ Y *=6
443
+ assert np.allclose(Y.blocks[0]._data, -6*(x1)._data, rtol=1e-14, atol=1e-14 )
444
+ assert np.allclose(Y.blocks[1]._data, -6*(x2)._data, rtol=1e-14, atol=1e-14 )
445
+
446
+ Y /=-6
447
+ assert np.allclose(Y.blocks[0]._data, (x1)._data, rtol=1e-14, atol=1e-14 )
448
+ assert np.allclose(Y.blocks[1]._data, (x2)._data, rtol=1e-14, atol=1e-14 )
449
+
450
+ Y[0]=x2
451
+ Y[1]=-x1
452
+
453
+ Z1=Y+X
454
+ assert isinstance(Z1,BlockVector)
455
+ assert np.allclose(Z1.blocks[0]._data, (x1+x2)._data, rtol=1e-14, atol=1e-14 )
456
+ assert np.allclose(Z1.blocks[1]._data, (x2-x1)._data, rtol=1e-14, atol=1e-14 )
457
+
458
+ Z2=Y-X
459
+ assert isinstance(Z2,BlockVector)
460
+ assert np.allclose(Z2.blocks[0]._data, (x2-x1)._data, rtol=1e-14, atol=1e-14 )
461
+ assert np.allclose(Z2.blocks[1]._data, (-x2-x1)._data, rtol=1e-14, atol=1e-14 )
462
+
463
+ Z3=3*Y
464
+ assert isinstance(Z3,BlockVector)
465
+ assert np.allclose(Z3.blocks[0]._data, 3*(x2)._data, rtol=1e-14, atol=1e-14 )
466
+ assert np.allclose(Z3.blocks[1]._data, 3*(-x1)._data, rtol=1e-14, atol=1e-14 )
467
+
468
+ Z4=Y/4
469
+ assert isinstance(Z4,BlockVector)
470
+ assert np.allclose(Z4.blocks[0]._data, (x2)._data/4, rtol=1e-14, atol=1e-14 )
471
+ assert np.allclose(Z4.blocks[1]._data, (-x1)._data/4, rtol=1e-14, atol=1e-14 )
472
+
473
+
474
+ M1 = StencilMatrix(V, V)
475
+ M2 = StencilMatrix(V, V)
476
+ M3 = StencilMatrix(V, V)
477
+
478
+ if dtype==complex:
479
+ f = lambda k1, k2, k3: 10 * k1 + 100j*k2+1000*k3
480
+ else:
481
+ f = lambda k1, k2, k3: 10 * k1 + 100*k2+1000*k3
482
+
483
+ for k1 in range(-p[0], p[0] + 1):
484
+ for k2 in range(-p[1], p[1] + 1):
485
+ for k3 in range(-p[1], p[1] + 1):
486
+ M1[:, k1] = f(k1,k2,k3)
487
+ M2[:, k1] = f(k1,k2,k3) + 2.
488
+ M3[:, k1] = f(k1,k2,k3) + 5.
489
+
490
+ M1.remove_spurious_entries()
491
+ M2.remove_spurious_entries()
492
+ M3.remove_spurious_entries()
493
+
494
+ M = BlockLinearOperator(W, W, blocks=[[M1, M2], [M3, None]])
495
+ A = BlockLinearOperator(W, W)
496
+
497
+ A +=M
498
+ assert A.dtype == dtype
499
+ assert np.allclose(A.blocks[0][0]._data, (M1)._data, rtol=1e-14, atol=1e-14 )
500
+ assert np.allclose(A.blocks[0][1]._data, (M2)._data, rtol=1e-14, atol=1e-14 )
501
+ assert np.allclose(A.blocks[1][0]._data, (M3)._data, rtol=1e-14, atol=1e-14 )
502
+ assert A.blocks[1][1]==None
503
+
504
+ A -= 2*M
505
+ assert np.allclose(A.blocks[0][0]._data, -(M1)._data, rtol=1e-14, atol=1e-14 )
506
+ assert np.allclose(A.blocks[0][1]._data, -(M2)._data, rtol=1e-14, atol=1e-14 )
507
+ assert np.allclose(A.blocks[1][0]._data, -(M3)._data, rtol=1e-14, atol=1e-14 )
508
+ assert A.blocks[1][1]==None
509
+
510
+ A *= 5
511
+ assert np.allclose(A.blocks[0][0]._data, -5*(M1)._data, rtol=1e-14, atol=1e-14 )
512
+ assert np.allclose(A.blocks[0][1]._data, -5*(M2)._data, rtol=1e-14, atol=1e-14 )
513
+ assert np.allclose(A.blocks[1][0]._data, -5*(M3)._data, rtol=1e-14, atol=1e-14 )
514
+ assert A.blocks[1][1]==None
515
+
516
+ A /= -5
517
+ assert np.allclose(A.blocks[0][0]._data, (M1)._data, rtol=1e-14, atol=1e-14 )
518
+ assert np.allclose(A.blocks[0][1]._data, (M2)._data, rtol=1e-14, atol=1e-14 )
519
+ assert np.allclose(A.blocks[1][0]._data, (M3)._data, rtol=1e-14, atol=1e-14 )
520
+ assert A.blocks[1][1]==None
521
+
522
+ A= BlockLinearOperator(W, W, blocks=[[None, M3], [M2, M1]])
523
+
524
+ A1=A+M
525
+ assert isinstance(A1,BlockLinearOperator)
526
+ assert np.allclose(A1.blocks[0][0]._data, (M1)._data, rtol=1e-14, atol=1e-14 )
527
+ assert np.allclose(A1.blocks[0][1]._data, (M2+M3)._data, rtol=1e-14, atol=1e-14 )
528
+ assert np.allclose(A1.blocks[1][0]._data, (M3+M2)._data, rtol=1e-14, atol=1e-14 )
529
+ assert np.allclose(A1.blocks[1][1]._data, (M1)._data, rtol=1e-14, atol=1e-14 )
530
+
531
+ A2=A-M
532
+ assert isinstance(A2,BlockLinearOperator)
533
+ assert np.allclose(A2.blocks[0][0]._data, (-M1)._data, rtol=1e-14, atol=1e-14 )
534
+ assert np.allclose(A2.blocks[0][1]._data, (M3-M2)._data, rtol=1e-14, atol=1e-14 )
535
+ assert np.allclose(A2.blocks[1][0]._data, (M2-M3)._data, rtol=1e-14, atol=1e-14 )
536
+ assert np.allclose(A2.blocks[1][1]._data, (M1)._data, rtol=1e-14, atol=1e-14 )
537
+
538
+ A3=6*A
539
+ assert isinstance(A3,BlockLinearOperator)
540
+ assert np.allclose(A3.blocks[0][1]._data, 6*(M3)._data, rtol=1e-14, atol=1e-14 )
541
+ assert np.allclose(A3.blocks[1][0]._data, 6*(M2)._data, rtol=1e-14, atol=1e-14 )
542
+ assert np.allclose(A3.blocks[1][1]._data, 6*(M1)._data, rtol=1e-14, atol=1e-14 )
543
+
544
+ A4=A/5
545
+ assert isinstance(A4,BlockLinearOperator)
546
+ assert np.allclose(A4.blocks[0][1]._data, (M3)._data/5, rtol=1e-14, atol=1e-14 )
547
+ assert np.allclose(A4.blocks[1][0]._data, (M2)._data/5, rtol=1e-14, atol=1e-14 )
548
+ assert np.allclose(A4.blocks[1][1]._data, (M1)._data/5, rtol=1e-14, atol=1e-14 )
549
+
550
+ #===============================================================================
551
+ @pytest.mark.parametrize( 'dtype', [float] )
552
+ @pytest.mark.parametrize( 'npts', [[6, 8]] )
553
+ @pytest.mark.parametrize( 'p', [[1,1], [2,3]] )
554
+ @pytest.mark.parametrize( 'P1', [True, False] )
555
+ @pytest.mark.parametrize( 'P2', [True] )
556
+ def test_2D_block_serial_math( dtype, npts, p, P1, P2 ):
557
+
558
+ # set seed for reproducibility
559
+ D = DomainDecomposition(npts, periods=[P1,P2])
560
+
561
+ # Partition the points
562
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
563
+
564
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=p, shifts=[1,1])
565
+
566
+ # Create vector spaces, stencil matrices, and stencil vectors
567
+ V = StencilVectorSpace( cart, dtype=dtype)
568
+
569
+ x1 = StencilVector( V )
570
+ x2 = StencilVector( V )
571
+
572
+ W = BlockVectorSpace(V, V)
573
+ if dtype==complex:
574
+ x1[:,:,:] = 2.0*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1]))+1j*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1]))
575
+ x2[:,:,:] = 5.0*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1]))+2j*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1]))
576
+ else:
577
+ x1[:,:,:] = 2.0*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1]))
578
+ x2[:,:,:] = 5.0*np.random.random((npts[0]+2*p[0],npts[1]+2*p[1]))
579
+
580
+
581
+ x1.update_ghost_regions()
582
+ x2.update_ghost_regions()
583
+
584
+ x1a = x1.toarray().conj()
585
+ x2a = x2.toarray().conj()
586
+
587
+ # Construct a BlockVector object containing x1 and x2
588
+ # |x1|
589
+ # X = | |
590
+ # |x2|
591
+
592
+ X = BlockVector(W)
593
+ X[0] = x1
594
+ X[1] = x2
595
+
596
+ Xc=X.conjugate()
597
+ assert np.allclose(Xc.blocks[0].toarray(), x1a, rtol=1e-14, atol=1e-14 )
598
+ assert np.allclose(Xc.blocks[1].toarray(), x2a, rtol=1e-14, atol=1e-14 )
599
+
600
+ Xc=X.conj()
601
+ assert np.allclose(Xc.blocks[0].toarray(), x1a, rtol=1e-14, atol=1e-14 )
602
+ assert np.allclose(Xc.blocks[1].toarray(), x2a, rtol=1e-14, atol=1e-14 )
603
+
604
+
605
+ M1 = StencilMatrix(V, V)
606
+ M2 = StencilMatrix(V, V)
607
+ M3 = StencilMatrix(V, V)
608
+
609
+ if dtype==complex:
610
+ f = lambda k1, k2: 10 * k1 + 100j*k2
611
+ else:
612
+ f = lambda k1, k2: 10 * k1 + 100*k2
613
+
614
+ for k1 in range(-p[0], p[0] + 1):
615
+ for k2 in range(-p[1], p[1] + 1):
616
+ M1[:, k1] = f(k1,k2)
617
+ M2[:, k1] = f(k1,k2) + 2.
618
+ M3[:, k1] = f(k1,k2) + 5.
619
+
620
+ M1.remove_spurious_entries()
621
+ M2.remove_spurious_entries()
622
+ M3.remove_spurious_entries()
623
+
624
+ M1a = M1.toarray().conjugate()
625
+ M2a = M2.toarray().conjugate()
626
+ M3a = M3.toarray().conjugate()
627
+
628
+ # Construct a BlockLinearOperator object containing M1, M2, M3
629
+ # |M1 M2|
630
+ # M = | |
631
+ # |M3 0|
632
+
633
+ M = BlockLinearOperator(W, W, blocks=[[M1, M2], [M3, None]])
634
+
635
+ Mc = M.conjugate()
636
+ assert np.allclose(Mc.blocks[0][0].toarray(), M1a, rtol=1e-14, atol=1e-14 )
637
+ assert np.allclose(Mc.blocks[0][1].toarray(), M2a, rtol=1e-14, atol=1e-14 )
638
+ assert np.allclose(Mc.blocks[1][0].toarray(), M3a, rtol=1e-14, atol=1e-14 )
639
+
640
+ Mc = M.conj()
641
+ assert np.allclose(Mc.blocks[0][0].toarray(), M1a, rtol=1e-14, atol=1e-14 )
642
+ assert np.allclose(Mc.blocks[0][1].toarray(), M2a, rtol=1e-14, atol=1e-14 )
643
+ assert np.allclose(Mc.blocks[1][0].toarray(), M3a, rtol=1e-14, atol=1e-14 )
644
+
645
+ #===============================================================================
646
+ @pytest.mark.parametrize( 'dtype', [float] )
647
+ @pytest.mark.parametrize( 'n1', [8, 16] )
648
+ @pytest.mark.parametrize( 'n2', [8, 12] )
649
+ @pytest.mark.parametrize( 'p1', [1, 3] )
650
+ @pytest.mark.parametrize( 'p2', [1, 2] )
651
+ @pytest.mark.parametrize( 'P1', [True, False] )
652
+ @pytest.mark.parametrize( 'P2', [True] )
653
+
654
+ def test_block_linear_operator_serial_dot( dtype, n1, n2, p1, p2, P1, P2 ):
655
+ # set seed for reproducibility
656
+ seed(n1*n2*p1*p2)
657
+
658
+ D = DomainDecomposition([n1,n2], periods=[P1,P2])
659
+
660
+ # Partition the points
661
+ npts = [n1,n2]
662
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
663
+
664
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
665
+
666
+ # Create vector spaces, stencil matrices, and stencil vectors
667
+ V = StencilVectorSpace( cart, dtype=dtype )
668
+ M1 = StencilMatrix( V, V)
669
+ M2 = StencilMatrix( V, V )
670
+ M3 = StencilMatrix( V, V )
671
+ x1 = StencilVector( V )
672
+ x2 = StencilVector( V )
673
+
674
+ # Fill in stencil matrices based on diagonal index
675
+ if dtype==complex:
676
+ f=lambda k1,k2: 10j*k1+k2
677
+ else:
678
+ f=lambda k1,k2: 10*k1+k2
679
+
680
+ for k1 in range(-p1,p1+1):
681
+ for k2 in range(-p2,p2+1):
682
+ M1[:,:,k1,k2] = f(k1,k2)
683
+ M2[:,:,k1,k2] = f(k1,k2)+2.
684
+ M3[:,:,k1,k2] = f(k1,k2)+5.
685
+
686
+ M1.remove_spurious_entries()
687
+ M2.remove_spurious_entries()
688
+ M3.remove_spurious_entries()
689
+
690
+ # Fill in vector with random values, then update ghost regions
691
+ for i1 in range(n1):
692
+ for i2 in range(n2):
693
+ x1[i1,i2] = 2.0*random() - 1.0
694
+ x2[i1,i2] = 5.0*random() - 1.0
695
+ x1.update_ghost_regions()
696
+ x2.update_ghost_regions()
697
+
698
+ W = BlockVectorSpace(V, V)
699
+
700
+ # Construct a BlockLinearOperator object containing M1, M2, M, using 3 ways
701
+ # |M1 M2|
702
+ # L = | |
703
+ # |M3 0 |
704
+
705
+ dict_blocks = {(0,0):M1, (0,1):M2, (1,0):M3}
706
+
707
+ L = BlockLinearOperator( W, W, blocks=dict_blocks )
708
+
709
+ # Construct a BlockVector object containing x1 and x2
710
+ # |x1|
711
+ # X = | |
712
+ # |x2|
713
+
714
+ X = BlockVector(W)
715
+ X[0] = x1
716
+ X[1] = x2
717
+
718
+ # Compute BlockLinearOperator product
719
+ Y = L.dot(X)
720
+
721
+ # Compute matrix-vector products for each block
722
+ y1 = M1.dot(x1) + M2.dot(x2)
723
+ y2 = M3.dot(x1)
724
+
725
+ # Check data in 1D array
726
+ assert np.allclose( Y.blocks[0].toarray(), y1.toarray(), rtol=1e-14, atol=1e-14 )
727
+ assert np.allclose( Y.blocks[1].toarray(), y2.toarray(), rtol=1e-14, atol=1e-14 )
728
+ #===============================================================================
729
+ @pytest.mark.parametrize( 'dtype', [float] )
730
+ @pytest.mark.parametrize( 'n1', [8, 16] )
731
+ @pytest.mark.parametrize( 'n2', [8, 12] )
732
+ @pytest.mark.parametrize( 'p1', [1, 3] )
733
+ @pytest.mark.parametrize( 'p2', [1, 2] )
734
+ @pytest.mark.parametrize( 'P1', [True, False] )
735
+ @pytest.mark.parametrize( 'P2', [True] )
736
+
737
+ def test_sparse_matrix_linear_operator_serial_dot( dtype, n1, n2, p1, p2, P1, P2 ):
738
+ # set seed for reproducibility
739
+ seed(n1*n2*p1*p2)
740
+
741
+ D = DomainDecomposition([n1,n2], periods=[P1,P2])
742
+
743
+ # Partition the points
744
+ npts = [n1,n2]
745
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
746
+
747
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
748
+
749
+ # Create vector spaces, stencil matrices, and stencil vectors
750
+ V = StencilVectorSpace( cart, dtype=dtype )
751
+ M1 = StencilMatrix( V, V)
752
+ M2 = StencilMatrix( V, V )
753
+ M3 = StencilMatrix( V, V )
754
+ x1 = StencilVector( V )
755
+ x2 = StencilVector( V )
756
+
757
+ # Fill in stencil matrices based on diagonal index
758
+ if dtype==complex:
759
+ f=lambda k1,k2: 10j*k1+k2
760
+ else:
761
+ f=lambda k1,k2: 10*k1+k2
762
+
763
+ for k1 in range(-p1,p1+1):
764
+ for k2 in range(-p2,p2+1):
765
+ M1[:,:,k1,k2] = f(k1,k2)
766
+ M2[:,:,k1,k2] = f(k1,k2)+2.
767
+ M3[:,:,k1,k2] = f(k1,k2)+5.
768
+
769
+ M1.remove_spurious_entries()
770
+ M2.remove_spurious_entries()
771
+ M3.remove_spurious_entries()
772
+
773
+ # Fill in vector with random values, then update ghost regions
774
+ for i1 in range(n1):
775
+ for i2 in range(n2):
776
+ x1[i1,i2] = 2.0*random() - 1.0
777
+ x2[i1,i2] = 5.0*random() - 1.0
778
+ x1.update_ghost_regions()
779
+ x2.update_ghost_regions()
780
+
781
+ W = BlockVectorSpace(V, V)
782
+
783
+ # Construct a BlockLinearOperator object containing M1, M2, M, using 3 ways
784
+ # |M1 M2|
785
+ # L = | |
786
+ # |M3 0 |
787
+
788
+ dict_blocks = {(0,0):M1, (0,1):M2, (1,0):M3}
789
+
790
+ L = BlockLinearOperator( W, W, blocks=dict_blocks )
791
+ Lm = SparseMatrixLinearOperator(W, W, L.tosparse().tocsr())
792
+
793
+ # Construct a BlockVector object containing x1 and x2
794
+ # |x1|
795
+ # X = | |
796
+ # |x2|
797
+
798
+ X = BlockVector(W)
799
+ X[0] = x1
800
+ X[1] = x2
801
+
802
+ # Compute BlockLinearOperator product
803
+ Y = L.dot(X)
804
+
805
+ Ym = Lm.dot(X)
806
+
807
+ # Check data in 1D array
808
+ assert np.allclose( Ym.toarray(), Y.toarray(), rtol=1e-12, atol=1e-12 )
809
+ #===============================================================================
810
+ @pytest.mark.parametrize( 'dtype', [float, complex] )
811
+ @pytest.mark.parametrize( 'n1', [8, 16] )
812
+ @pytest.mark.parametrize( 'n2', [8, 12] )
813
+ @pytest.mark.parametrize( 'p1', [1, 2] )
814
+ @pytest.mark.parametrize( 'p2', [1, 3] )
815
+ @pytest.mark.parametrize( 'P1', [True, False] )
816
+ @pytest.mark.parametrize( 'P2', [True] )
817
+
818
+ def test_block_2d_serial_array_to_psydac( dtype, n1, n2, p1, p2, P1, P2 ):
819
+ #Define a factor for the data
820
+ if dtype==complex:
821
+ factor=1j
822
+ else:
823
+ factor=1
824
+ # set seed for reproducibility
825
+ seed(n1*n2*p1*p2)
826
+
827
+ D = DomainDecomposition([n1,n2], periods=[P1,P2])
828
+
829
+ # Partition the points
830
+ npts = [n1,n2]
831
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
832
+
833
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
834
+
835
+ # Create vector spaces, and stencil vectors
836
+ V1 = StencilVectorSpace( cart ,dtype=dtype)
837
+ V2 = StencilVectorSpace( cart ,dtype=dtype)
838
+
839
+ W = BlockVectorSpace(V1, V2)
840
+ W2 = BlockVectorSpace(W, W)
841
+
842
+ x = BlockVector(W)
843
+ x2 = BlockVector(W2)
844
+
845
+ # Fill in vector with random values, then update ghost regions
846
+ for i1 in range(n1):
847
+ for i2 in range(n2):
848
+ x[0][i1,i2] = 2.0*factor*random() + 1.0
849
+ x[1][i1,i2] = 5.0*factor*random() - 1.0
850
+ x2[0][0][i1,i2] = 2.0*factor*random() + 1.0
851
+ x2[0][1][i1,i2] = 5.0*factor*random() - 1.0
852
+ x2[1][0][i1,i2] = 2.0*factor*random() + 1.0
853
+ x2[1][1][i1,i2] = 5.0*factor*random() - 1.0
854
+ x.update_ghost_regions()
855
+ x2.update_ghost_regions()
856
+
857
+ xa = x.toarray()
858
+ x2a = x2.toarray()
859
+ v = array_to_psydac(xa, W)
860
+ v2 = array_to_psydac(x2a, W2)
861
+
862
+ assert np.allclose( xa , v.toarray() )
863
+ assert np.allclose( x2a , v2.toarray() )
864
+
865
+ #===============================================================================
866
+ @pytest.mark.parametrize( 'dtype', [float] )
867
+ @pytest.mark.parametrize( 'n1', [8, 16] )
868
+ @pytest.mark.parametrize( 'n2', [8, 12] )
869
+ @pytest.mark.parametrize( 'p1', [1, 2] )
870
+ @pytest.mark.parametrize( 'p2', [1, 3] )
871
+ @pytest.mark.parametrize( 'P1', [True, False] )
872
+ @pytest.mark.parametrize( 'P2', [True] )
873
+ @pytest.mark.petsc
874
+
875
+ def test_block_vector_2d_serial_topetsc( dtype, n1, n2, p1, p2, P1, P2 ):
876
+ #Define a factor for the data
877
+ if dtype==complex:
878
+ factor=1j
879
+ else:
880
+ factor=1
881
+ # set seed for reproducibility
882
+ seed(n1*n2*p1*p2)
883
+
884
+ D = DomainDecomposition([n1,n2], periods=[P1,P2])
885
+
886
+ # Partition the points
887
+ npts = [n1,n2]
888
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
889
+
890
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
891
+
892
+ # Create vector spaces, and stencil vectors
893
+ V1 = StencilVectorSpace( cart ,dtype=dtype)
894
+ V2 = StencilVectorSpace( cart ,dtype=dtype)
895
+
896
+ W = BlockVectorSpace(V1, V2)
897
+
898
+ x = BlockVector(W)
899
+
900
+ # Fill in vector with random values, then update ghost regions
901
+ for i1 in range(n1):
902
+ for i2 in range(n2):
903
+ x[0][i1,i2] = 2.0*factor*random() + 1.0
904
+ x[1][i1,i2] = 5.0*factor*random() - 1.0
905
+
906
+ x.update_ghost_regions()
907
+
908
+ v = x.topetsc()
909
+ v = petsc_to_psydac(v, W)
910
+
911
+ # The vectors can only be compared in the serial case
912
+ assert np.allclose( x.toarray() , v.toarray() )
913
+
914
+ #===============================================================================
915
+ @pytest.mark.parametrize( 'dtype', [float] )
916
+ @pytest.mark.parametrize( 'n1', [8, 16] )
917
+ @pytest.mark.parametrize( 'n2', [8, 12] )
918
+ @pytest.mark.parametrize( 'p1', [1, 3] )
919
+ @pytest.mark.parametrize( 'p2', [1, 2] )
920
+ @pytest.mark.parametrize( 'P1', [True, False] )
921
+ @pytest.mark.parametrize( 'P2', [True] )
922
+ @pytest.mark.petsc
923
+
924
+ def test_block_linear_operator_2d_serial_topetsc( dtype, n1, n2, p1, p2, P1, P2 ):
925
+ # set seed for reproducibility
926
+ seed(n1*n2*p1*p2)
927
+
928
+ D = DomainDecomposition([n1,n2], periods=[P1,P2])
929
+
930
+ # Partition the points
931
+ npts = [n1,n2]
932
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
933
+
934
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
935
+
936
+ # Create vector spaces, stencil matrices, and stencil vectors
937
+ V = StencilVectorSpace( cart, dtype=dtype )
938
+ M1 = StencilMatrix( V, V)
939
+ M2 = StencilMatrix( V, V )
940
+ M3 = StencilMatrix( V, V )
941
+
942
+ # Fill in stencil matrices based on diagonal index
943
+ if dtype==complex:
944
+ f=lambda k1,k2: 10j*k1+k2
945
+ else:
946
+ f=lambda k1,k2: 10*k1+k2
947
+
948
+ for k1 in range(-p1,p1+1):
949
+ for k2 in range(-p2,p2+1):
950
+ M1[:,:,k1,k2] = f(k1,k2)
951
+ M2[:,:,k1,k2] = f(k1,k2)+2.
952
+ M3[:,:,k1,k2] = f(k1,k2)+5.
953
+
954
+ M1.remove_spurious_entries()
955
+ M2.remove_spurious_entries()
956
+ M3.remove_spurious_entries()
957
+
958
+ W = BlockVectorSpace(V, V)
959
+
960
+ # Construct a BlockLinearOperator object containing M1, M2, M, using 3 ways
961
+ # |M1 M2|
962
+ # L = | |
963
+ # |M3 0 |
964
+
965
+ dict_blocks = {(0,0):M1, (0,1):M2, (1,0):M3}
966
+
967
+ L = BlockLinearOperator( W, W, blocks=dict_blocks )
968
+
969
+ Lp = L.topetsc()
970
+ indptr, indices, data = Lp.getValuesCSR()
971
+ if dtype == float:
972
+ data = data.real #PETSc with installation complex configuration only handles complex dtype
973
+ Lp = csr_matrix((data, indices, indptr), shape=Lp.size)
974
+ L = L.tosparse().tocsr()
975
+
976
+ # The operators can only be compared in the serial case
977
+ assert (L-Lp).data.size == 0
978
+
979
+ #===============================================================================
980
+ @pytest.mark.parametrize( 'dtype', [float] )
981
+ @pytest.mark.parametrize( 'n1', [8, 16] )
982
+ @pytest.mark.parametrize( 'n2', [8, 32] )
983
+ @pytest.mark.parametrize( 'p1', [1, 3] )
984
+ @pytest.mark.parametrize( 'p2', [2] )
985
+ @pytest.mark.parametrize( 'P1', [True, False] )
986
+ @pytest.mark.parametrize( 'P2', [True] )
987
+ @pytest.mark.parametrize( 'backend', [None, PSYDAC_BACKEND_GPYCCEL] )
988
+
989
+ def test_block_linear_operator_dot_backend( dtype, n1, n2, p1, p2, P1, P2, backend ):
990
+ # Define a factor for the data
991
+ if dtype == complex:
992
+ factor = 1j
993
+ else:
994
+ factor = 1
995
+
996
+ D = DomainDecomposition([n1,n2], periods=[P1,P2])
997
+
998
+ # Partition the points
999
+ npts = [n1,n2]
1000
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
1001
+
1002
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
1003
+
1004
+ # Create vector space, stencil matrix, and stencil vector
1005
+ V = StencilVectorSpace( cart, dtype=dtype )
1006
+
1007
+ M1 = StencilMatrix( V, V , backend=backend)
1008
+ M2 = StencilMatrix( V, V , backend=backend)
1009
+ M3 = StencilMatrix( V, V , backend=backend)
1010
+ M4 = StencilMatrix( V, V , backend=backend)
1011
+ x1 = StencilVector( V )
1012
+ x2 = StencilVector( V )
1013
+
1014
+ s1,s2 = V.starts
1015
+ e1,e2 = V.ends
1016
+
1017
+ # Fill in stencil matrix values based on diagonal index (periodic!)
1018
+ for k1 in range(-p1,p1+1):
1019
+ for k2 in range(-p2,p2+1):
1020
+ M1[:,:,k1,k2] = factor*k1+k2+10.
1021
+ M2[:,:,k1,k2] = factor*2.*k1+k2
1022
+ M3[:,:,k1,k2] = factor*5*k1+k2
1023
+ M4[:,:,k1,k2] = factor*10*k1+k2
1024
+
1025
+ # If any dimension is not periodic, set corresponding periodic corners to zero
1026
+ M1.remove_spurious_entries()
1027
+ M2.remove_spurious_entries()
1028
+ M3.remove_spurious_entries()
1029
+ M4.remove_spurious_entries()
1030
+
1031
+ # Fill in vector with random values, then update ghost regions
1032
+ for i1 in range(s1,e1+1):
1033
+ for i2 in range(s2,e2+1):
1034
+ x1[i1,i2] = 2.0*factor * i1 + i2
1035
+ x2[i1,i2] = 5.0*factor * i2 - i1
1036
+ x1.update_ghost_regions()
1037
+ x2.update_ghost_regions()
1038
+
1039
+ # Create and Fill Block objects
1040
+ W = BlockVectorSpace(V, V)
1041
+ L = BlockLinearOperator( W, W )
1042
+ L[0,0] = M1
1043
+ L[0,1] = M2
1044
+ L[1,0] = M3
1045
+ L[1,1] = M4
1046
+
1047
+ # L.set_backend(PSYDAC_BACKEND_GPYCCEL)
1048
+
1049
+ # X = BlockVector(W)
1050
+ # X[0] = x1
1051
+ # X[1] = x2
1052
+
1053
+ # # Compute Block-vector product
1054
+ # Y = L.dot(X)
1055
+
1056
+ # # Compute matrix-vector products for each block
1057
+ # y1 = M1.dot(x1) + M2.dot(x2)
1058
+ # y2 = M3.dot(x1) + M4.dot(x2)
1059
+
1060
+ # # Check data in 1D array
1061
+ # assert np.allclose( Y.blocks[0].toarray(), y1.toarray(), rtol=1e-13, atol=1e-13 )
1062
+ # assert np.allclose( Y.blocks[1].toarray(), y2.toarray(), rtol=1e-13, atol=1e-13 )
1063
+ #===============================================================================
1064
+ # PARALLEL TESTS
1065
+ #===============================================================================
1066
+ @pytest.mark.parametrize( 'dtype', [float] )
1067
+ @pytest.mark.parametrize( 'n1', [8, 16] )
1068
+ @pytest.mark.parametrize( 'n2', [8, 32] )
1069
+ @pytest.mark.parametrize( 'p1', [1, 3] )
1070
+ @pytest.mark.parametrize( 'p2', [2] )
1071
+ @pytest.mark.parametrize( 'P1', [True, False] )
1072
+ @pytest.mark.parametrize( 'P2', [True] )
1073
+ @pytest.mark.parallel
1074
+
1075
+ def test_block_linear_operator_parallel_dot( dtype, n1, n2, p1, p2, P1, P2 ):
1076
+ # Define a factor for the data
1077
+ if dtype == complex:
1078
+ factor = 1j
1079
+ else:
1080
+ factor = 1
1081
+
1082
+ # set seed for reproducibility
1083
+ seed(n1*n2*p1*p2)
1084
+
1085
+ comm = MPI.COMM_WORLD
1086
+ D = DomainDecomposition([n1,n2], periods=[P1,P2], comm=comm)
1087
+
1088
+ # Partition the points
1089
+ npts = [n1,n2]
1090
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
1091
+
1092
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
1093
+
1094
+ # Create vector space, stencil matrix, and stencil vector
1095
+ V = StencilVectorSpace( cart, dtype=dtype )
1096
+ M1 = StencilMatrix( V, V )
1097
+ M2 = StencilMatrix( V, V )
1098
+ M3 = StencilMatrix( V, V )
1099
+ M4 = StencilMatrix( V, V )
1100
+ x1 = StencilVector( V )
1101
+ x2 = StencilVector( V )
1102
+
1103
+ s1,s2 = V.starts
1104
+ e1,e2 = V.ends
1105
+
1106
+ # Fill in stencil matrix values based on diagonal index (periodic!)
1107
+ for k1 in range(-p1,p1+1):
1108
+ for k2 in range(-p2,p2+1):
1109
+ M1[:,:,k1,k2] = factor*k1+k2+10.
1110
+ M2[:,:,k1,k2] = factor*2.*k1+k2
1111
+ M3[:,:,k1,k2] = factor*5*k1+k2
1112
+ M4[:,:,k1,k2] = factor*10*k1+k2
1113
+
1114
+ # If any dimension is not periodic, set corresponding periodic corners to zero
1115
+ M1.remove_spurious_entries()
1116
+ M2.remove_spurious_entries()
1117
+ M3.remove_spurious_entries()
1118
+ M4.remove_spurious_entries()
1119
+
1120
+ # Fill in vector with random values, then update ghost regions
1121
+ for i1 in range(s1,e1+1):
1122
+ for i2 in range(s2,e2+1):
1123
+ x1[i1,i2] = 2.0*factor*random() + 1.0
1124
+ x2[i1,i2] = 5.0*factor*random() - 1.0
1125
+ x1.update_ghost_regions()
1126
+ x2.update_ghost_regions()
1127
+
1128
+ # Create and Fill Block objects
1129
+ W = BlockVectorSpace(V, V)
1130
+ L = BlockLinearOperator( W, W )
1131
+ L[0,0] = M1
1132
+ L[0,1] = M2
1133
+ L[1,0] = M3
1134
+ L[1,1] = M4
1135
+
1136
+ X = BlockVector(W)
1137
+ X[0] = x1
1138
+ X[1] = x2
1139
+
1140
+ # Compute Block-vector product
1141
+ Y = L.dot(X)
1142
+
1143
+ # Compute matrix-vector products for each block
1144
+ y1 = M1.dot(x1) + M2.dot(x2)
1145
+ y2 = M3.dot(x1) + M4.dot(x2)
1146
+
1147
+ # Check data in 1D array
1148
+ assert np.allclose( Y.blocks[0].toarray(), y1.toarray(), rtol=1e-14, atol=1e-14 )
1149
+ assert np.allclose( Y.blocks[1].toarray(), y2.toarray(), rtol=1e-14, atol=1e-14 )
1150
+
1151
+ # Test copy with an out
1152
+ # Create random matrix
1153
+ N1 = StencilMatrix( V, V )
1154
+ N2 = StencilMatrix( V, V )
1155
+ N3 = StencilMatrix( V, V )
1156
+ N4 = StencilMatrix( V, V )
1157
+
1158
+ for k1 in range(-p1,p1+1):
1159
+ for k2 in range(-p2,p2+1):
1160
+ N1[:,:,k1,k2] = factor*random()
1161
+ N2[:,:,k1,k2] = factor*random()
1162
+ N3[:,:,k1,k2] = factor*random()
1163
+ N4[:,:,k1,k2] = factor*random()
1164
+
1165
+ K = BlockLinearOperator( W, W )
1166
+ N = BlockLinearOperator( W, W )
1167
+
1168
+
1169
+ K[0,0] = N1
1170
+ K[0,1] = N2
1171
+ K[1,0] = N3
1172
+ K[1,1] = N4
1173
+
1174
+ #replace the random entries to check they are really overwritten
1175
+ K.copy(out=N)
1176
+ L.copy(out=K)
1177
+
1178
+ # Compute Block-vector product
1179
+ K.dot(X, out= Y)
1180
+
1181
+ # Check data in 1D array
1182
+ assert np.allclose( Y.blocks[0].toarray(), y1.toarray(), rtol=1e-14, atol=1e-14 )
1183
+ assert np.allclose( Y.blocks[1].toarray(), y2.toarray(), rtol=1e-14, atol=1e-14 )
1184
+
1185
+ # Test transpose with an out, check that we overwrite the random entries
1186
+ L.transpose(out = N)
1187
+
1188
+ # Compute Block-vector product
1189
+ Z = N.dot(X)
1190
+
1191
+ # Compute matrix-vector products for each block
1192
+ y1 = M1.T.dot(x1) + M3.T.dot(x2)
1193
+ y2 = M2.T.dot(x1) + M4.T.dot(x2)
1194
+
1195
+ # Check data in 1D array
1196
+ assert np.allclose( Z.blocks[0].toarray(), y1.toarray(), rtol=1e-14, atol=1e-14 )
1197
+ assert np.allclose( Z.blocks[1].toarray(), y2.toarray(), rtol=1e-14, atol=1e-14 )
1198
+
1199
+ # ===============================================================================
1200
+ @pytest.mark.parametrize('dtype', [float])
1201
+ @pytest.mark.parametrize('n1', [10, 17])
1202
+ @pytest.mark.parametrize('n2', [13, 7])
1203
+ @pytest.mark.parametrize('p1', [1, 2])
1204
+ @pytest.mark.parametrize('p2', [1])
1205
+ @pytest.mark.parametrize('s1', [1, 2])
1206
+ @pytest.mark.parametrize('s2', [1])
1207
+ @pytest.mark.parametrize('P1', [True, False])
1208
+ @pytest.mark.parametrize('P2', [True])
1209
+ @pytest.mark.parallel
1210
+
1211
+ def test_block_vector_2d_parallel_array_to_psydac(dtype, n1, n2, p1, p2, s1, s2, P1, P2):
1212
+ npts = [n1, n2]
1213
+
1214
+ comm = MPI.COMM_WORLD
1215
+
1216
+ # Create domain decomposition
1217
+ D = DomainDecomposition(npts, periods=[P1, P2], comm=comm)
1218
+
1219
+ # Partition the points
1220
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
1221
+ C = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1, p2], shifts=[s1, s2])
1222
+
1223
+ # Create Vector spaces and Vectors
1224
+ V = StencilVectorSpace(C, dtype=dtype)
1225
+ W = BlockVectorSpace(V, V)
1226
+ W2 = BlockVectorSpace(W, W, V)
1227
+ x = W.zeros()
1228
+ x2 = W2.zeros()
1229
+
1230
+ # Fill the vector with data
1231
+ if dtype == complex:
1232
+ f = lambda i1, i2: 10j * i1 + i2
1233
+ else:
1234
+ f = lambda i1, i2: 10 * i1 + i2
1235
+ for i1 in range(V.starts[0], V.ends[0]+1):
1236
+ for i2 in range(V.starts[1], V.ends[1]+1):
1237
+ x[0][i1, i2] = f(i1, i2)
1238
+ x[1][i1, i2] = -13*f(i1, i2)
1239
+ x2[0] = x
1240
+ x2[1] = 2*x
1241
+ x2[2][i1, i2] = 7*f(i1, i2)
1242
+
1243
+ x.update_ghost_regions()
1244
+ x2.update_ghost_regions()
1245
+
1246
+ # Convert vectors to arrays
1247
+ xa = x.toarray()
1248
+ x2a = x2.toarray()
1249
+
1250
+ # Apply array_to_psydac as left inverse of toarray
1251
+ w = array_to_psydac(xa, W)
1252
+ w2 = array_to_psydac(x2a, W2)
1253
+
1254
+
1255
+ # Apply array_to_psydac first, and toarray next
1256
+ xa_r_inv = np.array(np.random.rand(xa.size), dtype=dtype)*xa # the vector must be distributed as xa
1257
+ x_r_inv = array_to_psydac(xa_r_inv, W)
1258
+ x_r_inv.update_ghost_regions()
1259
+ va_r_inv = x_r_inv.toarray()
1260
+
1261
+ x2a_r_inv = np.array(np.random.rand(x2a.size), dtype=dtype)*x2a # the vector must be distributed as xa
1262
+ x2_r_inv = array_to_psydac(x2a_r_inv, W2)
1263
+ x2_r_inv.update_ghost_regions()
1264
+ v2a_r_inv = x2_r_inv.toarray()
1265
+
1266
+ ## Check that array_to_psydac is the inverse of .toarray():
1267
+ # left inverse:
1268
+ assert isinstance(w, BlockVector)
1269
+ assert w.space is W
1270
+ assert isinstance(w2, BlockVector)
1271
+ assert w2.space is W2
1272
+ for i in range(2):
1273
+ assert np.array_equal(x[i]._data, w[i]._data)
1274
+ for j in range(2):
1275
+ assert np.array_equal(x2[i][j]._data, w2[i][j]._data)
1276
+ assert np.array_equal(x2[i][j]._data, w2[i][j]._data)
1277
+
1278
+ assert np.array_equal(x2[2]._data, w2[2]._data)
1279
+
1280
+ # right inverse:
1281
+ assert np.array_equal(xa_r_inv, va_r_inv)
1282
+ assert np.array_equal(x2a_r_inv, v2a_r_inv)
1283
+
1284
+ #===============================================================================
1285
+ @pytest.mark.parametrize( 'dtype', [float] )
1286
+ @pytest.mark.parametrize( 'n1', [8, 16] )
1287
+ @pytest.mark.parametrize( 'n2', [8, 12] )
1288
+ @pytest.mark.parametrize( 'p1', [1, 2] )
1289
+ @pytest.mark.parametrize( 'p2', [1, 3] )
1290
+ @pytest.mark.parametrize( 'P1', [True, False] )
1291
+ @pytest.mark.parametrize( 'P2', [True] )
1292
+ @pytest.mark.parallel
1293
+ @pytest.mark.petsc
1294
+
1295
+ def test_block_vector_2d_parallel_topetsc( dtype, n1, n2, p1, p2, P1, P2 ):
1296
+ #Define a factor for the data
1297
+ if dtype==complex:
1298
+ factor=1j
1299
+ else:
1300
+ factor=1
1301
+ # set seed for reproducibility
1302
+ seed(n1*n2*p1*p2)
1303
+
1304
+ comm = MPI.COMM_WORLD
1305
+
1306
+ D = DomainDecomposition([n1,n2], periods=[P1,P2], comm=comm)
1307
+
1308
+ # Partition the points
1309
+ npts = [n1,n2]
1310
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
1311
+
1312
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
1313
+
1314
+ D2 = DomainDecomposition([n1+1,n2+1], periods=[P1,P2], comm=comm)
1315
+ npts2 = [n1+1,n2+1]
1316
+ global_starts2, global_ends2 = compute_global_starts_ends(D2, npts2)
1317
+ cart2 = CartDecomposition(D2, npts2, global_starts2, global_ends2, pads=[p1+1,p2+1], shifts=[1,1])
1318
+
1319
+ # Create vector spaces, and stencil vectors
1320
+ V1 = StencilVectorSpace( cart ,dtype=dtype)
1321
+ V2 = StencilVectorSpace( cart2 ,dtype=dtype)
1322
+
1323
+ W = BlockVectorSpace(V1, V2)
1324
+ #TODO: implement conversion to PETSc recursively to treat case of block of blocks
1325
+
1326
+ x = BlockVector(W)
1327
+
1328
+ # Fill in vector with random values, then update ghost regions
1329
+ for i0 in range(len(W.starts)):
1330
+ for i1 in range(W.starts[i0][0], W.ends[i0][0] + 1):
1331
+ for i2 in range(W.starts[i0][1], W.ends[i0][1] + 1):
1332
+ x[i0][i1,i2] = 2.0*factor*random() + 1.0
1333
+
1334
+ x.update_ghost_regions()
1335
+
1336
+ v = petsc_to_psydac(x.topetsc(), W)
1337
+
1338
+ assert np.allclose( x.toarray() , v.toarray(), rtol=1e-12, atol=1e-12 )
1339
+
1340
+ #===============================================================================
1341
+ @pytest.mark.parametrize( 'dtype', [float] )
1342
+ @pytest.mark.parametrize( 'n1', [8, 16] )
1343
+ @pytest.mark.parametrize( 'p1', [1, 2] )
1344
+ @pytest.mark.parametrize( 'P1', [True, False] )
1345
+ @pytest.mark.parallel
1346
+ @pytest.mark.petsc
1347
+
1348
+ def test_block_linear_operator_1d_parallel_topetsc( dtype, n1, p1, P1):
1349
+ # set seed for reproducibility
1350
+ seed(n1*p1)
1351
+
1352
+ D = DomainDecomposition([n1], periods=[P1], comm=MPI.COMM_WORLD)
1353
+
1354
+ # Partition the points
1355
+ npts = [n1]
1356
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
1357
+
1358
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1], shifts=[1])
1359
+
1360
+ # Create vector spaces, stencil matrices, and stencil vectors
1361
+ V = StencilVectorSpace( cart, dtype=dtype )
1362
+ M1 = StencilMatrix( V, V )
1363
+ M2 = StencilMatrix( V, V )
1364
+
1365
+ # Fill in stencil matrices based on diagonal index
1366
+ if dtype==complex:
1367
+ f=lambda k1: 10j*k1
1368
+ else:
1369
+ f=lambda k1: 10*k1
1370
+
1371
+ for k1 in range(-p1, p1+1):
1372
+ M1[:,k1] = f(k1)
1373
+ M2[:,k1] = f(k1)+2.
1374
+
1375
+ M1.remove_spurious_entries()
1376
+ M2.remove_spurious_entries()
1377
+
1378
+ W = BlockVectorSpace(V, V)
1379
+
1380
+ # Construct a BlockLinearOperator object containing M1, M2, M3:
1381
+ # |M1 M2|
1382
+ # L = | |
1383
+ # |M3 0 |
1384
+
1385
+ dict_blocks = {(0,0):M1, (0,1):M2}
1386
+
1387
+ L = BlockLinearOperator( W, V, blocks=dict_blocks )
1388
+ x = BlockVector(W)
1389
+
1390
+ # Fill in vector with random values, then update ghost regions
1391
+ for i0 in range(len(W.starts)):
1392
+ for i1 in range(W.starts[i0][0], W.ends[i0][0] + 1):
1393
+ x[i0][i1] = 2.0*random() + (1j if dtype==complex else 1.)
1394
+ x.update_ghost_regions()
1395
+
1396
+ y = L.dot(x)
1397
+
1398
+ # Cast operator to PETSc Mat format
1399
+ Lp = L.topetsc()
1400
+
1401
+ # Create Vec to allocate the result of the dot product
1402
+ y_petsc = Lp.createVecLeft()
1403
+ # Compute dot product
1404
+ Lp.mult(x.topetsc(), y_petsc)
1405
+ # Cast result back to Psydac BlockVector format
1406
+ y_p = petsc_to_psydac(y_petsc, V)
1407
+
1408
+ assert np.allclose(y_p.toarray(), y.toarray(), rtol=1e-12, atol=1e-12)
1409
+
1410
+ #===============================================================================
1411
+ @pytest.mark.parametrize( 'dtype', [float] )
1412
+ @pytest.mark.parametrize( 'n1', [8, 16] )
1413
+ @pytest.mark.parametrize( 'n2', [8, 12] )
1414
+ @pytest.mark.parametrize( 'p1', [1, 2] )
1415
+ @pytest.mark.parametrize( 'p2', [1, 3] )
1416
+ @pytest.mark.parametrize( 'P1', [True, False] )
1417
+ @pytest.mark.parametrize( 'P2', [True] )
1418
+ @pytest.mark.parallel
1419
+ @pytest.mark.petsc
1420
+
1421
+ def test_block_linear_operator_2d_parallel_topetsc( dtype, n1, n2, p1, p2, P1, P2):
1422
+ # set seed for reproducibility
1423
+ seed(n1*n2*p1*p2)
1424
+ comm = MPI.COMM_WORLD
1425
+
1426
+ D = DomainDecomposition([n1,n2], periods=[P1,P2], comm=comm)
1427
+
1428
+ # Partition the points
1429
+ npts = [n1,n2]
1430
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
1431
+
1432
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
1433
+
1434
+ # Create vector spaces, stencil matrices, and stencil vectors
1435
+ V = StencilVectorSpace( cart, dtype=dtype )
1436
+ M1 = StencilMatrix( V, V )
1437
+ M2 = StencilMatrix( V, V )
1438
+ M3 = StencilMatrix( V, V )
1439
+
1440
+ # Fill in stencil matrices based on diagonal index
1441
+ if dtype==complex:
1442
+ f=lambda k1,k2: 10j*k1+k2
1443
+ else:
1444
+ f=lambda k1,k2: 10*k1+k2
1445
+
1446
+ for k1 in range(-p1,p1+1):
1447
+ for k2 in range(-p2,p2+1):
1448
+ M1[:,:,k1,k2] = f(k1,k2)
1449
+ M2[:,:,k1,k2] = f(k1,k2)+2.
1450
+ M3[:,:,k1,k2] = -f(k1,k2)+1.
1451
+
1452
+ M1.remove_spurious_entries()
1453
+ M2.remove_spurious_entries()
1454
+ M3.remove_spurious_entries()
1455
+
1456
+ W = BlockVectorSpace(V, V)
1457
+
1458
+ # Construct a BlockLinearOperator object containing M1, M2, M3:
1459
+ #
1460
+ # L = |M1 M2|
1461
+ # |M3 0 |
1462
+
1463
+ dict_blocks = {(0,0):M1, (0,1):M2, (1,0):M3}
1464
+
1465
+ L = BlockLinearOperator( W, W, blocks=dict_blocks )
1466
+ x = BlockVector(W)
1467
+
1468
+ # Fill in vector with random values, then update ghost regions
1469
+ for i0 in range(len(W.starts)):
1470
+ for i1 in range(W.starts[i0][0], W.ends[i0][0] + 1):
1471
+ for i2 in range(W.starts[i0][1], W.ends[i0][1] + 1):
1472
+ x[i0][i1,i2] = 2.0*random() + (1j if dtype==complex else 1.)
1473
+ x.update_ghost_regions()
1474
+
1475
+ y = L.dot(x)
1476
+
1477
+ # Cast operator to PETSc Mat format
1478
+ Lp = L.topetsc()
1479
+
1480
+ # Create Vec to allocate the result of the dot product
1481
+ y_petsc = Lp.createVecLeft()
1482
+ # Compute dot product
1483
+ Lp.mult(x.topetsc(), y_petsc)
1484
+ # Cast result back to Psydac BlockVector format
1485
+ y_p = petsc_to_psydac(y_petsc, L.codomain)
1486
+
1487
+ assert np.allclose(y_p.toarray(), y.toarray(), rtol=1e-12, atol=1e-12)
1488
+
1489
+ #===============================================================================
1490
+
1491
+ @pytest.mark.parametrize( 'dtype', [float] )
1492
+ @pytest.mark.parametrize( 'n1', [8, 16] )
1493
+ @pytest.mark.parametrize( 'n2', [8, 32] )
1494
+ @pytest.mark.parametrize( 'p1', [1, 3] )
1495
+ @pytest.mark.parametrize( 'p2', [2] )
1496
+ @pytest.mark.parametrize( 'P1', [True, False] )
1497
+ @pytest.mark.parametrize( 'P2', [True] )
1498
+ @pytest.mark.parallel
1499
+
1500
+ def test_block_matrix_operator_parallel_dot_backend( dtype, n1, n2, p1, p2, P1, P2 ):
1501
+ # Define a factor for the data
1502
+ if dtype == complex:
1503
+ factor = 1j
1504
+ else:
1505
+ factor = 1
1506
+ # set seed for reproducibility
1507
+
1508
+ comm = MPI.COMM_WORLD
1509
+ D = DomainDecomposition([n1,n2], periods=[P1,P2], comm=comm)
1510
+
1511
+ # Partition the points
1512
+ npts = [n1,n2]
1513
+ global_starts, global_ends = compute_global_starts_ends(D, npts)
1514
+
1515
+ cart = CartDecomposition(D, npts, global_starts, global_ends, pads=[p1,p2], shifts=[1,1])
1516
+
1517
+ # Create vector space, stencil matrix, and stencil vector
1518
+ V = StencilVectorSpace( cart, dtype=dtype)
1519
+ M1 = StencilMatrix( V, V , backend=PSYDAC_BACKEND_GPYCCEL)
1520
+ M2 = StencilMatrix( V, V , backend=PSYDAC_BACKEND_GPYCCEL)
1521
+ M3 = StencilMatrix( V, V , backend=PSYDAC_BACKEND_GPYCCEL)
1522
+ M4 = StencilMatrix( V, V , backend=PSYDAC_BACKEND_GPYCCEL)
1523
+ x1 = StencilVector( V )
1524
+ x2 = StencilVector( V )
1525
+
1526
+ s1,s2 = V.starts
1527
+ e1,e2 = V.ends
1528
+
1529
+ # Fill in stencil matrix values based on diagonal index (periodic!)
1530
+ for k1 in range(-p1,p1+1):
1531
+ for k2 in range(-p2,p2+1):
1532
+ M1[:,:,k1,k2] = k1*factor+k2+10.
1533
+ M2[:,:,k1,k2] = 2.*factor*k1+k2
1534
+ M3[:,:,k1,k2] = 5*factor*k1+k2
1535
+ M4[:,:,k1,k2] = 10*factor*k1+k2
1536
+
1537
+ # If any dimension is not periodic, set corresponding periodic corners to zero
1538
+ M1.remove_spurious_entries()
1539
+ M2.remove_spurious_entries()
1540
+ M3.remove_spurious_entries()
1541
+ M4.remove_spurious_entries()
1542
+
1543
+ # Fill in vector with random values, then update ghost regions
1544
+ for i1 in range(s1,e1+1):
1545
+ for i2 in range(s2,e2+1):
1546
+ x1[i1,i2] = 2.0*factor*random() + 1.0
1547
+ x2[i1,i2] = 5.0*factor*random() - 1.0
1548
+ x1.update_ghost_regions()
1549
+ x2.update_ghost_regions()
1550
+
1551
+ # Create and Fill Block objects
1552
+ W = BlockVectorSpace(V, V)
1553
+ L = BlockLinearOperator( W, W )
1554
+ L[0,0] = M1
1555
+ L[0,1] = M2
1556
+ L[1,0] = M3
1557
+ L[1,1] = M4
1558
+
1559
+ # L.set_backend(PSYDAC_BACKEND_GPYCCEL)
1560
+
1561
+ # X = BlockVector(W)
1562
+ # X[0] = x1
1563
+ # X[1] = x2
1564
+
1565
+ # # Compute Block-vector product
1566
+ # Y = L.dot(X)
1567
+
1568
+ # # Compute matrix-vector products for each block
1569
+ # y1 = M1.dot(x1) + M2.dot(x2)
1570
+ # y2 = M3.dot(x1) + M4.dot(x2)
1571
+
1572
+ # #Test axpy method in parallel
1573
+ # z3 = X + 5 * factor * Y
1574
+ # X.mul_iadd(5 * factor, Y)
1575
+
1576
+ # # Test exact value and symetry of the scalar product
1577
+ # assert np.allclose(X[0]._data, z3[0]._data)
1578
+
1579
+ # # Check data in 1D array
1580
+ # assert np.allclose( Y.blocks[0].toarray(), y1.toarray(), rtol=1e-13, atol=1e-13 )
1581
+ # assert np.allclose( Y.blocks[1].toarray(), y2.toarray(), rtol=1e-13, atol=1e-13 )
1582
+
1583
+ #===============================================================================
1584
+ # SCRIPT FUNCTIONALITY
1585
+ #===============================================================================
1586
+ if __name__ == "__main__":
1587
+ import sys
1588
+ pytest.main( sys.argv )