capytaine 2.3.1__cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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 (93) hide show
  1. capytaine/__about__.py +16 -0
  2. capytaine/__init__.py +36 -0
  3. capytaine/bem/__init__.py +0 -0
  4. capytaine/bem/airy_waves.py +111 -0
  5. capytaine/bem/engines.py +441 -0
  6. capytaine/bem/problems_and_results.py +600 -0
  7. capytaine/bem/solver.py +594 -0
  8. capytaine/bodies/__init__.py +4 -0
  9. capytaine/bodies/bodies.py +1221 -0
  10. capytaine/bodies/dofs.py +19 -0
  11. capytaine/bodies/predefined/__init__.py +6 -0
  12. capytaine/bodies/predefined/cylinders.py +151 -0
  13. capytaine/bodies/predefined/rectangles.py +111 -0
  14. capytaine/bodies/predefined/spheres.py +70 -0
  15. capytaine/green_functions/FinGreen3D/.gitignore +1 -0
  16. capytaine/green_functions/FinGreen3D/FinGreen3D.f90 +3589 -0
  17. capytaine/green_functions/FinGreen3D/LICENSE +165 -0
  18. capytaine/green_functions/FinGreen3D/Makefile +16 -0
  19. capytaine/green_functions/FinGreen3D/README.md +24 -0
  20. capytaine/green_functions/FinGreen3D/test_program.f90 +39 -0
  21. capytaine/green_functions/LiangWuNoblesse/.gitignore +1 -0
  22. capytaine/green_functions/LiangWuNoblesse/LICENSE +504 -0
  23. capytaine/green_functions/LiangWuNoblesse/LiangWuNoblesseWaveTerm.f90 +751 -0
  24. capytaine/green_functions/LiangWuNoblesse/Makefile +16 -0
  25. capytaine/green_functions/LiangWuNoblesse/README.md +2 -0
  26. capytaine/green_functions/LiangWuNoblesse/test_program.f90 +28 -0
  27. capytaine/green_functions/__init__.py +2 -0
  28. capytaine/green_functions/abstract_green_function.py +64 -0
  29. capytaine/green_functions/delhommeau.py +507 -0
  30. capytaine/green_functions/hams.py +204 -0
  31. capytaine/green_functions/libs/Delhommeau_float32.cpython-310-x86_64-linux-gnu.so +0 -0
  32. capytaine/green_functions/libs/Delhommeau_float64.cpython-310-x86_64-linux-gnu.so +0 -0
  33. capytaine/green_functions/libs/__init__.py +0 -0
  34. capytaine/io/__init__.py +0 -0
  35. capytaine/io/bemio.py +153 -0
  36. capytaine/io/legacy.py +328 -0
  37. capytaine/io/mesh_loaders.py +1086 -0
  38. capytaine/io/mesh_writers.py +692 -0
  39. capytaine/io/meshio.py +38 -0
  40. capytaine/io/wamit.py +479 -0
  41. capytaine/io/xarray.py +668 -0
  42. capytaine/matrices/__init__.py +16 -0
  43. capytaine/matrices/block.py +592 -0
  44. capytaine/matrices/block_toeplitz.py +325 -0
  45. capytaine/matrices/builders.py +89 -0
  46. capytaine/matrices/linear_solvers.py +232 -0
  47. capytaine/matrices/low_rank.py +395 -0
  48. capytaine/meshes/__init__.py +6 -0
  49. capytaine/meshes/clipper.py +465 -0
  50. capytaine/meshes/collections.py +342 -0
  51. capytaine/meshes/geometry.py +409 -0
  52. capytaine/meshes/mesh_like_protocol.py +37 -0
  53. capytaine/meshes/meshes.py +890 -0
  54. capytaine/meshes/predefined/__init__.py +6 -0
  55. capytaine/meshes/predefined/cylinders.py +314 -0
  56. capytaine/meshes/predefined/rectangles.py +261 -0
  57. capytaine/meshes/predefined/spheres.py +62 -0
  58. capytaine/meshes/properties.py +276 -0
  59. capytaine/meshes/quadratures.py +80 -0
  60. capytaine/meshes/quality.py +448 -0
  61. capytaine/meshes/surface_integrals.py +63 -0
  62. capytaine/meshes/symmetric.py +462 -0
  63. capytaine/post_pro/__init__.py +6 -0
  64. capytaine/post_pro/free_surfaces.py +88 -0
  65. capytaine/post_pro/impedance.py +92 -0
  66. capytaine/post_pro/kochin.py +54 -0
  67. capytaine/post_pro/rao.py +60 -0
  68. capytaine/tools/__init__.py +0 -0
  69. capytaine/tools/cache_on_disk.py +26 -0
  70. capytaine/tools/deprecation_handling.py +18 -0
  71. capytaine/tools/lists_of_points.py +52 -0
  72. capytaine/tools/lru_cache.py +49 -0
  73. capytaine/tools/optional_imports.py +27 -0
  74. capytaine/tools/prony_decomposition.py +150 -0
  75. capytaine/tools/symbolic_multiplication.py +149 -0
  76. capytaine/tools/timer.py +66 -0
  77. capytaine/ui/__init__.py +0 -0
  78. capytaine/ui/cli.py +28 -0
  79. capytaine/ui/rich.py +5 -0
  80. capytaine/ui/vtk/__init__.py +3 -0
  81. capytaine/ui/vtk/animation.py +329 -0
  82. capytaine/ui/vtk/body_viewer.py +28 -0
  83. capytaine/ui/vtk/helpers.py +82 -0
  84. capytaine/ui/vtk/mesh_viewer.py +461 -0
  85. capytaine-2.3.1.dist-info/LICENSE +674 -0
  86. capytaine-2.3.1.dist-info/METADATA +750 -0
  87. capytaine-2.3.1.dist-info/RECORD +93 -0
  88. capytaine-2.3.1.dist-info/WHEEL +6 -0
  89. capytaine-2.3.1.dist-info/entry_points.txt +3 -0
  90. capytaine.libs/libgfortran-83c28eba.so.5.0.0 +0 -0
  91. capytaine.libs/libgomp-e985bcbb.so.1.0.0 +0 -0
  92. capytaine.libs/libmvec-2-583a17db.28.so +0 -0
  93. capytaine.libs/libquadmath-2284e583.so.0.0.0 +0 -0
