capytaine 2.0__cp310-cp310-macosx_10_9_x86_64.whl → 2.2__cp310-cp310-macosx_10_9_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 (90) hide show
  1. capytaine/.dylibs/libgcc_s.1.1.dylib +0 -0
  2. capytaine/.dylibs/libgfortran.5.dylib +0 -0
  3. capytaine/.dylibs/libquadmath.0.dylib +0 -0
  4. capytaine/__about__.py +3 -3
  5. capytaine/__init__.py +5 -3
  6. capytaine/bem/airy_waves.py +4 -6
  7. capytaine/bem/engines.py +146 -25
  8. capytaine/bem/problems_and_results.py +217 -106
  9. capytaine/bem/solver.py +179 -47
  10. capytaine/bodies/__init__.py +1 -1
  11. capytaine/bodies/bodies.py +207 -39
  12. capytaine/bodies/predefined/__init__.py +0 -2
  13. capytaine/bodies/predefined/cylinders.py +0 -2
  14. capytaine/bodies/predefined/rectangles.py +0 -2
  15. capytaine/bodies/predefined/spheres.py +0 -2
  16. capytaine/green_functions/abstract_green_function.py +0 -3
  17. capytaine/green_functions/delhommeau.py +225 -63
  18. capytaine/green_functions/libs/Delhommeau_float32.cpython-310-darwin.so +0 -0
  19. capytaine/green_functions/libs/Delhommeau_float64.cpython-310-darwin.so +0 -0
  20. capytaine/io/bemio.py +17 -16
  21. capytaine/io/legacy.py +52 -20
  22. capytaine/io/mesh_loaders.py +49 -27
  23. capytaine/io/mesh_writers.py +1 -3
  24. capytaine/io/meshio.py +4 -1
  25. capytaine/io/xarray.py +73 -35
  26. capytaine/matrices/__init__.py +0 -2
  27. capytaine/matrices/block.py +23 -2
  28. capytaine/matrices/block_toeplitz.py +0 -2
  29. capytaine/matrices/builders.py +2 -4
  30. capytaine/matrices/linear_solvers.py +84 -7
  31. capytaine/matrices/low_rank.py +0 -2
  32. capytaine/meshes/__init__.py +0 -2
  33. capytaine/meshes/clipper.py +0 -3
  34. capytaine/meshes/collections.py +49 -20
  35. capytaine/meshes/geometry.py +3 -6
  36. capytaine/meshes/meshes.py +170 -81
  37. capytaine/meshes/predefined/__init__.py +0 -1
  38. capytaine/meshes/predefined/cylinders.py +48 -7
  39. capytaine/meshes/predefined/rectangles.py +43 -10
  40. capytaine/meshes/predefined/spheres.py +15 -4
  41. capytaine/meshes/properties.py +43 -2
  42. capytaine/meshes/quadratures.py +80 -0
  43. capytaine/meshes/quality.py +1 -3
  44. capytaine/meshes/surface_integrals.py +0 -1
  45. capytaine/meshes/symmetric.py +55 -14
  46. capytaine/post_pro/free_surfaces.py +4 -7
  47. capytaine/post_pro/impedance.py +12 -10
  48. capytaine/post_pro/kochin.py +5 -3
  49. capytaine/post_pro/rao.py +16 -22
  50. capytaine/tools/cache_on_disk.py +26 -0
  51. capytaine/tools/deprecation_handling.py +2 -2
  52. capytaine/tools/lists_of_points.py +13 -3
  53. capytaine/tools/lru_cache.py +23 -29
  54. capytaine/tools/optional_imports.py +0 -2
  55. capytaine/tools/prony_decomposition.py +0 -3
  56. capytaine/tools/symbolic_multiplication.py +107 -0
  57. capytaine/ui/cli.py +7 -27
  58. capytaine/ui/rich.py +5 -0
  59. capytaine/ui/vtk/__init__.py +0 -3
  60. capytaine/ui/vtk/animation.py +28 -8
  61. capytaine/ui/vtk/body_viewer.py +2 -2
  62. capytaine/ui/vtk/helpers.py +0 -3
  63. capytaine/ui/vtk/mesh_viewer.py +0 -3
  64. {capytaine-2.0.dist-info → capytaine-2.2.dist-info}/METADATA +32 -14
  65. capytaine-2.2.dist-info/RECORD +76 -0
  66. capytaine/green_functions/libDelhommeau/.gitignore +0 -5
  67. capytaine/green_functions/libDelhommeau/LICENSE +0 -203
  68. capytaine/green_functions/libDelhommeau/Makefile +0 -123
  69. capytaine/green_functions/libDelhommeau/README.md +0 -15
  70. capytaine/green_functions/libDelhommeau/benchmarks/openmp/benchmark_omp.f90 +0 -212
  71. capytaine/green_functions/libDelhommeau/benchmarks/openmp/display_mesh.py +0 -7
  72. capytaine/green_functions/libDelhommeau/benchmarks/openmp/read_output.py +0 -82
  73. capytaine/green_functions/libDelhommeau/benchmarks/profiling/benchmark_profiling.f90 +0 -201
  74. capytaine/green_functions/libDelhommeau/benchmarks/tabulations/benchmark_tabulation.f90 +0 -87
  75. capytaine/green_functions/libDelhommeau/examples/minimal/minimal_example.f90 +0 -213
  76. capytaine/green_functions/libDelhommeau/examples/minimal/minimal_example.py +0 -60
  77. capytaine/green_functions/libDelhommeau/src/Delhommeau_integrals.f90 +0 -311
  78. capytaine/green_functions/libDelhommeau/src/Green_Rankine.f90 +0 -148
  79. capytaine/green_functions/libDelhommeau/src/Green_wave.f90 +0 -303
  80. capytaine/green_functions/libDelhommeau/src/constants.f90 +0 -16
  81. capytaine/green_functions/libDelhommeau/src/float32.f90 +0 -7
  82. capytaine/green_functions/libDelhommeau/src/float64.f90 +0 -7
  83. capytaine/green_functions/libDelhommeau/src/matrices.f90 +0 -274
  84. capytaine/green_functions/libDelhommeau/src/old_Prony_decomposition.f90 +0 -636
  85. capytaine/green_functions/libs/XieDelhommeau_float32.cpython-310-darwin.so +0 -0
  86. capytaine/green_functions/libs/XieDelhommeau_float64.cpython-310-darwin.so +0 -0
  87. capytaine-2.0.dist-info/RECORD +0 -93
  88. {capytaine-2.0.dist-info → capytaine-2.2.dist-info}/LICENSE +0 -0
  89. {capytaine-2.0.dist-info → capytaine-2.2.dist-info}/WHEEL +0 -0
  90. {capytaine-2.0.dist-info → capytaine-2.2.dist-info}/entry_points.txt +0 -0
