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,286 @@
1
+ from typing import Iterable
2
+
3
+ from sympy import Expr, ImmutableDenseMatrix, Matrix
4
+
5
+ import numpy as np
6
+
7
+ from sympde.expr.basic import BasicForm
8
+ from sympde.expr.evaluation import KernelExpression
9
+ from sympde.topology.space import ScalarFunction, VectorFunction, IndexedVectorFunction
10
+ from sympde.topology.space import ProductSpace, VectorFunctionSpace
11
+ from sympde.topology.datatype import H1SpaceType, L2SpaceType, UndefinedSpaceType
12
+ from sympde.topology.derivatives import get_atom_logical_derivatives
13
+ from sympde.topology.derivatives import _logical_partial_derivatives
14
+
15
+ from feectools.fem.basic import FemSpace
16
+ from feectools.linalg.stencil import StencilMatrix, StencilInterfaceMatrix
17
+ from feectools.linalg.basic import ComposedLinearOperator
18
+ from feectools.api.utilities import flatten
19
+ from feectools.api.ast.utilities import math_atoms_as_str, get_max_partial_derivatives
20
+
21
+ # TODO [YG 01.08.2025]: Avoid importing anything from feectools.pyccel
22
+ from feectools.pyccel.ast.core import _atomic
23
+
24
+ __all__ = (
25
+ 'compute_max_nderiv',
26
+ 'compute_imports',
27
+ 'compute_free_arguments',
28
+ 'collect_spaces',
29
+ 'compute_diag_len',
30
+ 'construct_test_space_arguments',
31
+ 'construct_trial_space_arguments',
32
+ 'construct_quad_grids_arguments',
33
+ 'do_nothing',
34
+ 'extract_stencil_mats',
35
+ 'reset_arrays',
36
+ )
37
+
38
+ #==============================================================================
39
+ def compute_max_nderiv(kernel_expr: KernelExpression) -> int:
40
+ """
41
+ Compute the highest derivative order in the given kernel expression.
42
+
43
+ Parameters
44
+ ----------
45
+ kernel_expr : KernelExpression (from sympde.expr.evaluation)
46
+
47
+ Returns
48
+ -------
49
+ nderiv : int
50
+ The highest order of derivation in `terminal_expr`.
51
+
52
+ """
53
+ assert isinstance(kernel_expr, KernelExpression)
54
+
55
+ terminal_expr = kernel_expr.expr
56
+ if not isinstance(terminal_expr, (ImmutableDenseMatrix, Matrix)):
57
+ terminal_expr = ImmutableDenseMatrix([[terminal_expr]])
58
+ n_rows, n_cols = terminal_expr.shape
59
+
60
+ atoms_types = (ScalarFunction, VectorFunction, IndexedVectorFunction)
61
+ extended_atoms_types = atoms_types + _logical_partial_derivatives
62
+
63
+ nderiv = 0
64
+ for i_row in range(n_rows):
65
+ for i_col in range(n_cols):
66
+ texpr = terminal_expr[i_row, i_col]
67
+ atoms = _atomic(texpr, cls=extended_atoms_types)
68
+ Fs = [get_atom_logical_derivatives(a) for a in atoms]
69
+ d = get_max_partial_derivatives(texpr, logical=True, F=Fs)
70
+ nderiv = max(nderiv, max(d.values()))
71
+
72
+ return nderiv
73
+
74
+ #==============================================================================
75
+ def compute_imports(expr: Expr,
76
+ spaces: Iterable[FemSpace],
77
+ *,
78
+ openmp: bool
79
+ ) -> dict[str, list[str]]:
80
+ """
81
+ Compute all the imports to be added to the generated Python code.
82
+
83
+ Parameters
84
+ ----------
85
+ expr : sympy.Expr
86
+ The integrand expression of a BilinearForm, LinearForm, or
87
+ Functional. This is a pure SymPy expression where SymPDE partial
88
+ derivatives have been converted to SymPy symbols. See Notes.
89
+
90
+ spaces : iterable of feectools.fem.FemSpace
91
+ The discrete spaces which define the finite element representation
92
+ of a BilinearForm, LinearForm, or Functional.
93
+
94
+ openmp : bool
95
+ Whether or not OpenMP pragmas and functions are used in the code.
96
+
97
+ Returns
98
+ -------
99
+ imports : dict[str, list[str]]
100
+ A dictionary whose keys are the names of the Python modules to be
101
+ imported, and whose values are the names of the corresponding
102
+ objects (variables, functions, classes) to be imported from the
103
+ modules.
104
+
105
+ Notes
106
+ -----
107
+ Assume that we start from an object of type BilinearForm, LinearForm,
108
+ or Functional. We take the integrand and expand its vector operations
109
+ with TerminalExpr(), then pull back from physical to logical
110
+ coordinates with LogicalExpr(), and finally convert the symbolic
111
+ partial derivatives with SymbolicExpr(). Where:
112
+ - TerminalExpr is defined in sympde.expr.evaluation
113
+ - LogicalExpr is defined in sympde.topology.mapping
114
+ - SymbolicExpr is defined in sympde.topology.mapping
115
+
116
+ The resulting expression `expr` can be passed to this function.
117
+ """
118
+ assert isinstance(expr, Expr)
119
+ assert all(isinstance(V, FemSpace) for V in spaces)
120
+ assert isinstance(openmp, bool)
121
+
122
+ # Determine the type of scalar quantities to be managed in the code
123
+ dtypes = [getattr(V.symbolic_space, 'codomain_type', 'real') for V in spaces]
124
+ assert all(t in ['complex', 'real'] for t in dtypes)
125
+ dtype = 'complex' if 'complex' in dtypes else 'real'
126
+
127
+ # TODO uncomment this line when we have a SesquilinearForm defined in SymPDE
128
+ #assert isinstance(expr, SesquilinearForm)
129
+
130
+ #... Compute the imports
131
+ math_library = 'cmath' if dtype=='complex' else 'math' # Function names are the same
132
+ math_imports = math_atoms_as_str(expr, 'math')
133
+ numpy_imports = ['array', 'zeros', 'zeros_like', 'floor']
134
+
135
+ imports = {'numpy': numpy_imports}
136
+ if math_imports:
137
+ imports[math_library] = math_imports
138
+ if openmp:
139
+ imports['pyccel.stdlib.internal.openmp'] = ['omp_get_thread_num']
140
+ #...
141
+
142
+ return imports
143
+
144
+ #==============================================================================
145
+ def compute_free_arguments(expr: BasicForm, kernel_expr: KernelExpression) -> tuple[str]:
146
+ """
147
+ The string representation (i.e. the names) of the free arguments in
148
+ the given BilinearForm, LinearForm, or Functional.
149
+
150
+ Parameters
151
+ ----------
152
+ expr : BilinearForm | LinearForm | Functional
153
+ The expression of which we want to compute the free arguments.
154
+
155
+ kernel_expr : sympde.expr.evaluation.KernelExpression
156
+ The atomic representation of the form, which is obtained after using
157
+ LogicalExpr (if there is a mapping) and TerminalExpr on `expr`.
158
+
159
+ Returns
160
+ -------
161
+ tuple[str]
162
+ The string representation (i.e. the names) of the free arguments in
163
+ the given BilinearForm, LinearForm, or Functional.
164
+
165
+ """
166
+ assert isinstance(expr, BasicForm)
167
+ assert isinstance(kernel_expr, KernelExpression)
168
+
169
+ free_args_dict = expr.get_free_variables()
170
+ free_args_str = tuple(str(a) for a in free_args_dict)
171
+
172
+ return free_args_str
173
+
174
+ #==============================================================================
175
+ def collect_spaces(space, *args):
176
+ """
177
+ This function collect the arguments used in the assembly function
178
+
179
+ Parameters
180
+ ----------
181
+ space: <FunctionSpace>
182
+ the symbolic space
183
+
184
+ args : <list>
185
+ list of discrete space components like basis values, spans, ...
186
+
187
+ Returns
188
+ -------
189
+ args : <list>
190
+ list of discrete space components elements used in the asembly
191
+
192
+ """
193
+
194
+ if isinstance(space, ProductSpace):
195
+ spaces = space.spaces
196
+ indices = []
197
+ i = 0
198
+ for space in spaces:
199
+ if isinstance(space, VectorFunctionSpace):
200
+ if isinstance(space.kind, (H1SpaceType, L2SpaceType, UndefinedSpaceType)):
201
+ indices.append(i)
202
+ else:
203
+ indices += [i+j for j in range(space.ldim)]
204
+ i = i + space.ldim
205
+ else:
206
+ indices.append(i)
207
+ i = i + 1
208
+ args = [[e[i] for i in indices] for e in args]
209
+
210
+ elif isinstance(space, VectorFunctionSpace):
211
+ if isinstance(space.kind, (H1SpaceType, L2SpaceType, UndefinedSpaceType)):
212
+ args = [[e[0]] for e in args]
213
+
214
+ return args
215
+
216
+ #==============================================================================
217
+ def compute_diag_len(p, md, mc):
218
+ n = ((np.ceil((p+1)/mc)-1)*md).astype('int')
219
+ n = n-np.minimum(0, n-p)+p+1
220
+ return n.astype('int')
221
+
222
+ #==============================================================================
223
+ def construct_test_space_arguments(basis_values):
224
+ space = basis_values.space
225
+ test_basis = basis_values.basis
226
+ spans = basis_values.spans
227
+ test_degrees = space.degree
228
+ pads = space.pads
229
+ multiplicity = space.multiplicity
230
+
231
+ test_basis, test_degrees, spans = collect_spaces(space.symbolic_space, test_basis, test_degrees, spans)
232
+
233
+ test_basis = flatten(test_basis)
234
+ test_degrees = flatten(test_degrees)
235
+ spans = flatten(spans)
236
+ pads = flatten(pads)
237
+ multiplicity = flatten(multiplicity)
238
+ pads = [p*m for p,m in zip(pads, multiplicity)]
239
+ return test_basis, test_degrees, spans, pads, multiplicity
240
+
241
+ def construct_trial_space_arguments(basis_values):
242
+ space = basis_values.space
243
+ trial_basis = basis_values.basis
244
+ trial_degrees = space.degree
245
+ pads = space.pads
246
+ multiplicity = space.multiplicity
247
+ trial_basis, trial_degrees = collect_spaces(space.symbolic_space, trial_basis, trial_degrees)
248
+
249
+ trial_basis = flatten(trial_basis)
250
+ trial_degrees = flatten(trial_degrees)
251
+ pads = flatten(pads)
252
+ multiplicity = flatten(multiplicity)
253
+ pads = [p*m for p,m in zip(pads, multiplicity)]
254
+ return trial_basis, trial_degrees, pads, multiplicity
255
+
256
+ #==============================================================================
257
+ def construct_quad_grids_arguments(grid, use_weights=True):
258
+ points = grid.points
259
+ if use_weights:
260
+ weights = grid.weights
261
+ quads = flatten(list(zip(points, weights)))
262
+ else:
263
+ quads = flatten(list(zip(points)))
264
+
265
+ nquads = flatten(grid.nquads)
266
+ n_elements = grid.n_elements
267
+ return n_elements, quads, nquads
268
+
269
+ #==============================================================================
270
+ def do_nothing(*args):
271
+ return 0
272
+
273
+ #==============================================================================
274
+ def extract_stencil_mats(mats):
275
+ new_mats = []
276
+ for M in mats:
277
+ if isinstance(M, (StencilInterfaceMatrix, StencilMatrix)):
278
+ new_mats.append(M)
279
+ elif isinstance(M, ComposedLinearOperator):
280
+ new_mats += [i for i in M.multiplicants if isinstance(i, (StencilInterfaceMatrix, StencilMatrix))]
281
+ return new_mats
282
+
283
+ #==============================================================================
284
+ def reset_arrays(*args):
285
+ for a in args:
286
+ a[:]= 0.j if a.dtype==complex else 0.
@@ -0,0 +1,123 @@
1
+ import numpy as np
2
+
3
+ from sympde.expr.expr import (
4
+ BilinearForm as sym_BilinearForm,
5
+ LinearForm as sym_LinearForm,
6
+ Functional as sym_Functional
7
+ )
8
+
9
+ from .basic import BasicDiscrete
10
+ from .fem import DiscreteFunctional
11
+ from .fem import DiscreteLinearForm
12
+ from .fem import DiscreteBilinearForm
13
+ from .fem_bilinear_form import DiscreteBilinearForm as DiscreteBilinearForm_SF
14
+ from .fem_common import reset_arrays
15
+ from .utilities import random_string
16
+
17
+ __all__ = ('DiscreteSumForm',)
18
+
19
+ #==============================================================================
20
+ class DiscreteSumForm(BasicDiscrete):
21
+
22
+ def __init__(self, a, kernel_expr, *args, **kwargs):
23
+
24
+ # Sum factorization is only implemented for bilinear forms in 3D, in
25
+ # which case we use it by default. A 2D implementation should be the
26
+ # next step, hence we allow the user to pass `sum_factorization=True`
27
+ # even if not supported yet. In the case of linear forms or functionals
28
+ # this option is irrelevant for now, so we ignore it.
29
+ #
30
+ # In every case we remove the `sum_factorization` key from the dict
31
+ # in order to avoid errors, because none of the class constructors
32
+ # accept this argument.
33
+ sum_factorization = kwargs.pop('sum_factorization', a.ldim == 3)
34
+
35
+ # TODO Uncomment when the SesquilinearForm exist in SymPDE
36
+ #if not isinstance(a, (sym_BilinearForm, sym_SesquilinearForm, sym_LinearForm, sym_Functional)):
37
+ # raise TypeError('> Expecting a symbolic BilinearForm, SesquilinearForm, LinearForm, Functional')
38
+ if not isinstance(a, (sym_BilinearForm, sym_LinearForm, sym_Functional)):
39
+ raise TypeError('> Expecting a symbolic BilinearForm, LinearForm, Functional')
40
+
41
+ self._expr = a
42
+ backend = kwargs.pop('backend', None)
43
+ self._backend = backend
44
+
45
+ folder = kwargs.get('folder', None)
46
+ self._folder = self._initialize_folder(folder)
47
+
48
+ # create a module name if not given
49
+ tag = random_string(8)
50
+
51
+ # ...
52
+ forms = []
53
+ free_args = []
54
+ self._kernel_expr = kernel_expr
55
+ operator = None
56
+ for e in kernel_expr:
57
+ if isinstance(a, sym_LinearForm):
58
+ kwargs['update_ghost_regions'] = False
59
+ ah = DiscreteLinearForm(a, e, *args, backend=backend, **kwargs)
60
+ kwargs['vector'] = ah._vector
61
+ operator = ah._vector
62
+
63
+ # TODO Uncomment when the SesquilinearForm exist in SymPDE
64
+ # elif isinstance(a, sym_SesquilinearForm):
65
+ # kwargs['update_ghost_regions'] = False
66
+ # ah = DiscreteSesquilinearForm(a, e, *args, assembly_backend=backend, **kwargs)
67
+ # kwargs['matrix'] = ah._matrix
68
+ # operator = ah._matrix
69
+
70
+ elif isinstance(a, sym_BilinearForm):
71
+ kwargs['update_ghost_regions'] = False
72
+ if sum_factorization:
73
+ ah = DiscreteBilinearForm_SF(a, e, *args, assembly_backend=backend, **kwargs)
74
+ else:
75
+ ah = DiscreteBilinearForm(a, e, *args, assembly_backend=backend, **kwargs)
76
+ kwargs['matrix'] = ah._matrix
77
+ operator = ah._matrix
78
+
79
+ elif isinstance(a, sym_Functional):
80
+ ah = DiscreteFunctional(a, e, *args, backend=backend, **kwargs)
81
+
82
+ forms.append(ah)
83
+ free_args.extend(ah.free_args)
84
+
85
+ if isinstance(a, sym_BilinearForm):
86
+ is_broken = len(args[0].domain)>1
87
+ if self._backend is not None and is_broken:
88
+ for mat in kwargs['matrix']._blocks.values():
89
+ mat.set_backend(backend)
90
+ elif self._backend is not None:
91
+ kwargs['matrix'].set_backend(backend)
92
+
93
+ self._forms = forms
94
+ self._operator = operator
95
+ self._free_args = tuple(set(free_args))
96
+ self._is_functional = isinstance(a, sym_Functional)
97
+ # ...
98
+
99
+ @property
100
+ def forms(self):
101
+ return self._forms
102
+
103
+ @property
104
+ def free_args(self):
105
+ return self._free_args
106
+
107
+ @property
108
+ def is_functional(self):
109
+ return self._is_functional
110
+
111
+ def assemble(self, *, reset=True, **kwargs):
112
+ if not self.is_functional:
113
+ if reset :
114
+ reset_arrays(*[i for M in self.forms for i in M.global_matrices])
115
+
116
+ for form in self.forms:
117
+ form.assemble(reset=False, **kwargs)
118
+ self._operator.exchange_assembly_data()
119
+ return self._operator
120
+ else:
121
+ M = [form.assemble(**kwargs) for form in self.forms]
122
+ M = np.sum(M)
123
+ return M
@@ -0,0 +1,82 @@
1
+ # coding: utf-8
2
+ import subprocess # nosec B404
3
+ import platform
4
+ import re
5
+ from packaging.version import Version
6
+
7
+
8
+ __all__ = ('PSYDAC_DEFAULT_FOLDER', 'PSYDAC_BACKENDS')
9
+
10
+ #==============================================================================
11
+
12
+ PSYDAC_DEFAULT_FOLDER = {'name':'__psydac__'}
13
+
14
+ # ... defining PSYDAC backends
15
+ PSYDAC_BACKEND_PYTHON = {'name': 'python', 'tag':'python', 'openmp':False}
16
+
17
+ PSYDAC_BACKEND_GPYCCEL = {'name': 'pyccel',
18
+ 'compiler_family': 'GNU',
19
+ 'flags' : '-O3 -ffast-math',
20
+ 'folder' : '__gpyccel__',
21
+ 'tag' : 'gpyccel',
22
+ 'openmp' : False}
23
+
24
+ PSYDAC_BACKEND_IPYCCEL = {'name': 'pyccel',
25
+ 'compiler_family': 'intel',
26
+ 'flags' : '-O3',
27
+ 'folder' : '__ipyccel__',
28
+ 'tag' :'ipyccel',
29
+ 'openmp' : False}
30
+
31
+ PSYDAC_BACKEND_PGPYCCEL = {'name': 'pyccel',
32
+ 'compiler_family': 'PGI',
33
+ 'flags' : '-O3 -Munroll',
34
+ 'folder' : '__pgpyccel__',
35
+ 'tag' : 'pgpyccel',
36
+ 'openmp' : False}
37
+
38
+ PSYDAC_BACKEND_NVPYCCEL = {'name': 'pyccel',
39
+ 'compiler_family': 'nvidia',
40
+ 'flags' : '-O3 -Munroll',
41
+ 'folder' : '__nvpyccel__',
42
+ 'tag' : 'nvpyccel',
43
+ 'openmp' : False}
44
+ # ...
45
+
46
+ # Get gfortran version
47
+ gfortran_version_output = subprocess.check_output(['gfortran', '--version']).decode('utf-8') # nosec B603, B607
48
+ gfortran_version_string = re.search(r"(\d+\.\d+\.\d+)", gfortran_version_output).group()
49
+ gfortran_version = Version(gfortran_version_string)
50
+
51
+ # Platform-dependent flags
52
+ if platform.system() == "Darwin" and platform.machine() == 'arm64' and gfortran_version >= Version("14"):
53
+
54
+ # Apple silicon requires architecture-specific flags (see https://github.com/pyccel/psydac/pull/411)
55
+ # which are only available on GCC version >= 14
56
+ cpu_brand = subprocess.check_output(['sysctl','-n','machdep.cpu.brand_string']).decode('utf-8').strip() # nosec B603, B607
57
+ if cpu_brand.startswith("Apple M"):
58
+ # Example: "Apple M3 Pro (virtual)" --> " -mcpu=apple-m3"
59
+ cpu_flag = '-'.join(cpu_brand.lower().split()[:2])
60
+ PSYDAC_BACKEND_GPYCCEL['flags'] += f' -mcpu={cpu_flag}'
61
+ else:
62
+ # TODO: Support later Apple CPU models. Perhaps the CPU naming scheme could be easily guessed
63
+ # based on the output of 'sysctl -n machdep.cpu.brand_string', but I wouldn't rely on this
64
+ # guess unless it has been manually verified. Loud errors are better than silent failures!
65
+ raise SystemError(f"Unsupported Apple CPU '{cpu_brand}'.")
66
+
67
+ else:
68
+ # Default architecture flags
69
+ PSYDAC_BACKEND_GPYCCEL['flags'] += ' -march=native -mtune=native'
70
+ if platform.machine() == 'x86_64':
71
+ PSYDAC_BACKEND_GPYCCEL['flags'] += ' -mavx'
72
+
73
+ #==============================================================================
74
+
75
+ # List of all available backends for accelerating Python code
76
+ PSYDAC_BACKENDS = {
77
+ 'python' : PSYDAC_BACKEND_PYTHON,
78
+ 'pyccel-gcc' : PSYDAC_BACKEND_GPYCCEL,
79
+ 'pyccel-intel' : PSYDAC_BACKEND_IPYCCEL,
80
+ 'pyccel-pgi' : PSYDAC_BACKEND_PGPYCCEL,
81
+ 'pyccel-nvidia': PSYDAC_BACKEND_NVPYCCEL,
82
+ }
@@ -0,0 +1,11 @@
1
+ from feectools.core import bsplines
2
+
3
+ __all__ = ['bsplines']
4
+
5
+ try:
6
+ from feectools.core import bsp
7
+ from feectools.core import interface
8
+ except ImportError:
9
+ pass
10
+ else:
11
+ __all__.extend( ['bsp','interface'] )