@@ -0,0 +1,395 @@
1
+ """This module implements a class to describe a low-rank matrix as the tensor product of two smaller matrices.
2
+ In particular, an implementation of the Adaptive Cross Approximation is used to build such a matrix.
3
+
4
+ It takes inspiration from the following works:
5
+
6
+ * `openHmx module from Gypsilab by Matthieu Aussal (GPL licensed) <https://github.com/matthieuaussal/gypsilab>`_
7
+ * `HierarchicalMatrices by Markus Neumann (GPL licensed) <https://github.com/maekke97/HierarchicalMatrices>`_
8
+ """
9
+ # Copyright (C) 2017-2021 Matthieu Ancellin
10
+ # See LICENSE file at <https://github.com/mancellin/capytaine>
11
+
12
+ import logging
13
+
14
+ import numpy as np
15
+
16
+ LOG = logging.getLogger(__name__)
17
+
18
+
19
+ class NoConvergenceOfACA(Exception):
20
+ pass
21
+
22
+
23
+ class LowRankMatrix:
24
+ """Matrix defined as the tensor product of two small matrices.
25
+
26
+ Parameters
27
+ ----------
28
+ left_matrix: numpy.array
29
+ Matrix of shape (nb_cols, rank).
30
+ right_matrix: numpy.array
31
+ Matrix of shape (rank, nb_rows).
32
+
33
+ Attributes
34
+ ----------
35
+ shape: Tuple[int, int]
36
+ The shape of the full matrix.
37
+ rank: int
38
+ The rank of the full matrix.
39
+ dtype: numpy.dtype
40
+ Type of data in the matrix.
41
+ """
42
+
43
+ ndim = 2
44
+
45
+ ##############
46
+ # Creation #
47
+ ##############
48
+
49
+ def __init__(self, left_matrix, right_matrix):
50
+ self.left_matrix = left_matrix
51
+ self.right_matrix = right_matrix
52
+ self.shape = left_matrix.shape[0], right_matrix.shape[1]
53
+ assert left_matrix.shape[1] == right_matrix.shape[0], "Sizes of the left and right matrices do not match."
54
+ self.rank = left_matrix.shape[1] # == right_matrix.shape[0]
55
+ assert left_matrix.dtype == right_matrix.dtype, "Left and right matrices should have the same type of data."
56
+ self.dtype = left_matrix.dtype
57
+
58
+ @classmethod
59
+ def from_full_matrix_with_SVD(cls, full_matrix, max_rank):
60
+ """Create a low rank matrix from a full matrix using Singular Value Decomposition.
61
+
62
+ Parameters
63
+ ----------
64
+ full_matrix: numpy array
65
+ The matrix that will be approximated.
66
+ max_rank: int
67
+ Rank of the low-rank approximation.
68
+
69
+ Returns
70
+ -------
71
+ LowRankMatrix
72
+ """
73
+ u, s, v = np.linalg.svd(full_matrix)
74
+ left_matrix = u[:, 0:max_rank] @ np.diag(s[0:max_rank])
75
+ right_matrix = v[0:max_rank, :]
76
+ return cls(left_matrix, right_matrix)
77
+
78
+ @classmethod
79
+ def from_full_matrix_with_ACA(cls, full_matrix, max_rank=None, tol=0.0):
80
+ """Create a low rank matrix from a full matrix using Adaptive Cross Approximation.
81
+ The user should provide either the `max_rank` optional argument or the `tol` optional argument.
82
+
83
+ Parameters
84
+ ----------
85
+ full_matrix: numpy.array
86
+ The matrix that will be approximated.
87
+ max_rank: int, optional
88
+ The maximum rank allowed for the output low rank matrix.
89
+ The default value is half the size of the full matrix, that is no gain in storage space.
90
+ tol: float, optional
91
+ The tolerance on the relative error (default: 0).
92
+ If the Frobenius norm of the increment is lower than the tolerance, the iteration stops.
93
+ If the tolerance is set to 0, the resulting matrix will have the maximum rank defined by `max_rank`.
94
+
95
+ Returns
96
+ -------
97
+ LowRankMatrix
98
+ """
99
+ def get_row(i):
100
+ return full_matrix[i, :]
101
+
102
+ def get_col(j):
103
+ return full_matrix[:, j]
104
+
105
+ return cls.from_rows_and_cols_functions_with_ACA(
106
+ get_row, get_col, full_matrix.shape[0], full_matrix.shape[1],
107
+ max_rank=max_rank, tol=tol, dtype=full_matrix.dtype
108
+ )
109
+
110
+ @classmethod
111
+ def from_function_with_ACA(cls, func, nb_rows, nb_cols, max_rank=None, tol=0.0, dtype=np.float64):
112
+ """Create a low rank matrix from a function using Adaptive Cross Approximation.
113
+ The user should provide either the `max_rank` optional argument or the `tol` optional argument.
114
+
115
+ Parameters
116
+ ----------
117
+ func: Function
118
+ Function such that `func(i, j)` returns the value of the (i, j) entry of the full matrix.
119
+ nb_rows: int
120
+ Number of rows in the full matrix.
121
+ nb_cols: int
122
+ Number of cols in the full matrix.
123
+ max_rank: int, optional
124
+ The maximum rank allowed for the output low rank matrix.
125
+ The default value is half the size of the full matrix, that is no gain in storage space.
126
+ tol: float, optional
127
+ The tolerance on the relative error (default: 0).
128
+ If the Frobenius norm of the increment is lower than the tolerance, the iteration stops.
129
+ If the tolerance is set to 0, the resulting matrix will have the maximum rank defined by `max_rank`.
130
+ dtype: numpy.dtype, optional
131
+ Type of the data returned by the function.
132
+
133
+ Returns
134
+ -------
135
+ LowRankMatrix
136
+ """
137
+ def get_row(i):
138
+ return np.asarray([func(i, j) for j in range(nb_cols)])
139
+
140
+ def get_col(j):
141
+ return np.asarray([func(i, j) for i in range(nb_rows)])
142
+
143
+ return cls.from_rows_and_cols_functions_with_ACA(
144
+ get_row, get_col, nb_rows, nb_cols,
145
+ max_rank=max_rank, tol=tol, dtype=dtype
146
+ )
147
+
148
+ @classmethod
149
+ def from_rows_and_cols_functions_with_ACA(cls, get_row_func, get_col_func, nb_rows, nb_cols, max_rank=None, tol=0.0, dtype=np.float64):
150
+ """Create a low rank matrix from functions using Adaptive Cross Approximation.
151
+ The user should provide either the `max_rank` optional argument or the `tol` optional argument.
152
+
153
+ Parameters
154
+ ----------
155
+ get_row_func: Function
156
+ Function such that `get_row_func(i)` returns the `i`-th row of the full matrix.
157
+ get_col_func: Function
158
+ Function such that `get_col_func(j)` returns the `j`-th column of the full matrix.
159
+ nb_rows: int
160
+ Number of rows in the full matrix.
161
+ nb_cols: int
162
+ Number of columns in the full matrix.
163
+ max_rank: int, optional
164
+ The maximum rank allowed for the output low rank matrix.
165
+ The default value is half the size of the full matrix, that is no gain in storage space.
166
+ tol: float, optional
167
+ The tolerance on the relative error (default: 0).
168
+ If the Frobenius norm of the increment is lower than the tolerance, the iteration stops.
169
+ If the tolerance is set to 0, the resulting matrix will have the maximum rank defined by `max_rank`.
170
+ dtype: numpy.dtype, optional
171
+ The type of data in the low rank matrix (default: float64).
172
+
173
+ Returns
174
+ -------
175
+ LowRankMatrix
176
+ """
177
+ # Just some wrapping and unwrapping to use the multi-ACA below.
178
+ def get_row(i):
179
+ return [get_row_func(i)]
180
+
181
+ def get_col(j):
182
+ return [get_col_func(j)]
183
+
184
+ return cls.from_rows_and_cols_functions_with_multi_ACA(
185
+ get_row, get_col, nb_rows, nb_cols,
186
+ nb_matrices=1, id_main=0,
187
+ max_rank=max_rank, tol=tol, dtype=dtype
188
+ )[0]
189
+
190
+ @classmethod
191
+ def from_rows_and_cols_functions_with_multi_ACA(cls, get_row, get_col, nb_rows, nb_cols,
192
+ nb_matrices=1, id_main=0,
193
+ max_rank=None, tol=0.0, dtype=np.float64):
194
+ """Create several low rank matrices while running an Adaptive Cross Approximation.
195
+ The user should provide either the `max_rank` optional argument or the `tol` optional argument.
196
+
197
+ In Capytaine, the routines evaluating the influence matrices return the values of two matrices
198
+ (S and V) at once, because there is a lot of common computations in their evaluation.
199
+ The present function can be used to build the ACA of one of them while getting at the same time
200
+ an approximation of the other for free.
201
+
202
+ Freely adapted from the routine hmxACA.m from Gypsilab.
203
+
204
+ Parameters
205
+ ----------
206
+ get_row: Function
207
+ Function such that `get_row(i)` returns the `i`-th row of all the full matrices.
208
+ get_col: Function
209
+ Function such that `get_col(j)` returns the `j`-th column of all the full matrices.
210
+ nb_rows: int
211
+ Number of rows in all full matrices.
212
+ nb_cols: int
213
+ Number of columns in all full matrices.
214
+ nb_matrices: int, optional
215
+ The number of matrices approximated at the same time.
216
+ id_main: int, optional
217
+ The matrix used primarily in the ACA.
218
+ max_rank: int, optional
219
+ The maximum rank allowed for both output low rank matrices.
220
+ The default value is half the size of the full matrix, that is no gain in storage space.
221
+ tol: float, optional
222
+ The tolerance on the relative error (default: 0).
223
+ If the Frobenius norm of the increment is lower than the tolerance, the iteration stops.
224
+ If the tolerance is set to 0, the resulting matrix will have the maximum rank defined by `max_rank`.
225
+ dtype: numpy.dtype, optional
226
+ The type of data in both low rank matrices (default: float64).
227
+
228
+ Returns
229
+ -------
230
+ List[LowRankMatrix]
231
+ """
232
+ if max_rank is None and tol <= 0.0:
233
+ LOG.warning("No stopping criterion for the Adaptive Cross Approximation."
234
+ "Please provide either max_rank or tol.")
235
+
236
+ if max_rank is None:
237
+ max_rank = min(nb_rows, nb_cols)//2
238
+
239
+ # Initialize work matrices
240
+ left = np.zeros((nb_matrices, nb_rows, max_rank), dtype=dtype)
241
+ right = np.zeros((nb_matrices, max_rank, nb_cols), dtype=dtype)
242
+
243
+ squared_norm_of_low_rank_approximation = 0.0
244
+
245
+ # List of indices of unused entries in the full matrix
246
+ available_rows = list(range(nb_rows))
247
+ available_cols = list(range(nb_cols))
248
+
249
+ for l in range(max_rank):
250
+ # Pick a row
251
+ if l == 0:
252
+ relative_i = 0
253
+ # Could also have been chosen at random.
254
+ else:
255
+ relative_i = int(np.argmax(np.abs(left[id_main, available_rows, l-1])))
256
+ # The "int" is useless except for my type checker...
257
+
258
+ i = available_rows.pop(relative_i)
259
+ # relative_i is the index of the row in the list of remaining rows,
260
+ # e.g. if available_rows = [2, 7, 8] and relative_i = 2, the chosen
261
+ # row has index i = 8 in the full matrix.
262
+
263
+ # Add the chosen row to the approximation of all the matrices
264
+ one_row = get_row(i)
265
+ for id_mat in range(nb_matrices):
266
+ right[id_mat, l, :] = one_row[id_mat] - left[id_mat, i, :l] @ right[id_mat, :l, :]
267
+
268
+ # Pick a column
269
+ relative_j = int(np.argmax(np.abs(right[id_main, l, available_cols])))
270
+ j = available_cols.pop(relative_j)
271
+ # Similar to i above.
272
+
273
+ one_col = get_col(j)
274
+
275
+ # Add the column to the approximations of all matrices.
276
+ for id_mat in range(nb_matrices):
277
+ new_col = one_col[id_mat] - left[id_mat, :, :l] @ right[id_mat, :l, j]
278
+ pivot = new_col[i]
279
+ if abs(pivot) < 1e-12:
280
+ pivot = 1e-12
281
+ left[id_mat, :, l] = new_col/pivot
282
+
283
+ # Update norm of the full matrix
284
+ squared_norm_of_increment = np.real(
285
+ (np.conj(left[id_main, :, l]) @ left[id_main, :, l]) *
286
+ (np.conj(right[id_main, l, :]) @ right[id_main, l, :])
287
+ )
288
+
289
+ crossed_terms = (
290
+ (np.conj(left[id_main, :, l].T) @ left[id_main, :, :l]) @
291
+ (np.conj(right[id_main, l, :]) @ right[id_main, :l, :].T)
292
+ )
293
+ squared_norm_of_low_rank_approximation += squared_norm_of_increment + 2*np.real(crossed_terms)
294
+
295
+ if squared_norm_of_increment <= tol**2*squared_norm_of_low_rank_approximation:
296
+ LOG.debug(f"The ACA has found an approximation of rank {l}.")
297
+
298
+ if l == 0: # Edge case of the zero matrix, ...
299
+ l = 1 # ... we actually return a "rank 1" LowRankMatrix with coefficients equal to zero.
300
+
301
+ return [LowRankMatrix(left[id_mat, :, :l], right[id_mat, :l, :]) for id_mat in range(nb_matrices)]
302
+
303
+ if tol > 0:
304
+ LOG.warning(f"The ACA was unable to find a low rank approximation "
305
+ f"of rank lower or equal to {max_rank} with tolerance {tol:.2e} (latest iteration: "
306
+ f"{np.sqrt(squared_norm_of_increment):.2e}/{np.sqrt(squared_norm_of_low_rank_approximation):.2e}).")
307
+ raise NoConvergenceOfACA()
308
+
309
+ return [LowRankMatrix(left[id_mat, :, :], right[id_mat, :, :]) for id_mat in range(nb_matrices)]
310
+
311
+ ####################
312
+ # Representation #
313
+ ####################
314
+
315
+ def full_matrix(self, dtype=None):
316
+ if dtype is not None:
317
+ return (self.left_matrix @ self.right_matrix).astype(dtype)
318
+ else:
319
+ return self.left_matrix @ self.right_matrix
320
+
321
+ def __array__(self, dtype=None, copy=True):
322
+ if not copy:
323
+ raise ValueError("Making an ndarray out of a BlockMatrix requires copy")
324
+ return self.full_matrix(dtype=dtype)
325
+
326
+ @property
327
+ def stored_data_size(self):
328
+ return np.prod(self.left_matrix.shape) + np.prod(self.right_matrix.shape)
329
+
330
+ @property
331
+ def density(self):
332
+ return self.stored_data_size/np.prod(self.shape)
333
+
334
+ @property
335
+ def sparcity(self):
336
+ return 1 - self.density
337
+
338
+ ####################
339
+ # Transformation #
340
+ ####################
341
+
342
+ def recompress(self, tol=None, new_rank=None):
343
+ """Recompress the matrix to a lower rank. Based on the routine hmxQRSVD.m from Gipsylab."""
344
+ if new_rank is None:
345
+ new_rank = self.rank
346
+ QA, RA = np.linalg.qr(self.left_matrix)
347
+ QB, RB = np.linalg.qr(self.right_matrix.T)
348
+ U, S, V = np.linalg.svd(RA @ RB.T)
349
+ if tol is not None:
350
+ new_rank = np.count_nonzero(S/S[0] >= tol)
351
+ A = QA @ (U[:, :new_rank] @ np.diag(S[:new_rank]))
352
+ B = QB @ V[:, :new_rank]
353
+ return LowRankMatrix(A, B.T)
354
+
355
+ def __add__(self, other):
356
+ if isinstance(other, LowRankMatrix):
357
+ new_left = np.concatenate([self.left_matrix, other.left_matrix], axis=1)
358
+ new_right = np.concatenate([self.right_matrix, other.right_matrix], axis=0)
359
+ return LowRankMatrix(new_left, new_right).recompress(new_rank=min(self.rank, other.rank))
360
+ else:
361
+ return NotImplemented
362
+
363
+ def __neg__(self):
364
+ return LowRankMatrix(-self.left_matrix, self.right_matrix)
365
+
366
+ def __sub__(self, other):
367
+ return self + (-other)
368
+
369
+ def __truediv__(self, other):
370
+ from numbers import Number
371
+ if isinstance(other, Number):
372
+ return LowRankMatrix(self.left_matrix, self.right_matrix/other)
373
+ else:
374
+ return NotImplemented
375
+
376
+ def __matmul__(self, other):
377
+ if isinstance(other, np.ndarray) and len(other.shape) == 1:
378
+ return self._mul_with_vector(other)
379
+ else:
380
+ return NotImplemented
381
+
382
+ def _mul_with_vector(self, other):
383
+ return self.left_matrix @ (self.right_matrix @ other)
384
+
385
+ def __rmatmul__(self, other):
386
+ if isinstance(other, np.ndarray) and len(other.shape) == 1:
387
+ return self._rmul_with_vector(other)
388
+ else:
389
+ return NotImplemented
390
+
391
+ def _rmul_with_vector(self, other):
392
+ return (other @ self.left_matrix) @ self.right_matrix
393
+
394
+ def astype(self, dtype):
395
+ return LowRankMatrix(self.left_matrix.astype(dtype), self.right_matrix.astype(dtype))
@@ -0,0 +1,6 @@
1
+ # Copyright (C) 2017-2019 Matthieu Ancellin, based on the work of François Rongère
2
+ # See LICENSE file at <https://github.com/mancellin/capytaine>
3
+
4
+ from capytaine.meshes.meshes import Mesh
5
+ from capytaine.meshes.collections import CollectionOfMeshes
6
+ from capytaine.meshes.symmetric import ReflectionSymmetricMesh, TranslationalSymmetricMesh, AxialSymmetricMesh