Binary file
Binary file
Binary file
capytaine/__about__.py CHANGED
@@ -1,14 +1,14 @@
1
- #!/usr/bin/env python
1
+ #!/usr/bin/env python3
2
2
 
3
3
  __all__ = ["__title__", "__description__", "__version__", "__author__", "__uri__", "__license__"]
4
4
 
5
5
  __title__ = "capytaine"
6
6
  __description__ = """Python BEM solver for linear potential flow, based on Nemoh"""
7
7
 
8
- __version__ = "2.0"
8
+ __version__ = "2.2"
9
9
 
10
10
  __author__ = "Matthieu Ancellin"
11
- __uri__ = "https://github.com/mancellin/capytaine"
11
+ __uri__ = "https://github.com/capytaine/capytaine"
12
12
  __license__ = "GPL-3.0"
13
13
 
14
14
 
capytaine/__init__.py CHANGED
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env python
2
- # coding: utf-8
3
1
  # Copyright (C) 2017-2019 Matthieu Ancellin
4
2
  # See LICENSE file at <https://github.com/mancellin/capytaine>
5
3
 
@@ -24,10 +22,14 @@ from capytaine.bodies.predefined.rectangles import Rectangle, RectangularParalle
24
22
 
25
23
  from capytaine.bem.problems_and_results import RadiationProblem, DiffractionProblem
26
24
  from capytaine.bem.solver import BEMSolver
27
- from capytaine.bem.engines import BasicMatrixEngine, HierarchicalToeplitzMatrixEngine
25
+ from capytaine.bem.engines import BasicMatrixEngine, HierarchicalToeplitzMatrixEngine, HierarchicalPrecondMatrixEngine
28
26
  from capytaine.green_functions.delhommeau import Delhommeau, XieDelhommeau
29
27
 
30
28
  from capytaine.post_pro.free_surfaces import FreeSurface
31
29
 
32
30
  from capytaine.io.mesh_loaders import load_mesh
33
31
  from capytaine.io.xarray import assemble_dataset
