feectools 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- feectools/__init__.py +0 -0
- feectools/accelerate/__init__.py +0 -0
- feectools/accelerate/accelerate.py +220 -0
- feectools/accelerate/compile_psydac.mk +52 -0
- feectools/api/__init__.py +0 -0
- feectools/api/essential_bc.py +122 -0
- feectools/api/fem_bilinear_form.py +2226 -0
- feectools/api/fem_common.py +286 -0
- feectools/api/fem_sum_form.py +123 -0
- feectools/api/settings.py +82 -0
- feectools/core/__init__.py +11 -0
- feectools/core/bsplines.py +1107 -0
- feectools/core/bsplines_kernels.py +1349 -0
- feectools/core/field_evaluation_kernels.py +5015 -0
- feectools/core/tests/__init__.py +0 -0
- feectools/core/tests/test_bsplines.py +263 -0
- feectools/core/tests/test_bsplines_kernel.py +40 -0
- feectools/core/tests/test_bsplines_pyccel.py +752 -0
- feectools/ddm/__init__.py +3 -0
- feectools/ddm/basic.py +78 -0
- feectools/ddm/blocking_data_exchanger.py +348 -0
- feectools/ddm/cart.py +1835 -0
- feectools/ddm/interface_data_exchanger.py +122 -0
- feectools/ddm/mpi.py +109 -0
- feectools/ddm/nonblocking_data_exchanger.py +331 -0
- feectools/ddm/partition.py +207 -0
- feectools/ddm/petsc.py +112 -0
- feectools/ddm/tests/__init__.py +0 -0
- feectools/ddm/tests/test_cart_1d.py +138 -0
- feectools/ddm/tests/test_cart_2d.py +164 -0
- feectools/ddm/tests/test_cart_3d.py +158 -0
- feectools/ddm/tests/test_multicart_2d.py +173 -0
- feectools/ddm/tests/test_partition.py +124 -0
- feectools/ddm/utilities.py +24 -0
- feectools/feec/__init__.py +0 -0
- feectools/feec/derivatives.py +780 -0
- feectools/feec/dof_kernels.py +210 -0
- feectools/feec/global_geometric_projectors.py +1073 -0
- feectools/feec/hodge.py +148 -0
- feectools/fem/__init__.py +0 -0
- feectools/fem/basic.py +465 -0
- feectools/fem/grid.py +181 -0
- feectools/fem/partitioning.py +344 -0
- feectools/fem/projectors.py +160 -0
- feectools/fem/splines.py +559 -0
- feectools/fem/tensor.py +1393 -0
- feectools/fem/tests/__init__.py +0 -0
- feectools/fem/tests/analytical_profiles_1d.py +100 -0
- feectools/fem/tests/analytical_profiles_base.py +34 -0
- feectools/fem/tests/splines_error_bounds.py +155 -0
- feectools/fem/tests/test_spline_histopolation.py +120 -0
- feectools/fem/tests/test_spline_interpolation.py +182 -0
- feectools/fem/tests/test_splines.py +184 -0
- feectools/fem/tests/test_splines_par.py +46 -0
- feectools/fem/tests/test_vector_spaces.py +150 -0
- feectools/fem/tests/utilities.py +47 -0
- feectools/fem/vector.py +729 -0
- feectools/linalg/__init__.py +0 -0
- feectools/linalg/basic.py +1386 -0
- feectools/linalg/block.py +1451 -0
- feectools/linalg/direct_solvers.py +201 -0
- feectools/linalg/fft.py +258 -0
- feectools/linalg/kernels/__init__.py +0 -0
- feectools/linalg/kernels/axpy_kernels.py +57 -0
- feectools/linalg/kernels/inner_kernels.py +100 -0
- feectools/linalg/kernels/matvec_kernels.py +206 -0
- feectools/linalg/kernels/stencil2IJV_kernels.py +227 -0
- feectools/linalg/kernels/stencil2coo_kernels.py +179 -0
- feectools/linalg/kernels/transpose_kernels.py +263 -0
- feectools/linalg/kron.py +911 -0
- feectools/linalg/solvers.py +1914 -0
- feectools/linalg/sparse.py +114 -0
- feectools/linalg/stencil.py +2923 -0
- feectools/linalg/stencil_dot_kernels.py +317 -0
- feectools/linalg/stencil_transpose_kernels.py +372 -0
- feectools/linalg/tests/__init__.py +0 -0
- feectools/linalg/tests/test_block.py +1588 -0
- feectools/linalg/tests/test_fft.py +106 -0
- feectools/linalg/tests/test_kron_stencil_matrix.py +114 -0
- feectools/linalg/tests/test_linalg.py +1065 -0
- feectools/linalg/tests/test_matrix_free.py +128 -0
- feectools/linalg/tests/test_solvers.py +213 -0
- feectools/linalg/tests/test_stencil_interface_matrix.py +379 -0
- feectools/linalg/tests/test_stencil_vector.py +1036 -0
- feectools/linalg/tests/test_stencil_vector_space.py +440 -0
- feectools/linalg/topetsc.py +522 -0
- feectools/linalg/utilities.py +200 -0
- feectools/utilities/__init__.py +0 -0
- feectools/utilities/quadratures.py +113 -0
- feectools/utilities/utils.py +166 -0
- feectools/version.py +1 -0
- feectools-0.1.0.dist-info/METADATA +66 -0
- feectools-0.1.0.dist-info/RECORD +98 -0
- feectools-0.1.0.dist-info/WHEEL +5 -0
- feectools-0.1.0.dist-info/entry_points.txt +3 -0
- feectools-0.1.0.dist-info/licenses/AUTHORS +22 -0
- feectools-0.1.0.dist-info/licenses/LICENSE +21 -0
- feectools-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,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 )
|