capytaine 2.3.1__cp312-cp312-win_amd64.whl → 3.0.0a1__cp312-cp312-win_amd64.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.
- capytaine/__about__.py +7 -2
- capytaine/__init__.py +11 -15
- capytaine/bem/engines.py +234 -354
- capytaine/bem/problems_and_results.py +14 -13
- capytaine/bem/solver.py +204 -80
- capytaine/bodies/bodies.py +278 -869
- capytaine/bodies/dofs.py +136 -9
- capytaine/bodies/hydrostatics.py +540 -0
- capytaine/bodies/multibodies.py +216 -0
- capytaine/green_functions/{libs/Delhommeau_float32.cp312-win_amd64.dll.a → Delhommeau_float32.cp312-win_amd64.dll.a} +0 -0
- capytaine/green_functions/Delhommeau_float32.cp312-win_amd64.pyd +0 -0
- capytaine/green_functions/{libs/Delhommeau_float64.cp312-win_amd64.dll.a → Delhommeau_float64.cp312-win_amd64.dll.a} +0 -0
- capytaine/green_functions/Delhommeau_float64.cp312-win_amd64.pyd +0 -0
- capytaine/green_functions/abstract_green_function.py +2 -2
- capytaine/green_functions/delhommeau.py +31 -16
- capytaine/green_functions/hams.py +19 -13
- capytaine/io/legacy.py +3 -103
- capytaine/io/xarray.py +11 -6
- capytaine/meshes/__init__.py +2 -6
- capytaine/meshes/abstract_meshes.py +375 -0
- capytaine/meshes/clean.py +302 -0
- capytaine/meshes/clip.py +347 -0
- capytaine/meshes/export.py +89 -0
- capytaine/meshes/geometry.py +244 -394
- capytaine/meshes/io.py +433 -0
- capytaine/meshes/meshes.py +617 -681
- capytaine/meshes/predefined/cylinders.py +22 -56
- capytaine/meshes/predefined/rectangles.py +26 -85
- capytaine/meshes/predefined/spheres.py +4 -11
- capytaine/meshes/quality.py +118 -407
- capytaine/meshes/surface_integrals.py +48 -29
- capytaine/meshes/symmetric_meshes.py +641 -0
- capytaine/meshes/visualization.py +353 -0
- capytaine/post_pro/free_surfaces.py +1 -4
- capytaine/post_pro/kochin.py +10 -10
- capytaine/tools/block_circulant_matrices.py +275 -0
- capytaine/tools/lists_of_points.py +2 -2
- capytaine/tools/memory_monitor.py +45 -0
- capytaine/tools/symbolic_multiplication.py +13 -1
- capytaine/tools/timer.py +58 -34
- capytaine-3.0.0a1.dist-info/DELVEWHEEL +2 -0
- {capytaine-2.3.1.dist-info → capytaine-3.0.0a1.dist-info}/METADATA +7 -2
- capytaine-3.0.0a1.dist-info/RECORD +70 -0
- capytaine/bodies/predefined/__init__.py +0 -6
- capytaine/bodies/predefined/cylinders.py +0 -151
- capytaine/bodies/predefined/rectangles.py +0 -111
- capytaine/bodies/predefined/spheres.py +0 -70
- capytaine/green_functions/FinGreen3D/.gitignore +0 -1
- capytaine/green_functions/FinGreen3D/FinGreen3D.f90 +0 -3589
- capytaine/green_functions/FinGreen3D/LICENSE +0 -165
- capytaine/green_functions/FinGreen3D/Makefile +0 -16
- capytaine/green_functions/FinGreen3D/README.md +0 -24
- capytaine/green_functions/FinGreen3D/test_program.f90 +0 -39
- capytaine/green_functions/LiangWuNoblesse/.gitignore +0 -1
- capytaine/green_functions/LiangWuNoblesse/LICENSE +0 -504
- capytaine/green_functions/LiangWuNoblesse/LiangWuNoblesseWaveTerm.f90 +0 -751
- capytaine/green_functions/LiangWuNoblesse/Makefile +0 -16
- capytaine/green_functions/LiangWuNoblesse/README.md +0 -2
- capytaine/green_functions/LiangWuNoblesse/test_program.f90 +0 -28
- capytaine/green_functions/libs/Delhommeau_float32.cp312-win_amd64.pyd +0 -0
- capytaine/green_functions/libs/Delhommeau_float64.cp312-win_amd64.pyd +0 -0
- capytaine/green_functions/libs/__init__.py +0 -0
- capytaine/io/mesh_loaders.py +0 -1086
- capytaine/io/mesh_writers.py +0 -692
- capytaine/io/meshio.py +0 -38
- capytaine/matrices/__init__.py +0 -16
- capytaine/matrices/block.py +0 -592
- capytaine/matrices/block_toeplitz.py +0 -325
- capytaine/matrices/builders.py +0 -89
- capytaine/matrices/linear_solvers.py +0 -232
- capytaine/matrices/low_rank.py +0 -395
- capytaine/meshes/clipper.py +0 -465
- capytaine/meshes/collections.py +0 -342
- capytaine/meshes/mesh_like_protocol.py +0 -37
- capytaine/meshes/properties.py +0 -276
- capytaine/meshes/quadratures.py +0 -80
- capytaine/meshes/symmetric.py +0 -462
- capytaine/tools/lru_cache.py +0 -49
- capytaine/ui/vtk/__init__.py +0 -3
- capytaine/ui/vtk/animation.py +0 -329
- capytaine/ui/vtk/body_viewer.py +0 -28
- capytaine/ui/vtk/helpers.py +0 -82
- capytaine/ui/vtk/mesh_viewer.py +0 -461
- capytaine-2.3.1.dist-info/DELVEWHEEL +0 -2
- capytaine-2.3.1.dist-info/RECORD +0 -97
- {capytaine-2.3.1.dist-info → capytaine-3.0.0a1.dist-info}/LICENSE +0 -0
- {capytaine-2.3.1.dist-info → capytaine-3.0.0a1.dist-info}/WHEEL +0 -0
- {capytaine-2.3.1.dist-info → capytaine-3.0.0a1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from itertools import chain, accumulate
|
|
5
|
+
from typing import Union, List, Optional
|
|
6
|
+
from functools import cached_property, lru_cache
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import xarray as xr
|
|
10
|
+
|
|
11
|
+
from capytaine.bodies.dofs import (
|
|
12
|
+
AbstractDof,
|
|
13
|
+
DofOnSubmesh,
|
|
14
|
+
add_dofs_labels_to_vector,
|
|
15
|
+
add_dofs_labels_to_matrix
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
LOG = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Multibody:
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
bodies: List[Union[FloatingBody, Multibody]],
|
|
25
|
+
# own_dofs: Optional[Dict[str, np.array]] = None,
|
|
26
|
+
*,
|
|
27
|
+
name: Optional[str] = None
|
|
28
|
+
):
|
|
29
|
+
self.bodies = bodies
|
|
30
|
+
|
|
31
|
+
if len(set(b.name for b in self.bodies)) < len(self.bodies):
|
|
32
|
+
raise ValueError(
|
|
33
|
+
"In Multibody, all component bodies must have a distinct name.\n"
|
|
34
|
+
f"Got: {[b.name for b in self.bodies]}"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# if own_dofs is None:
|
|
38
|
+
# self.own_dofs = {}
|
|
39
|
+
# else:
|
|
40
|
+
# self.own_dofs = own_dofs
|
|
41
|
+
|
|
42
|
+
if name is None:
|
|
43
|
+
self.name = '+'.join(b.name for b in self.bodies)
|
|
44
|
+
else:
|
|
45
|
+
self.name = name
|
|
46
|
+
|
|
47
|
+
# Keep legacy behavior of former mesh joining
|
|
48
|
+
for matrix_name in ["inertia_matrix", "hydrostatic_stiffness"]:
|
|
49
|
+
if all(hasattr(body, matrix_name) for body in bodies):
|
|
50
|
+
from scipy.linalg import block_diag
|
|
51
|
+
setattr(self, matrix_name, self.add_dofs_labels_to_matrix(
|
|
52
|
+
block_diag(*[getattr(body, matrix_name) for body in bodies])
|
|
53
|
+
))
|
|
54
|
+
|
|
55
|
+
LOG.debug(f"New multibody: {self.__str__()}.")
|
|
56
|
+
|
|
57
|
+
@lru_cache
|
|
58
|
+
def as_FloatingBody(self):
|
|
59
|
+
from capytaine.bodies.bodies import FloatingBody
|
|
60
|
+
if all(body.mass is not None for body in self.bodies):
|
|
61
|
+
total_mass = sum(body.mass for body in self.bodies)
|
|
62
|
+
else:
|
|
63
|
+
total_mass = None
|
|
64
|
+
|
|
65
|
+
if (all(body.mass is not None for body in self.bodies)
|
|
66
|
+
and all(body.center_of_mass is not None for body in self.bodies)):
|
|
67
|
+
new_cog = sum(body.mass*np.asarray(body.center_of_mass) for body in self.bodies)/total_mass
|
|
68
|
+
else:
|
|
69
|
+
new_cog = None
|
|
70
|
+
|
|
71
|
+
return FloatingBody(
|
|
72
|
+
mesh=self.mesh,
|
|
73
|
+
dofs=self.dofs,
|
|
74
|
+
lid_mesh=self.lid_mesh,
|
|
75
|
+
mass=total_mass,
|
|
76
|
+
center_of_mass=new_cog,
|
|
77
|
+
name=self.name,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def __str__(self):
|
|
81
|
+
short_bodies = ', '.join(b.__short_str__() for b in self.bodies)
|
|
82
|
+
# short_dofs = '{' + ', '.join('"{}": ...'.format(d) for d in self.own_dofs) + '}'
|
|
83
|
+
return f"Multibody({short_bodies})" #, own_dofs={short_dofs})"
|
|
84
|
+
|
|
85
|
+
def __short_str__(self):
|
|
86
|
+
return str(self)
|
|
87
|
+
|
|
88
|
+
def __repr__(self):
|
|
89
|
+
return str(self)
|
|
90
|
+
|
|
91
|
+
def _check_dofs_shape_consistency(self):
|
|
92
|
+
# TODO
|
|
93
|
+
...
|
|
94
|
+
|
|
95
|
+
@cached_property
|
|
96
|
+
def minimal_computable_wavelength(self):
|
|
97
|
+
return min(b.minimal_computable_wavelength for b in self.bodies)
|
|
98
|
+
|
|
99
|
+
def first_irregular_frequency_estimate(self, *args, **kwargs):
|
|
100
|
+
return min(b.first_irregular_frequency_estimate(*args, **kwargs) for b in self.bodies)
|
|
101
|
+
|
|
102
|
+
@cached_property
|
|
103
|
+
def mesh(self):
|
|
104
|
+
return self.bodies[0].mesh.join_meshes(*[b.mesh for b in self.bodies[1:]])
|
|
105
|
+
|
|
106
|
+
@cached_property
|
|
107
|
+
def lid_mesh(self):
|
|
108
|
+
if all(body.lid_mesh is None for body in self.bodies):
|
|
109
|
+
return None
|
|
110
|
+
else:
|
|
111
|
+
lid_meshes = [body.lid_mesh.copy() for body in self.bodies if body.lid_mesh is not None]
|
|
112
|
+
joined_lid = lid_meshes[0].join_meshes(*lid_meshes[1:], name=f"{self.name}_lid_mesh")
|
|
113
|
+
return joined_lid
|
|
114
|
+
|
|
115
|
+
@cached_property
|
|
116
|
+
def mesh_including_lid(self):
|
|
117
|
+
return self.bodies[0].mesh_including_lid.join_meshes(*[b.mesh_including_lid for b in self.bodies[1:]])
|
|
118
|
+
|
|
119
|
+
@cached_property
|
|
120
|
+
def hull_mask(self):
|
|
121
|
+
return np.concatenate([b.hull_mask for b in self.bodies])
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def nb_dofs(self):
|
|
125
|
+
return sum(b.nb_dofs for b in self.bodies) # + len(self.own_dofs)
|
|
126
|
+
|
|
127
|
+
@cached_property
|
|
128
|
+
def dofs(self):
|
|
129
|
+
for body in self.bodies:
|
|
130
|
+
body._check_dofs_shape_consistency()
|
|
131
|
+
|
|
132
|
+
componenents_dofs = {}
|
|
133
|
+
cum_nb_faces = accumulate(chain([0], (body.mesh.nb_faces for body in self.bodies)))
|
|
134
|
+
total_nb_faces = sum(body.mesh.nb_faces for body in self.bodies)
|
|
135
|
+
for body, nbf in zip(self.bodies, cum_nb_faces):
|
|
136
|
+
# nbf is the cumulative number of faces of the previous subbodies,
|
|
137
|
+
# that is the offset of the indices of the faces of the current body.
|
|
138
|
+
for name, dof in body.dofs.items():
|
|
139
|
+
if isinstance(dof, AbstractDof):
|
|
140
|
+
new_dof = DofOnSubmesh(dof, range(nbf, nbf+body.mesh.nb_faces))
|
|
141
|
+
else:
|
|
142
|
+
new_dof = np.zeros((total_nb_faces, 3))
|
|
143
|
+
new_dof[nbf:nbf+len(dof), :] = dof
|
|
144
|
+
|
|
145
|
+
if '__' not in name:
|
|
146
|
+
new_dof_name = '__'.join([body.name, name])
|
|
147
|
+
else:
|
|
148
|
+
# The body is probably a combination of bodies already.
|
|
149
|
+
# So for the associativity of the + operation,
|
|
150
|
+
# it is better to keep the same name.
|
|
151
|
+
new_dof_name = name
|
|
152
|
+
componenents_dofs[new_dof_name] = new_dof
|
|
153
|
+
|
|
154
|
+
return {**componenents_dofs} #, **self.own_dofs}
|
|
155
|
+
|
|
156
|
+
def add_dofs_labels_to_vector(self, vector):
|
|
157
|
+
"""Helper function turning a bare vector into a vector labelled by the name of the dofs of the body,
|
|
158
|
+
to be used for instance for the computation of RAO."""
|
|
159
|
+
return add_dofs_labels_to_vector(self.dofs.keys(), vector)
|
|
160
|
+
|
|
161
|
+
def add_dofs_labels_to_matrix(self, matrix):
|
|
162
|
+
"""Helper function turning a bare matrix into a matrix labelled by the name of the dofs of the body,
|
|
163
|
+
to be used for instance for the computation of RAO."""
|
|
164
|
+
return add_dofs_labels_to_matrix(self.dofs.keys(), matrix)
|
|
165
|
+
|
|
166
|
+
def immersed_part(self, *args, **kwargs):
|
|
167
|
+
return Multibody(
|
|
168
|
+
[b.immersed_part() for b in self.bodies],
|
|
169
|
+
# own_dofs=None, # TODO
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def __add__(self, body_to_add):
|
|
173
|
+
return self.join_bodies(body_to_add)
|
|
174
|
+
|
|
175
|
+
def join_bodies(*bodies, name=None):
|
|
176
|
+
from capytaine.bodies.multibodies import Multibody
|
|
177
|
+
return Multibody(bodies, name=name)
|
|
178
|
+
|
|
179
|
+
def integrate_pressure(self, pressure):
|
|
180
|
+
return self.as_FloatingBody().integrate_pressure(pressure)
|
|
181
|
+
|
|
182
|
+
@cached_property
|
|
183
|
+
def center_of_buoyancy(self):
|
|
184
|
+
return {b.name: b.center_of_buoyancy for b in self.bodies}
|
|
185
|
+
|
|
186
|
+
@cached_property
|
|
187
|
+
def center_of_mass(self):
|
|
188
|
+
return {b.name: b.center_of_mass for b in self.bodies}
|
|
189
|
+
|
|
190
|
+
@cached_property
|
|
191
|
+
def volume(self):
|
|
192
|
+
return {b.name: b.volume for b in self.bodies}
|
|
193
|
+
|
|
194
|
+
@cached_property
|
|
195
|
+
def mass(self):
|
|
196
|
+
return {b.name: b.mass for b in self.bodies}
|
|
197
|
+
|
|
198
|
+
def _combine_component_matrices(self, matrices):
|
|
199
|
+
for m, b in zip(matrices, self.bodies):
|
|
200
|
+
m.coords['radiating_dof'] = np.array([b.name + '__' + k for k in m.coords['radiating_dof'].values])
|
|
201
|
+
m.coords['influenced_dof'] = np.array([b.name + '__' + k for k in m.coords['influenced_dof'].values])
|
|
202
|
+
|
|
203
|
+
return xr.concat(
|
|
204
|
+
matrices,
|
|
205
|
+
dim="radiating_dof",
|
|
206
|
+
fill_value=0.0
|
|
207
|
+
).sel(
|
|
208
|
+
radiating_dof=list(self.dofs.keys()),
|
|
209
|
+
influenced_dof=list(self.dofs.keys())
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
def compute_hydrostatic_stiffness(self, *, rho=1000.0, g=9.81):
|
|
213
|
+
return self._combine_component_matrices([b.compute_hydrostatic_stiffness(rho=rho, g=g) for b in self.bodies])
|
|
214
|
+
|
|
215
|
+
def compute_rigid_body_inertia(self, rho=1000.0):
|
|
216
|
+
return self._combine_component_matrices([b.compute_rigid_body_inertia(rho=rho) for b in self.bodies])
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -6,7 +6,7 @@ from abc import ABC, abstractmethod
|
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
|
|
9
|
-
from capytaine.meshes.
|
|
9
|
+
from capytaine.meshes.abstract_meshes import AbstractMesh
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class GreenFunctionEvaluationError(Exception):
|
|
@@ -19,7 +19,7 @@ class AbstractGreenFunction(ABC):
|
|
|
19
19
|
floating_point_precision: str
|
|
20
20
|
|
|
21
21
|
def _get_colocation_points_and_normals(self, mesh1, mesh2, adjoint_double_layer):
|
|
22
|
-
if isinstance(mesh1,
|
|
22
|
+
if isinstance(mesh1, AbstractMesh):
|
|
23
23
|
collocation_points = mesh1.faces_centers
|
|
24
24
|
nb_collocation_points = mesh1.nb_faces
|
|
25
25
|
if not adjoint_double_layer: # Computing the D matrix
|
|
@@ -116,7 +116,7 @@ class Delhommeau(AbstractGreenFunction):
|
|
|
116
116
|
gf_singularities=_default_parameters["gf_singularities"],
|
|
117
117
|
):
|
|
118
118
|
|
|
119
|
-
self.fortran_core = import_module(f"capytaine.green_functions.
|
|
119
|
+
self.fortran_core = import_module(f"capytaine.green_functions.Delhommeau_{floating_point_precision}")
|
|
120
120
|
|
|
121
121
|
self.tabulation_grid_shape = tabulation_grid_shape
|
|
122
122
|
fortran_enum = {
|
|
@@ -322,20 +322,22 @@ class Delhommeau(AbstractGreenFunction):
|
|
|
322
322
|
else:
|
|
323
323
|
raise ValueError(f"Unrecognized name for the Prony decomposition method: {repr(method)}. Expected 'python' or 'fortran'.")
|
|
324
324
|
|
|
325
|
-
def evaluate_rankine_only(
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
325
|
+
def evaluate_rankine_only(
|
|
326
|
+
self,
|
|
327
|
+
mesh1, mesh2, *,
|
|
328
|
+
adjoint_double_layer=True, early_dot_product=True,
|
|
329
|
+
diagonal_term_in_double_layer=True,
|
|
330
|
+
):
|
|
329
331
|
r"""Construct the matrices between mesh1 (that can also be a list of points)
|
|
330
332
|
and mesh2 for a Rankine kernel.
|
|
331
333
|
|
|
332
334
|
Parameters
|
|
333
335
|
----------
|
|
334
|
-
mesh1:
|
|
336
|
+
mesh1: MeshLike or list of points
|
|
335
337
|
mesh of the receiving body (where the potential is measured)
|
|
336
338
|
if only S is wanted or early_dot_product is False, then only a list
|
|
337
339
|
of points as an array of shape (n, 3) can be passed.
|
|
338
|
-
mesh2:
|
|
340
|
+
mesh2: MeshLike
|
|
339
341
|
mesh of the source body (over which the source distribution is integrated)
|
|
340
342
|
adjoint_double_layer: bool, optional
|
|
341
343
|
compute double layer for direct method (F) or adjoint double layer
|
|
@@ -343,6 +345,11 @@ class Delhommeau(AbstractGreenFunction):
|
|
|
343
345
|
early_dot_product: boolean, optional
|
|
344
346
|
if False, return K as a (n, m, 3) array storing ∫∇G
|
|
345
347
|
if True, return K as a (n, m) array storing ∫∇G·n
|
|
348
|
+
diagonal_term_in_double_layer: boolean, optional
|
|
349
|
+
if True, add the I/2 term in the double layer operator
|
|
350
|
+
It is assumed that mesh1 == mesh2, or at least that
|
|
351
|
+
the `n := min(mesh1.nb_faces, mesh2.nb_faces)` first faces
|
|
352
|
+
of each mesh are identical.
|
|
346
353
|
|
|
347
354
|
Returns
|
|
348
355
|
-------
|
|
@@ -365,9 +372,10 @@ class Delhommeau(AbstractGreenFunction):
|
|
|
365
372
|
adjoint_double_layer,
|
|
366
373
|
S, K)
|
|
367
374
|
|
|
368
|
-
if
|
|
375
|
+
if diagonal_term_in_double_layer:
|
|
376
|
+
n = min(K.shape[0], K.shape[1])
|
|
369
377
|
self.fortran_core.matrices.add_diagonal_term(
|
|
370
|
-
mesh2.faces_centers, early_dot_product_normals, np.inf, K,
|
|
378
|
+
mesh2.faces_centers[:n, :], early_dot_product_normals, np.inf, K,
|
|
371
379
|
)
|
|
372
380
|
|
|
373
381
|
S, K = np.real(S), np.real(K)
|
|
@@ -383,11 +391,12 @@ class Delhommeau(AbstractGreenFunction):
|
|
|
383
391
|
return S, K
|
|
384
392
|
|
|
385
393
|
|
|
386
|
-
def evaluate(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
394
|
+
def evaluate(
|
|
395
|
+
self, mesh1, mesh2, *,
|
|
396
|
+
free_surface=0.0, water_depth=np.inf, wavenumber=1.0,
|
|
397
|
+
adjoint_double_layer=True, early_dot_product=True,
|
|
398
|
+
diagonal_term_in_double_layer=True,
|
|
399
|
+
):
|
|
391
400
|
r"""The main method of the class, called by the engine to assemble the influence matrices.
|
|
392
401
|
|
|
393
402
|
Parameters
|
|
@@ -408,6 +417,11 @@ class Delhommeau(AbstractGreenFunction):
|
|
|
408
417
|
early_dot_product: boolean, optional
|
|
409
418
|
if False, return K as a (n, m, 3) array storing ∫∇G
|
|
410
419
|
if True, return K as a (n, m) array storing ∫∇G·n
|
|
420
|
+
diagonal_term_in_double_layer: boolean, optional
|
|
421
|
+
if True, add the I/2 term in the double layer operator.
|
|
422
|
+
It is assumed that mesh1 == mesh2, or at least that
|
|
423
|
+
the `n := min(mesh1.nb_faces, mesh2.nb_faces)` first faces
|
|
424
|
+
of each mesh are identical.
|
|
411
425
|
|
|
412
426
|
Returns
|
|
413
427
|
-------
|
|
@@ -483,9 +497,10 @@ class Delhommeau(AbstractGreenFunction):
|
|
|
483
497
|
S, K
|
|
484
498
|
)
|
|
485
499
|
|
|
486
|
-
if
|
|
500
|
+
if diagonal_term_in_double_layer:
|
|
501
|
+
n = min(K.shape[0], K.shape[1])
|
|
487
502
|
self.fortran_core.matrices.add_diagonal_term(
|
|
488
|
-
mesh2.faces_centers, early_dot_product_normals, free_surface, K,
|
|
503
|
+
mesh2.faces_centers[:n, :], early_dot_product_normals, free_surface, K,
|
|
489
504
|
)
|
|
490
505
|
|
|
491
506
|
if np.any(np.isnan(S)) or np.any(np.isnan(K)):
|
|
@@ -13,7 +13,7 @@ class LiangWuNoblesseGF(AbstractGreenFunction):
|
|
|
13
13
|
"""
|
|
14
14
|
floating_point_precision = "float64"
|
|
15
15
|
|
|
16
|
-
fortran_core = import_module("capytaine.green_functions.
|
|
16
|
+
fortran_core = import_module("capytaine.green_functions.Delhommeau_float64")
|
|
17
17
|
tabulation_grid_shape_index = fortran_core.constants.liang_wu_noblesse
|
|
18
18
|
exportable_settings = {'green_function': "LiangWuNoblesseGF"}
|
|
19
19
|
|
|
@@ -36,11 +36,12 @@ class LiangWuNoblesseGF(AbstractGreenFunction):
|
|
|
36
36
|
def _repr_pretty_(self, p, cycle):
|
|
37
37
|
p.text(self.__repr__())
|
|
38
38
|
|
|
39
|
-
def evaluate(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
def evaluate(
|
|
40
|
+
self, mesh1, mesh2, *,
|
|
41
|
+
free_surface=0.0, water_depth=np.inf, wavenumber,
|
|
42
|
+
adjoint_double_layer=True, early_dot_product=True,
|
|
43
|
+
diagonal_term_in_double_layer=True,
|
|
44
|
+
):
|
|
44
45
|
|
|
45
46
|
if free_surface == np.inf or water_depth < np.inf:
|
|
46
47
|
raise NotImplementedError("LiangWuNoblesseGF() is only implemented for infinite depth with a free surface")
|
|
@@ -71,7 +72,7 @@ class LiangWuNoblesseGF(AbstractGreenFunction):
|
|
|
71
72
|
S, K
|
|
72
73
|
)
|
|
73
74
|
|
|
74
|
-
if
|
|
75
|
+
if diagonal_term_in_double_layer:
|
|
75
76
|
self.fortran_core.matrices.add_diagonal_term(
|
|
76
77
|
mesh2.faces_centers, early_dot_product_normals, free_surface, K,
|
|
77
78
|
)
|
|
@@ -95,7 +96,7 @@ class FinGreen3D(AbstractGreenFunction):
|
|
|
95
96
|
"""
|
|
96
97
|
floating_point_precision = "float64"
|
|
97
98
|
|
|
98
|
-
fortran_core = import_module("capytaine.green_functions.
|
|
99
|
+
fortran_core = import_module("capytaine.green_functions.Delhommeau_float64")
|
|
99
100
|
finite_depth_method_index = fortran_core.constants.fingreen3d_method
|
|
100
101
|
gf_singularities_index = fortran_core.constants.low_freq
|
|
101
102
|
|
|
@@ -129,7 +130,12 @@ class FinGreen3D(AbstractGreenFunction):
|
|
|
129
130
|
return brentq(lambda y: omega2_h_over_g + y*np.tan(y), (2*i_root+1)*np.pi/2 + 1e-10, (2*i_root+2)*np.pi/2 - 1e-10)/depth
|
|
130
131
|
return np.array([wavenumber] + [root(i_root) for i_root in range(nk-1)])
|
|
131
132
|
|
|
132
|
-
def evaluate(
|
|
133
|
+
def evaluate(
|
|
134
|
+
self, mesh1, mesh2, *,
|
|
135
|
+
free_surface=0.0, water_depth=np.inf, wavenumber,
|
|
136
|
+
adjoint_double_layer=True, early_dot_product=True,
|
|
137
|
+
diagonal_term_in_double_layer=True,
|
|
138
|
+
):
|
|
133
139
|
|
|
134
140
|
if free_surface == np.inf or water_depth == np.inf:
|
|
135
141
|
raise NotImplementedError("FinGreen3D is only implemented for finite depth with a free surface.")
|
|
@@ -163,7 +169,7 @@ class FinGreen3D(AbstractGreenFunction):
|
|
|
163
169
|
S, K
|
|
164
170
|
)
|
|
165
171
|
|
|
166
|
-
if
|
|
172
|
+
if diagonal_term_in_double_layer:
|
|
167
173
|
self.fortran_core.matrices.add_diagonal_term(
|
|
168
174
|
mesh2.faces_centers, early_dot_product_normals, free_surface, K,
|
|
169
175
|
)
|
|
@@ -197,8 +203,8 @@ class HAMS_GF(AbstractGreenFunction):
|
|
|
197
203
|
def _repr_pretty_(self, p, cycle):
|
|
198
204
|
p.text(self.__repr__())
|
|
199
205
|
|
|
200
|
-
def evaluate(self, mesh1, mesh2,
|
|
206
|
+
def evaluate(self, mesh1, mesh2, *, water_depth=np.inf, **kwargs):
|
|
201
207
|
if water_depth == np.inf:
|
|
202
|
-
return self.infinite_depth_gf.evaluate(mesh1, mesh2,
|
|
208
|
+
return self.infinite_depth_gf.evaluate(mesh1, mesh2, water_depth=water_depth, **kwargs)
|
|
203
209
|
else:
|
|
204
|
-
return self.finite_depth_gf.evaluate(mesh1, mesh2,
|
|
210
|
+
return self.finite_depth_gf.evaluate(mesh1, mesh2, water_depth=water_depth, **kwargs)
|
capytaine/io/legacy.py
CHANGED
|
@@ -9,10 +9,9 @@ import numpy as np
|
|
|
9
9
|
|
|
10
10
|
from capytaine.bem.solver import BEMSolver
|
|
11
11
|
from capytaine.io.xarray import assemble_dataset
|
|
12
|
-
from capytaine.io
|
|
12
|
+
from capytaine.meshes.io import load_mesh
|
|
13
13
|
from capytaine.bodies.bodies import FloatingBody
|
|
14
14
|
from capytaine.bem.problems_and_results import DiffractionProblem, RadiationProblem
|
|
15
|
-
from capytaine.meshes.geometry import Axis
|
|
16
15
|
|
|
17
16
|
LOG = logging.getLogger(__name__)
|
|
18
17
|
|
|
@@ -47,7 +46,7 @@ def import_cal_file(filepath):
|
|
|
47
46
|
spec.loader.exec_module(body_initialization)
|
|
48
47
|
body = body_initialization.body
|
|
49
48
|
else:
|
|
50
|
-
body = FloatingBody
|
|
49
|
+
body = FloatingBody(mesh=load_mesh(mesh_file, file_format='nemoh'))
|
|
51
50
|
|
|
52
51
|
nb_dofs = int(cal_file.readline().split()[0])
|
|
53
52
|
for i_dof in range(nb_dofs):
|
|
@@ -58,7 +57,7 @@ def import_cal_file(filepath):
|
|
|
58
57
|
elif int(dof_data[0]) == 2:
|
|
59
58
|
direction = np.array([float(x) for x in dof_data[1:4]])
|
|
60
59
|
center_of_mass = np.array([float(x) for x in dof_data[4:7]])
|
|
61
|
-
body.add_rotation_dof(
|
|
60
|
+
body.add_rotation_dof(direction=direction, rotation_center=center_of_mass)
|
|
62
61
|
|
|
63
62
|
nb_forces = int(cal_file.readline().split()[0])
|
|
64
63
|
for i_force in range(nb_forces):
|
|
@@ -123,105 +122,6 @@ def import_cal_file(filepath):
|
|
|
123
122
|
return problems
|
|
124
123
|
|
|
125
124
|
|
|
126
|
-
def export_as_Nemoh_directory(problem, directory_name, omega_range=None):
|
|
127
|
-
"""Export radiation problems as Nemoh 2.0 directory (experimental).
|
|
128
|
-
|
|
129
|
-
TODO: Diffraction problem.
|
|
130
|
-
|
|
131
|
-
Parameters
|
|
132
|
-
----------
|
|
133
|
-
problem : RadiationProblem
|
|
134
|
-
the problem that should be exported
|
|
135
|
-
directory_name : string
|
|
136
|
-
path to the directory
|
|
137
|
-
omega_range : list of float or array of float, optional
|
|
138
|
-
the exported problem will be set up with the following linear range:
|
|
139
|
-
linspace(min(omega_range), max(omega_range), len(omega_range))
|
|
140
|
-
"""
|
|
141
|
-
|
|
142
|
-
if os.path.isdir(directory_name):
|
|
143
|
-
LOG.warning(f"""Exporting problem in already existing directory: {directory_name}
|
|
144
|
-
You might be overwriting existing files!""")
|
|
145
|
-
else:
|
|
146
|
-
os.makedirs(directory_name)
|
|
147
|
-
|
|
148
|
-
# Export the mesh
|
|
149
|
-
write_MAR(
|
|
150
|
-
os.path.join(directory_name, f'{problem.body.name}.dat'),
|
|
151
|
-
problem.body.mesh.vertices,
|
|
152
|
-
problem.body.mesh.faces,
|
|
153
|
-
# xOz_symmetry=isinstance(problem.body, ReflectionSymmetry)
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
# Set range of frequencies
|
|
157
|
-
if omega_range is None:
|
|
158
|
-
omega_nb_steps = 1
|
|
159
|
-
omega_start = problem.omega
|
|
160
|
-
omega_stop = problem.omega
|
|
161
|
-
else:
|
|
162
|
-
omega_nb_steps = len(omega_range)
|
|
163
|
-
omega_start = min(omega_range)
|
|
164
|
-
omega_stop = max(omega_range)
|
|
165
|
-
|
|
166
|
-
# Write Nemoh.cal
|
|
167
|
-
with open(os.path.join(directory_name, "Nemoh.cal"), "w") as nemoh_cal:
|
|
168
|
-
nemoh_cal.write(
|
|
169
|
-
DEFAULT_NEMOH_CAL.format(
|
|
170
|
-
rho=problem.rho,
|
|
171
|
-
g=problem.g,
|
|
172
|
-
depth=problem.water_depth if problem.water_depth < np.inf else 0,
|
|
173
|
-
mesh_filename=f'{problem.body.name}.dat',
|
|
174
|
-
mesh_vertices=problem.body.mesh.nb_vertices,
|
|
175
|
-
mesh_faces=problem.body.mesh.nb_faces,
|
|
176
|
-
omega_nb_steps=omega_nb_steps,
|
|
177
|
-
omega_start=omega_start,
|
|
178
|
-
omega_stop=omega_stop,
|
|
179
|
-
)
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
# Write input.txt
|
|
183
|
-
with open(os.path.join(directory_name, "input.txt"), "w") as input_txt:
|
|
184
|
-
input_txt.write(DEFAULT_INPUT_TXT)
|
|
185
|
-
|
|
186
|
-
# Write ID.dat
|
|
187
|
-
with open(os.path.join(directory_name, "ID.dat"), "w") as id_dat:
|
|
188
|
-
id_dat.write(f"1\n.")
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
DEFAULT_NEMOH_CAL = """--- Environment ------------------------------------------------------------------------------------------------------------------
|
|
192
|
-
{rho} ! RHO ! KG/M**3 ! Fluid specific volume
|
|
193
|
-
{g} ! G ! M/S**2 ! Gravity
|
|
194
|
-
{depth} ! DEPTH ! M ! Water depth
|
|
195
|
-
0. 0. ! XEFF YEFF ! M ! Wave measurement point
|
|
196
|
-
--- Description of floating bodies -----------------------------------------------------------------------------------------------
|
|
197
|
-
1 ! Number of bodies
|
|
198
|
-
--- Body 1 -----------------------------------------------------------------------------------------------------------------------
|
|
199
|
-
{mesh_filename}
|
|
200
|
-
{mesh_vertices} {mesh_faces}
|
|
201
|
-
1 ! Number of degrees of freedom
|
|
202
|
-
1 0. 0. 1. 0. 0. 0. ! Heave
|
|
203
|
-
1 ! Number of resulting generalised forces
|
|
204
|
-
1 0. 0. 1. 0. 0. 0. ! Heave
|
|
205
|
-
0 ! Number of lines of additional information
|
|
206
|
-
--- Load cases to be solved -------------------------------------------------------------------------------------------------------
|
|
207
|
-
{omega_nb_steps} {omega_start} {omega_stop} ! Frequencies range
|
|
208
|
-
0 0. 0. ! Number of wave directions, Min and Max (degrees)
|
|
209
|
-
--- Post processing ---------------------------------------------------------------------------------------------------------------
|
|
210
|
-
0 0.1 10. ! IRF ! IRF calculation (0 for no calculation), time step and duration
|
|
211
|
-
0 ! Show pressure
|
|
212
|
-
0 0. 180. ! Kochin function ! Number of directions of calculation (0 for no calculations), Min and Max (degrees)
|
|
213
|
-
0 0 100. 100. ! Free surface elevation ! Number of points in x direction (0 for no calcutions) and y direction and dimensions of domain in x and y direction
|
|
214
|
-
"""
|
|
215
|
-
|
|
216
|
-
DEFAULT_INPUT_TXT = """--- Calculation parameters ------------------------------------------------------------------------------------
|
|
217
|
-
1 ! Indiq_solver ! - ! Solver (0) Direct Gauss (1) GMRES (2) GMRES with FMM acceleration (2 not implemented yet)
|
|
218
|
-
20 ! IRES ! - ! Restart parameter for GMRES
|
|
219
|
-
5.E-07 ! TOL_GMRES ! - ! Stopping criterion for GMRES
|
|
220
|
-
100 ! MAXIT ! - ! Maximum iterations for GMRES
|
|
221
|
-
1 ! Sav_potential ! - ! Save potential for visualization
|
|
222
|
-
"""
|
|
223
|
-
|
|
224
|
-
|
|
225
125
|
def write_dataset_as_tecplot_files(results_directory, data):
|
|
226
126
|
"""Write some of the data from a xarray dataset into legacy tecplot file outputs."""
|
|
227
127
|
|
capytaine/io/xarray.py
CHANGED
|
@@ -11,6 +11,7 @@ from datetime import datetime
|
|
|
11
11
|
from itertools import product
|
|
12
12
|
from collections import Counter
|
|
13
13
|
from typing import Sequence, List, Union
|
|
14
|
+
from pathlib import Path
|
|
14
15
|
|
|
15
16
|
import numpy as np
|
|
16
17
|
import pandas as pd
|
|
@@ -18,6 +19,7 @@ import xarray as xr
|
|
|
18
19
|
|
|
19
20
|
from capytaine import __version__
|
|
20
21
|
from capytaine.bodies.bodies import FloatingBody
|
|
22
|
+
from capytaine.bodies.multibodies import Multibody
|
|
21
23
|
from capytaine.bem.problems_and_results import (
|
|
22
24
|
LinearPotentialFlowProblem, DiffractionProblem, RadiationProblem,
|
|
23
25
|
LinearPotentialFlowResult, _default_parameters)
|
|
@@ -43,7 +45,7 @@ def _unsqueeze_dimensions(data_array, dimensions=None):
|
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
def problems_from_dataset(dataset: xr.Dataset,
|
|
46
|
-
bodies: Union[FloatingBody, Sequence[FloatingBody]],
|
|
48
|
+
bodies: Union[FloatingBody, Sequence[FloatingBody], Multibody, Sequence[Multibody]],
|
|
47
49
|
) -> List[LinearPotentialFlowProblem]:
|
|
48
50
|
"""Generate a list of problems from a test matrix.
|
|
49
51
|
|
|
@@ -64,7 +66,7 @@ def problems_from_dataset(dataset: xr.Dataset,
|
|
|
64
66
|
ValueError
|
|
65
67
|
if required fields are missing in the dataset
|
|
66
68
|
"""
|
|
67
|
-
if isinstance(bodies, FloatingBody):
|
|
69
|
+
if isinstance(bodies, (FloatingBody, Multibody)):
|
|
68
70
|
bodies = [bodies]
|
|
69
71
|
|
|
70
72
|
# Should be done before looking for `frequency_keys`, otherwise
|
|
@@ -279,22 +281,24 @@ def kochin_data_array(results: Sequence[LinearPotentialFlowResult],
|
|
|
279
281
|
compute_kochin(result, theta_range, **kwargs))
|
|
280
282
|
])
|
|
281
283
|
|
|
284
|
+
main_freq_type = Counter((res.provided_freq_type for res in results)).most_common(1)[0][0]
|
|
285
|
+
|
|
282
286
|
kochin_data = xr.Dataset()
|
|
283
287
|
|
|
284
288
|
if "RadiationResult" in set(records['kind']):
|
|
285
289
|
radiation = _dataset_from_dataframe(
|
|
286
290
|
records[records['kind'] == "RadiationResult"],
|
|
287
291
|
variables=['kochin'],
|
|
288
|
-
dimensions=[
|
|
292
|
+
dimensions=[main_freq_type, 'radiating_dof', 'theta'],
|
|
289
293
|
optional_dims=['g', 'rho', 'body_name', 'water_depth', 'forward_speed', 'wave_direction']
|
|
290
294
|
)
|
|
291
|
-
kochin_data['
|
|
295
|
+
kochin_data['kochin_radiation'] = radiation['kochin']
|
|
292
296
|
|
|
293
297
|
if "DiffractionResult" in set(records['kind']):
|
|
294
298
|
diffraction = _dataset_from_dataframe(
|
|
295
299
|
records[records['kind'] == "DiffractionResult"],
|
|
296
300
|
['kochin'],
|
|
297
|
-
dimensions=[
|
|
301
|
+
dimensions=[main_freq_type, 'wave_direction', 'theta'],
|
|
298
302
|
optional_dims=['g', 'rho', 'body_name', 'water_depth', 'forward_speed']
|
|
299
303
|
)
|
|
300
304
|
kochin_data['kochin_diffraction'] = diffraction['kochin']
|
|
@@ -532,7 +536,7 @@ def assemble_dataset(results,
|
|
|
532
536
|
|
|
533
537
|
def assemble_matrices(results):
|
|
534
538
|
"""Simplified version of assemble_dataset, returning only bare matrices.
|
|
535
|
-
Meant mainly for teaching without introducing Xarray to
|
|
539
|
+
Meant mainly for teaching without introducing Xarray to beginners.
|
|
536
540
|
|
|
537
541
|
Parameters
|
|
538
542
|
----------
|
|
@@ -662,6 +666,7 @@ def export_dataset(filename, dataset, format=None, **kwargs):
|
|
|
662
666
|
(format is not None and format.lower() == "nemoh")
|
|
663
667
|
):
|
|
664
668
|
from capytaine.io.legacy import write_dataset_as_tecplot_files
|
|
669
|
+
Path(filename).mkdir(exist_ok=True)
|
|
665
670
|
write_dataset_as_tecplot_files(filename, dataset, **kwargs)
|
|
666
671
|
else:
|
|
667
672
|
raise ValueError("`export_dataset` could not infer export format based on filename or `format` argument.\n"
|
capytaine/meshes/__init__.py
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
from capytaine.meshes.meshes import Mesh
|
|
5
|
-
from capytaine.meshes.collections import CollectionOfMeshes
|
|
6
|
-
from capytaine.meshes.symmetric import ReflectionSymmetricMesh, TranslationalSymmetricMesh, AxialSymmetricMesh
|
|
1
|
+
from .meshes import Mesh
|
|
2
|
+
from .symmetric_meshes import ReflectionSymmetricMesh, RotationSymmetricMesh
|