32
+
33
+ from capytaine.ui.rich import set_logging
34
+
35
+ set_logging(level="WARNING")
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env python
2
- # coding: utf-8
3
1
  """Computing the potential and velocity of Airy wave."""
4
2
  # Copyright (C) 2017-2019 Matthieu Ancellin
5
3
  # See LICENSE file at <https://github.com/mancellin/capytaine>
@@ -27,7 +25,8 @@ def airy_waves_potential(points, pb):
27
25
  x, y, z = points.T
28
26
  k = pb.wavenumber
29
27
  h = pb.water_depth
30
- wbar = x * np.cos(pb.wave_direction) + y * np.sin(pb.wave_direction)
28
+ beta = pb.encounter_wave_direction
29
+ wbar = x * np.cos(beta) + y * np.sin(beta)
31
30
 
32
31
  if 0 <= k*h < 20:
33
32
  cih = np.cosh(k*(z+h))/np.cosh(k*h)
@@ -61,8 +60,9 @@ def airy_waves_velocity(points, pb):
61
60
  x, y, z = points.T
62
61
  k = pb.wavenumber
63
62
  h = pb.water_depth
63
+ beta = pb.encounter_wave_direction
64
64
 
65
- wbar = x * np.cos(pb.wave_direction) + y * np.sin(pb.wave_direction)
65
+ wbar = x * np.cos(beta) + y * np.sin(beta)
66
66
 
67
67
  if 0 <= k*h < 20:
68
68
  cih = np.cosh(k*(z+h))/np.cosh(k*h)
@@ -104,5 +104,3 @@ def airy_waves_free_surface_elevation(points, pb):
104
104
  """
105
105
  points, output_shape = _normalize_free_surface_points(points)
106
106
  return 1j * pb.omega / pb.g * airy_waves_potential(points, pb).reshape(output_shape)
107
-
108
-
capytaine/bem/engines.py CHANGED
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env python
2
- # coding: utf-8
3
1
  """Definition of the methods to build influence matrices, using possibly some sparse structures."""
4
2
  # Copyright (C) 2017-2019 Matthieu Ancellin
5
3
  # See LICENSE file at <https://github.com/mancellin/capytaine>
@@ -8,6 +6,9 @@ import logging
8
6
  from abc import ABC, abstractmethod
9
7
 
10
8
  import numpy as np
9
+ from scipy.linalg import lu_factor
10
+ from scipy.sparse import coo_matrix
11
+ from scipy.sparse import linalg as ssl
11
12
 
12
13
  from capytaine.meshes.collections import CollectionOfMeshes
13
14
  from capytaine.meshes.symmetric import ReflectionSymmetricMesh, TranslationalSymmetricMesh, AxialSymmetricMesh
@@ -16,7 +17,7 @@ from capytaine.matrices import linear_solvers
16
17
  from capytaine.matrices.block import BlockMatrix
17
18
  from capytaine.matrices.low_rank import LowRankMatrix, NoConvergenceOfACA
18
19
  from capytaine.matrices.block_toeplitz import BlockSymmetricToeplitzMatrix, BlockToeplitzMatrix, BlockCirculantMatrix
19
- from capytaine.tools.lru_cache import delete_first_lru_cache
20
+ from capytaine.tools.lru_cache import lru_cache_with_strict_maxsize
20
21
 
21
22
  LOG = logging.getLogger(__name__)
22
23
 
@@ -29,7 +30,7 @@ class MatrixEngine(ABC):
29
30
  """Abstract method to build a matrix."""
30
31
 
31
32
  @abstractmethod
32
- def build_matrices(self, mesh1, mesh2, free_surface, water_depth, wavenumber, green_function):
33
+ def build_matrices(self, mesh1, mesh2, free_surface, water_depth, wavenumber, green_function, adjoint_double_layer):
33
34
  pass
34
35
 
35
36
  def build_S_matrix(self, *args, **kwargs):
@@ -71,7 +72,7 @@ class BasicMatrixEngine(MatrixEngine):
71
72
  self.linear_solver = linear_solver
72
73
 
73
74
  if matrix_cache_size > 0:
74
- self.build_matrices = delete_first_lru_cache(maxsize=matrix_cache_size)(self.build_matrices)
75
+ self.build_matrices = lru_cache_with_strict_maxsize(maxsize=matrix_cache_size)(self.build_matrices)
75
76
 
76
77
  self.exportable_settings = {
77
78
  'engine': 'BasicMatrixEngine',
@@ -90,7 +91,7 @@ class BasicMatrixEngine(MatrixEngine):
90
91
  def _repr_pretty_(self, p, cycle):
91
92
  p.text(self.__str__())
92
93
 
93
- def build_matrices(self, mesh1, mesh2, free_surface, water_depth, wavenumber, green_function):
94
+ def build_matrices(self, mesh1, mesh2, free_surface, water_depth, wavenumber, green_function, adjoint_double_layer=True):
94
95
  r"""Build the influence matrices between mesh1 and mesh2.
