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,128 @@
1
+ import pytest
2
+ import numpy as np
3
+
4
+ from feectools.linalg.block import BlockLinearOperator, BlockVector, BlockVectorSpace
5
+ from feectools.linalg.basic import LinearOperator, ZeroOperator, IdentityOperator, ComposedLinearOperator, SumLinearOperator, PowerLinearOperator, ScaledLinearOperator
6
+ from feectools.linalg.basic import MatrixFreeLinearOperator
7
+ from feectools.linalg.stencil import StencilVectorSpace, StencilVector, StencilMatrix
8
+ from feectools.linalg.solvers import ConjugateGradient, inverse
9
+ from feectools.ddm.cart import DomainDecomposition, CartDecomposition
10
+
11
+ from feectools.linalg.tests.test_linalg import get_StencilVectorSpace, get_positive_definite_StencilMatrix, assert_pos_def
12
+
13
+ def get_random_StencilMatrix(domain, codomain):
14
+
15
+ np.random.seed(2)
16
+ V = domain
17
+ W = codomain
18
+ assert isinstance(V, StencilVectorSpace)
19
+ assert isinstance(W, StencilVectorSpace)
20
+ [n1, n2] = V._npts
21
+ [p1, p2] = V._pads
22
+ [P1, P2] = V._periods
23
+ assert (P1 == False) and (P2 == False)
24
+
25
+ [m1, m2] = W._npts
26
+ [q1, q2] = W._pads
27
+ [Q1, Q2] = W._periods
28
+ assert (Q1 == False) and (Q2 == False)
29
+
30
+ S = StencilMatrix(V, W)
31
+
32
+ for i in range(0, q1+1):
33
+ if i != 0:
34
+ for j in range(-q2, q2+1):
35
+ S[:, :, i, j] = 2*np.random.random()-1
36
+ else:
37
+ for j in range(1, q2+1):
38
+ S[:, :, i, j] = 2*np.random.random()-1
39
+ S.remove_spurious_entries()
40
+
41
+ return S
42
+
43
+ def get_random_StencilVector(V):
44
+ np.random.seed(3)
45
+ assert isinstance(V, StencilVectorSpace)
46
+ [n1, n2] = V._npts
47
+ v = StencilVector(V)
48
+ for i in range(n1):
49
+ for j in range(n2):
50
+ v[i,j] = np.random.random()
51
+ return v
52
+
53
+ #===============================================================================
54
+ @pytest.mark.parametrize('n1', [6, 5])
55
+ @pytest.mark.parametrize('n2', [6, 8])
56
+ @pytest.mark.parametrize('p1', [1, 2])
57
+ @pytest.mark.parametrize('p2', [2, 3])
58
+
59
+ def test_fake_matrix_free(n1, n2, p1, p2):
60
+ P1 = False
61
+ P2 = False
62
+ m1 = (n2+n1)//2
63
+ m2 = n1+1
64
+ q1 = p1 # using same degrees because both spaces must have same padding for now
65
+ q2 = p2
66
+ V1 = get_StencilVectorSpace([n1, n2], [p1, p2], [P1, P2])
67
+ V2 = get_StencilVectorSpace([m1, m2], [q1, q2], [P1, P2])
68
+ S = get_random_StencilMatrix(codomain=V2, domain=V1)
69
+ O = MatrixFreeLinearOperator(codomain=V2, domain=V1, dot=lambda v: S @ v)
70
+
71
+ print(f'O.domain = {O.domain}')
72
+ print(f'S.domain = {S.domain}')
73
+ print(f'V1: = {V1}')
74
+ v = get_random_StencilVector(V1)
75
+ tol = 1e-10
76
+ y = S.dot(v)
77
+ x = O.dot(v)
78
+ print(f'error = {np.linalg.norm( (x - y).toarray() )}')
79
+ assert np.linalg.norm( (x - y).toarray() ) < tol
80
+ O.dot(v, out=x)
81
+ print(f'error = {np.linalg.norm( (x - y).toarray() )}')
82
+ assert np.linalg.norm( (x - y).toarray() ) < tol
83
+
84
+ @pytest.mark.parametrize('solver', ['cg', 'pcg', 'bicg', 'minres', 'lsmr'])
85
+
86
+ def test_solvers_matrix_free(solver):
87
+ print(f'solver = {solver}')
88
+ n1 = 4
89
+ n2 = 3
90
+ p1 = 5
91
+ p2 = 2
92
+ P1 = False
93
+ P2 = False
94
+ V = get_StencilVectorSpace([n1, n2], [p1, p2], [P1, P2])
95
+ A_SM = get_positive_definite_StencilMatrix(V)
96
+ assert_pos_def(A_SM)
97
+ AT_SM = A_SM.transpose()
98
+ A = MatrixFreeLinearOperator(domain=V, codomain=V, dot=lambda v: A_SM @ v, dot_transpose=lambda v: AT_SM @ v)
99
+
100
+ # get rhs and solution
101
+ b = get_random_StencilVector(V)
102
+ x = A.dot(b)
103
+
104
+ # Create Inverse with A
105
+ tol = 3e-5
106
+ if solver == 'pcg':
107
+ inv_diagonal = A_SM.diagonal(inverse=True)
108
+ A_inv = inverse(A, solver, pc=inv_diagonal, tol=tol)
109
+ else:
110
+ A_inv = inverse(A, solver, tol=tol)
111
+
112
+ AA = A_inv._A
113
+ xx = AA.dot(b)
114
+ print(f'norm(xx) = {np.linalg.norm( xx.toarray() )}')
115
+ print(f'norm(x) = {np.linalg.norm( x.toarray() )}')
116
+
117
+ # Apply inverse and check
118
+ y = A_inv @ x
119
+ error = np.linalg.norm( (b - y).toarray())
120
+ assert np.linalg.norm( (b - y).toarray() ) < tol
121
+
122
+ #===============================================================================
123
+ # SCRIPT FUNCTIONALITY
124
+ #===============================================================================
125
+ if __name__ == "__main__":
126
+ # import sys
127
+ # pytest.main( sys.argv )
128
+ test_fake_matrix_free(5, 4, 1, 2)
@@ -0,0 +1,213 @@
1
+
2
+ import numpy as np
3
+ import pytest
4
+ from feectools.linalg.solvers import inverse
5
+ from feectools.linalg.stencil import StencilVectorSpace, StencilMatrix, StencilVector
6
+ from feectools.linalg.basic import LinearSolver
7
+ from feectools.ddm.cart import DomainDecomposition, CartDecomposition
8
+
9
+
10
+ def define_data_hermitian(n, p, dtype=float):
11
+ domain_decomposition = DomainDecomposition([n - p], [False])
12
+ cart = CartDecomposition(domain_decomposition, [n], [np.array([0])], [np.array([n - 1])], [p], [1])
13
+ # ... Vector Spaces
14
+ V = StencilVectorSpace(cart,dtype=dtype)
15
+ e = V.ends[0]
16
+ s = V.starts[0]
17
+
18
+ # Build banded matrix with 2p+1 diagonals: must be symmetric and positive definite
19
+ # Here we assign value 2*p on main diagonal and -1 on other diagonals
20
+ if dtype==complex:
21
+ factor=1+1j
22
+ else:
23
+ factor=1
24
+ A = StencilMatrix(V, V)
25
+ A[:, -p:0] = 1-1*factor
26
+ A[:, 0:1] = 2 * p
27
+ A[:, 1:p + 1] = 1-1*factor.conjugate()
28
+ A.remove_spurious_entries()
29
+
30
+ # Build exact solution
31
+ xe = StencilVector(V)
32
+ xe[s:e + 1] = factor*np.random.random(e + 1 - s)
33
+ return(V, A, xe)
34
+
35
+ def define_data(n, p, matrix_data, dtype=float):
36
+ domain_decomposition = DomainDecomposition([n - p], [False])
37
+ cart = CartDecomposition(domain_decomposition, [n], [np.array([0])], [np.array([n - 1])], [p], [1])
38
+ # ... Vector Spaces
39
+ V = StencilVectorSpace(cart, dtype=dtype)
40
+ e = V.ends[0]
41
+ s = V.starts[0]
42
+
43
+ # Build banded matrix with 2p+1 diagonals: must be symmetric and positive definite
44
+ # Here we assign value 2*p on main diagonal and -1 on other diagonals
45
+
46
+ A = StencilMatrix(V, V)
47
+ A[:, -p:0] = -matrix_data[0]
48
+ A[:, 0:1] = matrix_data[1]
49
+ A[:, 1:p + 1] = matrix_data[2]
50
+ A.remove_spurious_entries()
51
+
52
+ # Build exact solution
53
+ xe = StencilVector(V)
54
+ xe[s:e + 1] = np.random.random(e + 1 - s)
55
+ return(V, A, xe)
56
+
57
+
58
+ #===============================================================================
59
+ @pytest.mark.parametrize( 'n', [5, 10, 13] )
60
+ @pytest.mark.parametrize('p', [2, 3])
61
+ @pytest.mark.parametrize('dtype', [float])
62
+ @pytest.mark.parametrize('solver', ['cg', 'pcg', 'bicg', 'bicgstab', 'pbicgstab', 'minres', 'lsmr', 'gmres'])
63
+
64
+ def test_solver_tridiagonal(n, p, dtype, solver, verbose=False):
65
+
66
+ #---------------------------------------------------------------------------
67
+ # PARAMETERS
68
+ #---------------------------------------------------------------------------
69
+
70
+ if solver in ['bicg', 'bicgstab', 'pbicgstab', 'lsmr']:
71
+ if dtype==complex:
72
+ diagonals = [1-10j,6+9j,3+5j]
73
+ else:
74
+ diagonals = [1,6,3]
75
+
76
+ if solver == 'pbicgstab' and dtype == complex:
77
+ # pbicgstab only works for real matrices
78
+ return
79
+ elif solver == 'gmres':
80
+ if dtype==complex:
81
+ diagonals = [-7-2j,-6-2j,-1-10j]
82
+ else:
83
+ diagonals = [-7,-1,-3]
84
+
85
+ if solver in ['cg', 'pcg', 'minres']:
86
+ # pcg runs with Jacobi preconditioner
87
+ V, A, xe = define_data_hermitian(n, p, dtype=dtype)
88
+ if solver == 'minres' and dtype == complex:
89
+ # minres only works for real matrices
90
+ return
91
+ else:
92
+ V, A, xe = define_data(n, p, diagonals, dtype=dtype)
93
+
94
+ # Tolerance for success: 2-norm of error in solution
95
+ tol = 1e-8
96
+
97
+ #---------------------------------------------------------------------------
98
+ # TEST
99
+ #---------------------------------------------------------------------------
100
+ if verbose:
101
+ # Title
102
+ print()
103
+ print( "="*80 )
104
+ print( f"SERIAL TEST: solve linear system A*x = b using {solver}")
105
+ print( "="*80 )
106
+ print()
107
+
108
+ #Create the solvers
109
+ if solver in ['pcg', 'pbicgstab']:
110
+ pc = A.diagonal(inverse=True)
111
+ solv = inverse(A, solver, pc=pc, tol=1e-13, verbose=verbose, recycle=True)
112
+ else:
113
+ solv = inverse(A, solver, tol=1e-13, verbose=verbose, recycle=True)
114
+ solvt = solv.transpose()
115
+ solvh = solv.H
116
+ solv2 = inverse(A@A, solver, tol=1e-13, verbose=verbose, recycle=True) # Test solver of composition of operators
117
+
118
+ # Manufacture right-hand-side vector from exact solution
119
+ be = A @ xe
120
+ be2 = A @ be # Test solver with consecutive solves
121
+ bet = A.T @ xe
122
+ beh = A.H @ xe
123
+
124
+ # Solve linear system
125
+ # Assert x0 got updated correctly and is not the same object as the previous solution, but just a copy
126
+ x = solv @ be
127
+ info = solv.get_info()
128
+ solv_x0 = solv._options["x0"]
129
+ assert np.array_equal(x.toarray(), solv_x0.toarray())
130
+ assert x is not solv_x0
131
+
132
+ x2 = solv @ be2
133
+ solv_x0 = solv._options["x0"]
134
+ assert np.array_equal(x2.toarray(), solv_x0.toarray())
135
+ assert x2 is not solv_x0
136
+
137
+ xt = solvt.solve(bet)
138
+ solvt_x0 = solvt._options["x0"]
139
+ assert np.array_equal(xt.toarray(), solvt_x0.toarray())
140
+ assert xt is not solvt_x0
141
+
142
+ xh = solvh.dot(beh)
143
+ solvh_x0 = solvh._options["x0"]
144
+ assert np.array_equal(xh.toarray(), solvh_x0.toarray())
145
+ assert xh is not solvh_x0
146
+
147
+ if solver != 'pcg':
148
+ # PCG only works with operators with diagonal
149
+ xc = solv2 @ be2
150
+ solv2_x0 = solv2._options["x0"]
151
+ assert np.array_equal(xc.toarray(), solv2_x0.toarray())
152
+ assert xc is not solv2_x0
153
+
154
+
155
+ # Verify correctness of calculation: 2-norm of error
156
+ b = A @ x
157
+ b2 = A @ x2
158
+ bt = A.T @ xt
159
+ bh = A.H @ xh
160
+ if solver != 'pcg':
161
+ bc = A @ A @ xc
162
+
163
+ err = b - be
164
+ err_norm = np.linalg.norm( err.toarray() )
165
+ err2 = b2 - be2
166
+ err2_norm = np.linalg.norm( err2.toarray() )
167
+ errt = bt - bet
168
+ errt_norm = np.linalg.norm( errt.toarray() )
169
+ errh = bh - beh
170
+ errh_norm = np.linalg.norm( errh.toarray() )
171
+
172
+ if solver != 'pcg':
173
+ errc = bc - be2
174
+ errc_norm = np.linalg.norm( errc.toarray() )
175
+
176
+ #---------------------------------------------------------------------------
177
+ # TERMINAL OUTPUT
178
+ #---------------------------------------------------------------------------
179
+ if verbose:
180
+ print()
181
+ print( 'A =', A, sep='\n' )
182
+ print( 'b =', b )
183
+ print( 'x =', x )
184
+ print( 'xe =', xe )
185
+ print( 'info =', info )
186
+ print()
187
+
188
+ print( "-"*40 )
189
+ print( f"2-norm of error in solution = {err_norm:.2e}" )
190
+ if err_norm < tol:
191
+ print( "PASSED" )
192
+ else:
193
+ print( "FAIL" )
194
+ print( "-"*40 )
195
+
196
+ #---------------------------------------------------------------------------
197
+ # PYTEST
198
+ #---------------------------------------------------------------------------
199
+ # The lsmr solver does not consistently produce outputs x whose error ||Ax - b|| is less than tol.
200
+ if solver != 'lsmr':
201
+ assert err_norm < tol
202
+ assert err2_norm < tol
203
+ assert errt_norm < tol
204
+ assert errh_norm < tol
205
+ assert solver == 'pcg' or errc_norm < tol
206
+
207
+ # ===============================================================================
208
+ # SCRIPT FUNCTIONALITY
209
+ #===============================================================================
210
+
211
+ if __name__ == "__main__":
212
+ import sys
213
+ pytest.main( sys.argv )