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
File without changes
@@ -0,0 +1,100 @@
1
+ # coding: utf-8
2
+ # Copyright 2018 Yaman Güçlü
3
+
4
+ import math
5
+ import numpy as np
6
+
7
+ from feectools.fem.tests.analytical_profiles_base import AnalyticalProfile
8
+ from feectools.fem.tests.utilities import horner, falling_factorial
9
+
10
+ __all__ = ('AnalyticalProfile_1d_cos', 'AnalyticalProfile1D_Poly')
11
+
12
+ #===============================================================================
13
+ class AnalyticalProfile1D_Cos( AnalyticalProfile ):
14
+
15
+ def __init__( self, n=1, c=0.0 ):
16
+ twopi = 2.0*math.pi
17
+ self._k = twopi * n
18
+ self._phi = twopi * c
19
+
20
+ @property
21
+ def ndims( self ):
22
+ return 1
23
+
24
+ @property
25
+ def domain( self ):
26
+ return (0.0, 1.0)
27
+
28
+ @property
29
+ def poly_order( self ):
30
+ return -1
31
+
32
+ def eval( self, x, diff=0 ):
33
+ return self._k**diff * np.cos( 0.5*math.pi*diff + self._k*x + self._phi )
34
+
35
+ def max_norm( self, diff=0 ):
36
+ return self._k**diff
37
+
38
+ #===============================================================================
39
+ class AnalyticalProfile1D_Sin( AnalyticalProfile ):
40
+
41
+ def __init__( self, n=1, c=0.0 ):
42
+ twopi = 2.0*math.pi
43
+ self._k = twopi * n
44
+ self._phi = twopi * c
45
+
46
+ @property
47
+ def ndims( self ):
48
+ return 1
49
+
50
+ @property
51
+ def domain( self ):
52
+ return (0.0, 1.0)
53
+
54
+ @property
55
+ def poly_order( self ):
56
+ return -1
57
+
58
+ def eval( self, x, diff=0 ):
59
+ return self._k**diff * np.sin( 0.5*math.pi*diff + self._k*x + self._phi )
60
+
61
+ def max_norm( self, diff=0 ):
62
+ return self._k**diff
63
+ #===============================================================================
64
+ class AnalyticalProfile1D_Poly( AnalyticalProfile ):
65
+
66
+ def __init__( self, deg ):
67
+
68
+ coeffs = np.random.random_sample( 1+deg ) # 0 <= c < 1
69
+ coeffs = 1.0 - coeffs # 0 < c <= 1
70
+
71
+ self._deg = deg
72
+ self._coeffs = coeffs
73
+
74
+ @property
75
+ def ndims( self ):
76
+ return 1
77
+
78
+ @property
79
+ def domain( self ):
80
+ return (-1.0, 1.0)
81
+
82
+ @property
83
+ def poly_order( self ):
84
+ return self_deg
85
+
86
+ def eval( self, x, diff=0 ):
87
+ d = diff
88
+ coeffs = [c * falling_factorial( i+d, d ) \
89
+ for i,c in enumerate( self._coeffs[d:] )]
90
+ return horner( x, *coeffs )
91
+
92
+ def max_norm( self, diff=0 ):
93
+ xmin, xmax = self.domain
94
+
95
+ if xmax < abs(xmin):
96
+ raise NotImplementedError( "General formula not implemented" )
97
+
98
+ # For xmax >= |xmin|:
99
+ # max(|f^(d)(x)|) = f^(d)(xmax)
100
+ return self.eval( xmax, diff )
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ # Copyright 2018 Yaman Güçlü
3
+
4
+ from abc import ABCMeta, abstractmethod
5
+
6
+ __all__ = ('AnalyticalProfile',)
7
+
8
+ #===============================================================================
9
+ class AnalyticalProfile( metaclass=ABCMeta ):
10
+
11
+ @property
12
+ @abstractmethod
13
+ def ndims(self):
14
+ """ Number of dimensions. """
15
+
16
+ @property
17
+ @abstractmethod
18
+ def domain(self):
19
+ """ Domain limits in each dimension. """
20
+
21
+ @property
22
+ @abstractmethod
23
+ def poly_order(self):
24
+ """ If profile is polynomial, poly_order=degree;
25
+ otherwise poly_order=-1.
26
+ """
27
+
28
+ @abstractmethod
29
+ def eval(self, x, diff=0):
30
+ """ Evaluate profile (or its derivative) at position x. """
31
+
32
+ @abstractmethod
33
+ def max_norm(self, diff=0):
34
+ """ Compute max-norm of profile (or its derivative) over domain. """
@@ -0,0 +1,155 @@
1
+ # coding: utf-8
2
+ # Copyright 2018 Yaman Güçlü
3
+ #
4
+ # This file is the Python translation of a Selalib Fortran module:
5
+ # 'selalib/src/splines/tests/m_splines_error_bounds.F90'
6
+
7
+ __all__ = ('spline_1d_error_bound', 'spline_1d_error_bound_on_deriv',
8
+ 'spline_2d_error_bound', 'spline_2d_error_bounds_on_grad')
9
+
10
+ #===============================================================================
11
+
12
+ k = ( 1 / 2.0,
13
+ 1 / 8.0,
14
+ 1 / 24.0,
15
+ 5 / 384.0,
16
+ 1 / 240.0,
17
+ 61 / 46080.0,
18
+ 17 / 40320.0,
19
+ 277 / 2064384.0,
20
+ 31 / 725760.0,
21
+ 50521 / 3715891200.0 )
22
+
23
+ def tihomirov_error_bound( h, deg, norm_f ):
24
+ """
25
+ Error bound in max norm for spline interpolation of periodic functions from:
26
+
27
+ V M Tihomirov 1969 Math. USSR Sb. 9 275
28
+ https://doi.org/10.1070/SM1969v009n02ABEH002052 (page 286, bottom)
29
+
30
+ Yu. S. Volkov and Yu. N. Subbotin
31
+ https://doi.org/10.1134/S0081543815020236 (equation 14)
32
+
33
+ Also applicable to first derivative by passing deg-1 instead of deg
34
+ Volkov & Subbotin 2015, eq. 15
35
+
36
+ Parameters
37
+ ----------
38
+ h : float
39
+ Cell width
40
+
41
+ deg : int
42
+ Degree of spline S
43
+
44
+ norm_f : float
45
+ Max of function f(x) (or its derivative) over domain
46
+
47
+ Result
48
+ ------
49
+ norm_e : float
50
+ Max of error $E(x):=f(x)-S(x)$ over domain
51
+
52
+ """
53
+ norm_e = k[deg] * h**deg * norm_f
54
+
55
+ return norm_e
56
+
57
+ #===============================================================================
58
+ def spline_1d_error_bound( profile_1d, dx, deg ):
59
+ """
60
+ Compute error bound for spline approximation of 1D analytical profile.
61
+
62
+ Parameters
63
+ ----------
64
+ profile_1d : 1D analytical profile
65
+ Must provide 'max_norm( n )' method to compute max norm of n-th
66
+ derivative of profile over domain.
67
+
68
+ dx : float
69
+ Grid spacing.
70
+
71
+ deg : int
72
+ Spline degree.
73
+
74
+ Result
75
+ ------
76
+ max_error : float
77
+ Error bound: max-norm of error over domain should be smaller than this.
78
+
79
+ """
80
+ max_norm = profile_1d.max_norm( deg+1 )
81
+ max_error = tihomirov_error_bound( dx, deg, max_norm )
82
+ return max_error
83
+
84
+ #===============================================================================
85
+ def spline_1d_error_bound_on_deriv( profile_1d, dx, deg ):
86
+ """ Compute error bound on first derivative, for spline approximation of 1D
87
+ analytical profile. Signature is identical to 'spline_1d_error_bound'.
88
+ """
89
+ max_norm = profile_1d.max_norm( deg+1 )
90
+ max_error = tihomirov_error_bound( dx, deg-1, max_norm )
91
+ return max_error
92
+
93
+ #===============================================================================
94
+ def spline_2d_error_bound( profile_2d, dx1, dx2, deg1, deg2 ):
95
+ """
96
+ Compute error bound for spline approximation of 2D analytical profile.
97
+
98
+ Parameters
99
+ ----------
100
+ profile_2d : 2D analytical profile
101
+ Must provide 'max_norm( n1,n2 )' method to compute max norm of its
102
+ mixed derivative of degree (n1,n2) over domain.
103
+
104
+ dx1 : float
105
+ Grid spacing along 1st dimension.
106
+
107
+ dx2 : float
108
+ Grid spacing along 2nd dimension.
109
+
110
+ deg1 : int
111
+ Spline degree along 1st dimension.
112
+
113
+ deg2 : int
114
+ Spline degree along 2nd dimension.
115
+
116
+ Result
117
+ ------
118
+ max_error : float
119
+ Error bound: max-norm of error over domain should be smaller than this.
120
+
121
+ """
122
+ # Max norm of highest partial derivatives in x1 and x2 of analytical profile
123
+ max_norm1 = profile_2d.max_norm( deg1+1, 0 )
124
+ max_norm2 = profile_2d.max_norm( 0 , deg2+1 )
125
+
126
+ # Error bound on function value
127
+ max_error = f_tihomirov_error_bound( dx1, deg1, max_norm1 ) \
128
+ + f_tihomirov_error_bound( dx2, deg2, max_norm2 )
129
+
130
+ # Empirical correction: for linear interpolation increase estimate by 5%
131
+ if (deg1 == 1 or deg2 == 1):
132
+ max_error = 1.05 * max_error
133
+
134
+ return max_error
135
+
136
+ #===============================================================================
137
+ def spline_2d_error_bounds_on_grad( profile_2d, dx1, dx2, deg1, deg2 ):
138
+ """
139
+ Compute error bound on gradient, for spline approximation of 2D
140
+ analytical profile. Signature is identical to 'spline_2d_error_bound'.
141
+
142
+ """
143
+ # Max norm of highest partial derivatives in x1 and x2 of analytical profile
144
+ max_norm1 = profile_2d.max_norm( deg1+1, 0 )
145
+ max_norm2 = profile_2d.max_norm( 0 , deg2+1 )
146
+
147
+ # Error bound on x1-derivative
148
+ max_error1 = f_tihomirov_error_bound( dx1, deg1-1, max_norm1 ) \
149
+ + f_tihomirov_error_bound( dx2, deg2 , max_norm2 )
150
+
151
+ # Error bound on x2-derivative
152
+ max_error2 = f_tihomirov_error_bound( dx1, deg1 , max_norm1 ) \
153
+ + f_tihomirov_error_bound( dx2, deg2-1, max_norm2 )
154
+
155
+ return (max_error1, max_error2)
@@ -0,0 +1,120 @@
1
+ import pytest
2
+ import numpy as np
3
+ import matplotlib.pyplot as plt
4
+ from scipy.integrate import quad
5
+
6
+ from feectools.fem.basic import FemField
7
+ from feectools.fem.splines import SplineSpace
8
+ #from feectools.fem.tensor import TensorFemSpace
9
+
10
+ from feectools.fem.tests.utilities import horner, random_grid
11
+ from feectools.fem.tests.splines_error_bounds import spline_1d_error_bound
12
+ from feectools.fem.tests.analytical_profiles_1d import AnalyticalProfile1D_Cos
13
+
14
+ #==============================================================================
15
+ def histopolate_polynomial(basis, ncells, degree):
16
+
17
+ domain = (-1.0, 1.0)
18
+ periodic = False
19
+
20
+ # Polynomial to be approximated
21
+ poly_coeffs = np.random.random_sample( degree+1 ) # 0 <= c < 1
22
+ poly_coeffs = 1.0 - poly_coeffs # 0 < c <= 1
23
+ f = lambda x : horner( x, *poly_coeffs )
24
+
25
+ # Define spline space and field
26
+ grid = random_grid( domain, ncells, 0.5 )
27
+ Vh = SplineSpace( degree=degree, grid=grid, periodic=periodic, basis=basis )
28
+ fh = FemField( Vh )
29
+
30
+ # Compute histopolant
31
+ xg = Vh.ext_greville
32
+ Ig = np.array([quad(f, xg[i], xg[i+1])[0] for i in range(len(xg)-1)])
33
+ Vh.compute_histopolant(Ig, fh)
34
+
35
+ return domain, f, fh
36
+
37
+ #==============================================================================
38
+ @pytest.mark.parametrize('basis', ['B', 'M'])
39
+ @pytest.mark.parametrize('ncells', [10, 20, 33])
40
+ @pytest.mark.parametrize('degree', [2, 5, 7])
41
+ def test_histopolation_exact(basis, ncells, degree, num_pts=100, tol=1e-11):
42
+
43
+ domain, f, fh = histopolate_polynomial(basis, ncells, degree)
44
+
45
+ # Compare to exact solution
46
+ x = np.linspace(*domain, num=num_pts)
47
+ y = f(x)
48
+ yh = np.array([fh(xi) for xi in x])
49
+
50
+ assert np.allclose(yh, y, rtol=tol, atol=tol)
51
+
52
+ #==============================================================================
53
+ @pytest.mark.parametrize('basis', ['B', 'M'])
54
+ @pytest.mark.parametrize('ncells', [10, 20, 33])
55
+ @pytest.mark.parametrize('degree', [2, 5, 7])
56
+ @pytest.mark.parametrize('periodic', [True, False])
57
+ def test_histopolation_cosine(basis, ncells, degree, periodic, num_pts=100):
58
+
59
+ # Function to be approximated
60
+ # TODO: write function and domain explicitly
61
+ f = AnalyticalProfile1D_Cos()
62
+
63
+ # Define spline space and field
64
+ grid, dx = np.linspace(*f.domain, num=ncells+1, retstep=True)
65
+ Vh = SplineSpace(degree=degree, grid=grid, periodic=periodic)
66
+ fh = FemField(Vh)
67
+
68
+ # Compute histopolant
69
+ xg = Vh.histopolation_grid
70
+ Ig = np.array([quad(f.eval, xl, xr)[0] for xl, xr in zip(xg[:-1], xg[1:])])
71
+ Vh.compute_histopolant(Ig, fh)
72
+
73
+ # Compare to exact solution
74
+ x = np.linspace(*f.domain, num=num_pts)
75
+ y = f.eval(x)
76
+ yh = np.array([fh(xi) for xi in x])
77
+
78
+ max_norm_err = np.max(abs(y - yh))
79
+ err_bound = spline_1d_error_bound(f, dx, degree)
80
+
81
+ assert max_norm_err < err_bound
82
+
83
+ #==============================================================================
84
+ # Diagnostics
85
+ #==============================================================================
86
+ def compare_and_plot(domain, f, fh, num_pts=100):
87
+
88
+ x = np.linspace(*domain, num=num_pts)
89
+ y = f(x)
90
+ yh = np.array([fh(xi) for xi in x])
91
+
92
+ max_norm_err = np.max(abs(yh - y))
93
+ print("Maximum error on evaluation grid: {}".format(max_norm_err))
94
+
95
+ fig, ax = plt.subplots(1, 1)
96
+ ax.plot(x, y, label='f(x)')
97
+ ax.plot(x, yh, '.', label='f1_h(x)')
98
+ ax.grid(True)
99
+ ax.set_xlabel('x')
100
+ ax.set_ylabel('y')
101
+ fig.legend()
102
+ fig.show()
103
+
104
+ #==============================================================================
105
+ # Diagnostics
106
+ #==============================================================================
107
+ if __name__ == '__main__':
108
+
109
+ domain, f, fh = histopolate_polynomial(basis='B', ncells=10, degree=7)
110
+ compare_and_plot(domain, f, fh)
111
+
112
+ domain, f, fh = histopolate_polynomial(basis='M', ncells=10, degree=7)
113
+ compare_and_plot(domain, f, fh)
114
+
115
+ import __main__ as main
116
+ if hasattr( main, '__file__' ):
117
+ try:
118
+ __IPYTHON__
119
+ except NameError:
120
+ plt.show()
@@ -0,0 +1,182 @@
1
+ # coding: utf-8
2
+ # Copyright 2018 Yaman Güçlü
3
+
4
+ from feectools.ddm.mpi import mpi as MPI
5
+
6
+ import numpy as np
7
+ import pytest
8
+ import time
9
+
10
+ from feectools.core.bsplines import make_knots
11
+ from feectools.fem.basic import FemField
12
+ from feectools.fem.splines import SplineSpace
13
+ from feectools.fem.tensor import TensorFemSpace
14
+ from feectools.ddm.cart import DomainDecomposition
15
+
16
+ from feectools.fem.tests.utilities import horner, random_grid
17
+ from feectools.fem.tests.splines_error_bounds import spline_1d_error_bound
18
+ from feectools.fem.tests.analytical_profiles_1d import (AnalyticalProfile1D_Cos, AnalyticalProfile1D_Poly)
19
+ #===============================================================================
20
+ @pytest.mark.serial
21
+ @pytest.mark.parametrize( "ncells", [1,5,10,23] )
22
+ @pytest.mark.parametrize( "degree", range(1,11) )
23
+
24
+ def test_SplineInterpolation1D_exact( ncells, degree ):
25
+
26
+ domain = [-1.0, 1.0]
27
+ periodic = False
28
+
29
+ poly_coeffs = np.random.random_sample( degree+1 ) # 0 <= c < 1
30
+ poly_coeffs = 1.0 - poly_coeffs # 0 < c <= 1
31
+ f = lambda x : horner( x, *poly_coeffs )
32
+
33
+ grid = random_grid( domain, ncells, 0.5 )
34
+ space = SplineSpace( degree=degree, grid=grid, periodic=periodic )
35
+ field = FemField( space )
36
+
37
+ xg = space.greville
38
+ ug = f( xg )
39
+
40
+ space.compute_interpolant( ug, field )
41
+
42
+ xt = np.linspace( *domain, num=100 )
43
+ err = np.array( [field( x ) - f( x ) for x in xt] )
44
+
45
+ max_norm_err = np.max( abs( err ) )
46
+ assert max_norm_err < 1.0e-13
47
+
48
+ #===============================================================================
49
+ def args_SplineInterpolation1D_cosine():
50
+ for ncells in [5,10,23]:
51
+ for periodic in [True, False]:
52
+ pmax = min(ncells,9) if periodic else 9
53
+ for degree in range(1,pmax+1):
54
+ yield (ncells, degree, periodic)
55
+
56
+ @pytest.mark.serial
57
+ @pytest.mark.parametrize( "ncells,degree,periodic",
58
+ args_SplineInterpolation1D_cosine() )
59
+
60
+ def test_SplineInterpolation1D_cosine( ncells, degree, periodic ):
61
+
62
+ f = AnalyticalProfile1D_Cos()
63
+
64
+ grid, dx = np.linspace( *f.domain, num=ncells+1, retstep=True )
65
+ space = SplineSpace( degree=degree, grid=grid, periodic=periodic )
66
+ field = FemField( space )
67
+
68
+ xg = space.greville
69
+ ug = f.eval( xg )
70
+
71
+ space.compute_interpolant( ug, field )
72
+ xt = np.linspace( *f.domain, num=100 )
73
+ err = np.array( [field( x ) - f.eval( x ) for x in xt] )
74
+
75
+ max_norm_err = np.max( abs( err ) )
76
+ err_bound = spline_1d_error_bound( f, dx, degree )
77
+
78
+ assert max_norm_err < err_bound
79
+
80
+ #===============================================================================
81
+ @pytest.mark.parallel
82
+ @pytest.mark.parametrize( "nc1", [7,10,23] )
83
+ @pytest.mark.parametrize( "nc2", [7,10,23] )
84
+ @pytest.mark.parametrize( "deg1", range(1,5) )
85
+ @pytest.mark.parametrize( "deg2", range(1,5) )
86
+
87
+ def test_SplineInterpolation2D_parallel_exact( nc1, nc2, deg1, deg2 ):
88
+
89
+ # Communicator, size, rank
90
+ mpi_comm = MPI.COMM_WORLD
91
+ mpi_size = mpi_comm.Get_size()
92
+ mpi_rank = mpi_comm.Get_rank()
93
+
94
+ domain1 = [-1.0, 0.8]
95
+ periodic1 = False
96
+
97
+ domain2 = [-0.9, 1.0]
98
+ periodic2 = False
99
+
100
+ # Random coefficients of 1D polynomial (identical on all processes!)
101
+ poly_coeffs = np.random.random_sample( min(deg1,deg2)+1 ) # 0 <= c < 1
102
+ poly_coeffs = 1.0 - poly_coeffs # 0 < c <= 1
103
+ mpi_comm.Bcast( poly_coeffs, root=0 )
104
+
105
+ # 2D exact solution: 1D polynomial of linear combination z=x1-x2/2
106
+ f = lambda x1,x2 : horner( x1-0.5*x2, *poly_coeffs )
107
+
108
+ # Random 1D grids (identical on all processes!)
109
+ grid1 = random_grid( domain1, nc1, 0.1 )
110
+ grid2 = random_grid( domain2, nc2, 0.1 )
111
+ mpi_comm.Bcast( grid1, root=0 )
112
+ mpi_comm.Bcast( grid2, root=0 )
113
+
114
+ # 1D spline spaces along x1 and x2
115
+ space1 = SplineSpace( degree=deg1, grid=grid1, periodic=periodic1 )
116
+ space2 = SplineSpace( degree=deg2, grid=grid2, periodic=periodic2 )
117
+
118
+ domain_decomposition = DomainDecomposition([nc1, nc2], [periodic1, periodic2], comm=mpi_comm)
119
+ # Tensor-product 2D spline space, distributed, and field
120
+ tensor_space = TensorFemSpace( domain_decomposition, space1, space2 )
121
+ tensor_field = FemField( tensor_space )
122
+
123
+ # Coordinates of Greville points (global)
124
+ x1g = space1.greville
125
+ x2g = space2.greville
126
+
127
+ # Interpolation data on Greville points (distributed)
128
+ V = tensor_space.coeff_space
129
+ ug = V.zeros()
130
+ s1,s2 = V.starts
131
+ e1,e2 = V.ends
132
+ n1,n2 = V.npts
133
+ for i1 in range( s1, e1+1 ):
134
+ for i2 in range( s2, e2+1 ):
135
+ ug[i1,i2] = f( x1g[i1], x2g[i2] )
136
+
137
+ ug.update_ghost_regions()
138
+
139
+ # Compute 2D spline interpolant
140
+ tensor_space.compute_interpolant( ug, tensor_field )
141
+
142
+ #------------
143
+ # DIAGNOSTICS
144
+ #------------
145
+
146
+ # Verify that solution is exact at Greville points
147
+ err = V.zeros()
148
+ for i1 in range( s1, e1+1 ):
149
+ for i2 in range( s2, e2+1 ):
150
+ err[i1,i2] = ug[i1,i2] - tensor_field( x1g[i1], x2g[i2] )
151
+ interp_error = abs( err[:,:] ).max()
152
+
153
+ # Compute L2 norm of error
154
+ integrand = lambda x1,x2: (f(x1,x2)-tensor_field(x1,x2))**2
155
+ l2_error = np.sqrt( tensor_space.integral( integrand ) )
156
+
157
+ # Print some information to terminal
158
+ for i in range( mpi_size ):
159
+ if i == mpi_rank:
160
+ print( '--------------------------------------------------' )
161
+ print( ' RANK = {}'.format( mpi_rank ) )
162
+ print( '--------------------------------------------------' )
163
+ print( '> Degree :: [{:2d},{:2d}]'.format( deg1, deg2 ) )
164
+ print( '> Ncells :: [{:2d},{:2d}]'.format( nc1, nc2 ) )
165
+ print( '> Nbasis :: [{:2d},{:2d}]'.format( n1, n2 ) )
166
+ print( '> Starts :: [{:2d},{:2d}]'.format( s1, s2 ) )
167
+ print( '> Ends :: [{:2d},{:2d}]'.format( e1, e2 ) )
168
+ print( '> Interp. error :: {:.2e}'.format( interp_error ) )
169
+ print( '> L2 error :: {:.2e}'.format( l2_error ) )
170
+ print( '', flush=True )
171
+ time.sleep( 0.1 )
172
+ mpi_comm.Barrier()
173
+
174
+ # Verify that error is only caused by finite precision arithmetic
175
+ assert interp_error < 1.0e-13
176
+ assert l2_error < 1.0e-13
177
+
178
+ #===============================================================================
179
+ # SCRIPT FUNCTIONALITY
180
+ #===============================================================================
181
+ if __name__ == '__main__':
182
+ test_SplineInterpolation2D_parallel_exact( 10, 16, 3, 5 )