95
96
 
96
97
  Parameters
@@ -107,6 +108,8 @@ class BasicMatrixEngine(MatrixEngine):
107
108
  wavenumber (default: 1.0)
108
109
  green_function: AbstractGreenFunction
109
110
  object with an "evaluate" method that computes the Green function.
111
+ adjoint_double_layer: bool, optional
112
+ compute double layer for direct method (F) or adjoint double layer for indirect method (T) matrices (default: True)
110
113
 
111
114
  Returns
112
115
  -------
@@ -129,7 +132,7 @@ class BasicMatrixEngine(MatrixEngine):
129
132
 
130
133
  else:
131
134
  return green_function.evaluate(
132
- mesh1, mesh2, free_surface, water_depth, wavenumber,
135
+ mesh1, mesh2, free_surface, water_depth, wavenumber, adjoint_double_layer=adjoint_double_layer
133
136
  )
134
137
 
135
138
  ###################################
@@ -153,7 +156,7 @@ class HierarchicalToeplitzMatrixEngine(MatrixEngine):
153
156
  def __init__(self, *, ACA_distance=8.0, ACA_tol=1e-2, matrix_cache_size=1):
154
157
 
155
158
  if matrix_cache_size > 0:
156
- self.build_matrices = delete_first_lru_cache(maxsize=matrix_cache_size)(self.build_matrices)
159
+ self.build_matrices = lru_cache_with_strict_maxsize(maxsize=matrix_cache_size)(self.build_matrices)
157
160
 
158
161
  self.ACA_distance = ACA_distance
159
162
  self.ACA_tol = ACA_tol
@@ -179,9 +182,18 @@ class HierarchicalToeplitzMatrixEngine(MatrixEngine):
179
182
 
180
183
  def build_matrices(self,
181
184
  mesh1, mesh2, free_surface, water_depth, wavenumber, green_function,
182
- _rec_depth=1):
185
+ adjoint_double_layer=True):
186
+
187
+ return self._build_matrices(
188
+ mesh1, mesh2, free_surface, water_depth, wavenumber, green_function,
189
+ adjoint_double_layer, _rec_depth=1)
190
+
191
+
192
+ def _build_matrices(self,
193
+ mesh1, mesh2, free_surface, water_depth, wavenumber, green_function,
194
+ adjoint_double_layer, _rec_depth=1):
183
195
  """Recursively builds a hierarchical matrix between mesh1 and mesh2.
184
-
196
+
185
197
  Same arguments as :func:`BasicMatrixEngine.build_matrices`.
186
198
 
187
199
  :code:`_rec_depth` keeps track of the recursion depth only for pretty log printing.
@@ -208,12 +220,12 @@ class HierarchicalToeplitzMatrixEngine(MatrixEngine):
208
220
 
209
221
  LOG.debug(log_entry + " using mirror symmetry.")
210
222
 
211
- S_a, V_a = self.build_matrices(
223
+ S_a, V_a = self._build_matrices(
212
224
  mesh1[0], mesh2[0], free_surface, water_depth, wavenumber, green_function,
213
- _rec_depth=_rec_depth+1)
214
- S_b, V_b = self.build_matrices(
225
+ adjoint_double_layer=adjoint_double_layer, _rec_depth=_rec_depth+1)
226
+ S_b, V_b = self._build_matrices(
215
227
  mesh1[0], mesh2[1], free_surface, water_depth, wavenumber, green_function,
216
- _rec_depth=_rec_depth+1)
228
+ adjoint_double_layer=adjoint_double_layer, _rec_depth=_rec_depth+1)
217
229
 
218
230
  return BlockSymmetricToeplitzMatrix([[S_a, S_b]]), BlockSymmetricToeplitzMatrix([[V_a, V_b]])
219
231
 
@@ -226,15 +238,15 @@ class HierarchicalToeplitzMatrixEngine(MatrixEngine):
226
238
 
227
239
  S_list, V_list = [], []
228
240
  for submesh in mesh2:
229
- S, V = self.build_matrices(
241
+ S, V = self._build_matrices(
230
242
  mesh1[0], submesh, free_surface, water_depth, wavenumber, green_function,
231
- _rec_depth=_rec_depth+1)
243
+ adjoint_double_layer=adjoint_double_layer, _rec_depth=_rec_depth+1)
232
244
  S_list.append(S)
233
245
  V_list.append(V)
234
246
  for submesh in mesh1[1:][::-1]:
235
- S, V = self.build_matrices(
247
+ S, V = self._build_matrices(
236
248
  submesh, mesh2[0], free_surface, water_depth, wavenumber, green_function,
237
- _rec_depth=_rec_depth+1)
249
+ adjoint_double_layer=adjoint_double_layer, _rec_depth=_rec_depth+1)
238
250
  S_list.append(S)
239
251
  V_list.append(V)
240
252
 
@@ -249,9 +261,9 @@ class HierarchicalToeplitzMatrixEngine(MatrixEngine):
249
261
 
250
262
  S_line, V_line = [], []
251
263
  for submesh in mesh2[:mesh2.nb_submeshes]:
252
- S, V = self.build_matrices(
264
+ S, V = self._build_matrices(
253
265
  mesh1[0], submesh, free_surface, water_depth, wavenumber, green_function,
254
- _rec_depth=_rec_depth+1)
266
+ adjoint_double_layer=adjoint_double_layer, _rec_depth=_rec_depth+1)
255
267
  S_line.append(S)
256
268
  V_line.append(V)
257
269
 
@@ -266,14 +278,16 @@ class HierarchicalToeplitzMatrixEngine(MatrixEngine):
266
278
  def get_row_func(i):
267
279
  s, v = green_function.evaluate(
268
280
  mesh1.extract_one_face(i), mesh2,
269
- free_surface, water_depth, wavenumber
281
+ free_surface, water_depth, wavenumber,
282
+ adjoint_double_layer=adjoint_double_layer
270
283
  )
271
284
  return s.flatten(), v.flatten()
272
285
 
273
286
  def get_col_func(j):
274
287
  s, v = green_function.evaluate(
275
288
  mesh1, mesh2.extract_one_face(j),
276
- free_surface, water_depth, wavenumber
289
+ free_surface, water_depth, wavenumber,
290
+ adjoint_double_layer=adjoint_double_layer
277
291
  )
278
292
  return s.flatten(), v.flatten()
279
293
 
@@ -297,9 +311,9 @@ class HierarchicalToeplitzMatrixEngine(MatrixEngine):
297
311
  for submesh1 in mesh1:
298
312
  S_line, V_line = [], []
299
313
  for submesh2 in mesh2:
300
- S, V = self.build_matrices(
314
+ S, V = self._build_matrices(
301
315
  submesh1, submesh2, free_surface, water_depth, wavenumber, green_function,
302
- _rec_depth=_rec_depth+1)
316
+ adjoint_double_layer=adjoint_double_layer, _rec_depth=_rec_depth+1)
303
317
 
304
318
  S_line.append(S)
305
319
  V_line.append(V)
@@ -314,7 +328,114 @@ class HierarchicalToeplitzMatrixEngine(MatrixEngine):
314
328
  LOG.debug(log_entry)
315
329
 
316
330
  S, V = green_function.evaluate(
317
- mesh1, mesh2, free_surface, water_depth, wavenumber,
331
+ mesh1, mesh2, free_surface, water_depth, wavenumber, adjoint_double_layer=adjoint_double_layer
318
332
  )
319
333
  return S, V
320
334
 
335
+ class HierarchicalPrecondMatrixEngine(HierarchicalToeplitzMatrixEngine):
336
+ """An experimental matrix engine that build a hierarchical matrix with
337
+ some block-Toeplitz structure.
338
+
339
+ Parameters
340
+ ----------
341
+ ACA_distance: float, optional
342
+ Above this distance, the ACA is used to approximate the matrix with a low-rank block.
343
+ ACA_tol: float, optional
344
+ The tolerance of the ACA when building a low-rank matrix.
345
+ matrix_cache_size: int, optional
346
+ number of matrices to keep in cache
347
+ """
348
+
349
+ def __init__(self, *, ACA_distance=8.0, ACA_tol=1e-2, matrix_cache_size=1):
350
+ super().__init__(ACA_distance=ACA_distance, ACA_tol=ACA_tol, matrix_cache_size=matrix_cache_size)
351
+ self.linear_solver = linear_solvers.solve_precond_gmres
352
+
353
+ def build_matrices(self,
354
+ mesh1, mesh2, free_surface, water_depth, wavenumber,
355
+ green_function, adjoint_double_layer=True):
356
+ """Recursively builds a hierarchical matrix between mesh1 and mesh2,
357
+ and precomputes some of the quantities needed for the preconditioner.
358
+
359
+ Same arguments as :func:`BasicMatrixEngine.build_matrices`, except for rec_depth
360
+ """
361
+ # Build the matrices using the method of the parent class
362
+ S, K = super().build_matrices(mesh1, mesh2, free_surface, water_depth,
363
+ wavenumber, green_function,
364
+ adjoint_double_layer=adjoint_double_layer)
365
+
366
+ path_to_leaf = mesh1.path_to_leaf()
367
+
368
+ n = len(path_to_leaf)
369
+ N = K.shape[0]
370
+
371
+ # Navigate to the diagonal blocks and compute their LU decompositions
372
+ DLU = []
373
+ diag_shapes = []
374
+ for leaf in range(n):
375
+ # Navigate to the block containing the one we need
376
+ # (one layer above in the dendrogram)
377
+ #upper_block = self.access_block_by_path(K, path_to_leaf[leaf][:-1])
378
+ upper_block = K.access_block_by_path(path_to_leaf[leaf][:-1])
379
+ # find the local index in the full path
380
+ ind = path_to_leaf[leaf][-1]
381
+ # compute the LU decomposition and add to the list
382
+ DLU.append(lu_factor(upper_block.all_blocks[ind, ind]))
383
+ diag_shapes.append(upper_block.all_blocks[ind, ind].shape[0])
384
+
385
+ # Build the restriction and precompute its multiplication by K
386
+ R = np.zeros((n, N), dtype=complex)
387
+ RA = np.zeros((n, N), dtype=complex)
388
+ for ii in range(n):
389
+ row_slice = slice(sum(diag_shapes[:ii]), sum(diag_shapes[:ii+1]))
390
+ R[ii, row_slice] = 1
391
+ # Compute the multiplication using only the relevant slices of K
392
+ # The slices are found by navigating the tree
393
+ #RA[ii, :] = self.slice_rmatvec(R[ii, :], ii)
394
+ Aloc = K
395
+ v = R[ii, :]
396
+ va = np.zeros(N, dtype=complex)
397
+ free = [0, N]
398
+
399
+ for lvl, jj in enumerate(path_to_leaf[ii]):
400
+
401
+ Nrows = Aloc.all_blocks[jj, jj].shape[0]
402
+
403
+ if jj==0:
404
+ v = v[:Nrows]
405
+ w = v @ Aloc.all_blocks[0,1]
406
+ va[free[1]-len(w) : free[1]] = w
407
+ free[1] = free[1] - len(w)
408
+ else:
409
+ v = v[-Nrows:]
410
+ w = v @ Aloc.all_blocks[1, 0]
411
+ va[free[0] : free[0]+len(w)] = w
412
+ free[0] = free[0] + len(w)
413
+
414
+ Aloc = Aloc.all_blocks[jj, jj]
415
+
416
+ if lvl == len(path_to_leaf[ii])-1:
417
+ w = v@Aloc
418
+ va[free[0] : free[1]] = w
419
+ free[0] = free[0] + len(w)
420
+
421
+ RA[ii, :] = va
422
+
423
+ Ac = RA @ R.T
424
+ AcLU = lu_factor(Ac)
425
+
426
+ # Now navigate again to the diagonal blocks and set them to zero
427
+ for leaf in range(n):
428
+ upper_block = K.access_block_by_path(path_to_leaf[leaf][:-1])
429
+ ind = path_to_leaf[leaf][-1]
430
+ # turn the diagonal block into a zero sparse matrix
431
+ upper_block.all_blocks[ind, ind] = coo_matrix(upper_block.all_blocks[ind, ind].shape)
432
+
433
+ def PinvA_mv(v):
434
+ v = v + 1j*np.zeros(N)
435
+ return v - linear_solvers._block_Jacobi_coarse_corr(
436
+ K, np.zeros(N, dtype=complex), v,
437
+ R, RA, AcLU, DLU, diag_shapes, n)
438
+
439
+ PinvA = ssl.LinearOperator((N, N), matvec=PinvA_mv)
440
+
441
+ return S, (K, R, RA, AcLU, DLU, diag_shapes, n, PinvA)