capytaine 2.2.1__cp310-cp310-win_amd64.whl → 2.3.1__cp310-cp310-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 +1 -1
- capytaine/__init__.py +5 -4
- capytaine/bem/airy_waves.py +7 -2
- capytaine/bem/problems_and_results.py +91 -39
- capytaine/bem/solver.py +128 -40
- capytaine/bodies/bodies.py +46 -18
- capytaine/bodies/predefined/rectangles.py +2 -0
- capytaine/green_functions/FinGreen3D/.gitignore +1 -0
- capytaine/green_functions/FinGreen3D/FinGreen3D.f90 +3589 -0
- capytaine/green_functions/FinGreen3D/LICENSE +165 -0
- capytaine/green_functions/FinGreen3D/Makefile +16 -0
- capytaine/green_functions/FinGreen3D/README.md +24 -0
- capytaine/green_functions/FinGreen3D/test_program.f90 +39 -0
- capytaine/green_functions/LiangWuNoblesse/.gitignore +1 -0
- capytaine/green_functions/LiangWuNoblesse/LICENSE +504 -0
- capytaine/green_functions/LiangWuNoblesse/LiangWuNoblesseWaveTerm.f90 +751 -0
- capytaine/green_functions/LiangWuNoblesse/Makefile +16 -0
- capytaine/green_functions/LiangWuNoblesse/README.md +2 -0
- capytaine/green_functions/LiangWuNoblesse/test_program.f90 +28 -0
- capytaine/green_functions/abstract_green_function.py +55 -3
- capytaine/green_functions/delhommeau.py +205 -130
- capytaine/green_functions/hams.py +204 -0
- capytaine/green_functions/libs/Delhommeau_float32.cp310-win_amd64.dll.a +0 -0
- capytaine/green_functions/libs/Delhommeau_float32.cp310-win_amd64.pyd +0 -0
- capytaine/green_functions/libs/Delhommeau_float64.cp310-win_amd64.dll.a +0 -0
- capytaine/green_functions/libs/Delhommeau_float64.cp310-win_amd64.pyd +0 -0
- capytaine/io/bemio.py +14 -2
- capytaine/io/mesh_loaders.py +1 -1
- capytaine/io/wamit.py +479 -0
- capytaine/io/xarray.py +261 -117
- capytaine/matrices/linear_solvers.py +1 -1
- capytaine/meshes/clipper.py +1 -0
- capytaine/meshes/collections.py +19 -1
- capytaine/meshes/mesh_like_protocol.py +37 -0
- capytaine/meshes/meshes.py +28 -8
- capytaine/meshes/symmetric.py +89 -10
- capytaine/post_pro/kochin.py +4 -4
- capytaine/tools/lists_of_points.py +3 -3
- capytaine/tools/prony_decomposition.py +60 -4
- capytaine/tools/symbolic_multiplication.py +30 -4
- capytaine/tools/timer.py +66 -0
- capytaine-2.3.1.dist-info/DELVEWHEEL +2 -0
- {capytaine-2.2.1.dist-info → capytaine-2.3.1.dist-info}/METADATA +6 -10
- {capytaine-2.2.1.dist-info → capytaine-2.3.1.dist-info}/RECORD +47 -31
- capytaine-2.2.1.dist-info/DELVEWHEEL +0 -2
- {capytaine-2.2.1.dist-info → capytaine-2.3.1.dist-info}/LICENSE +0 -0
- {capytaine-2.2.1.dist-info → capytaine-2.3.1.dist-info}/WHEEL +0 -0
- {capytaine-2.2.1.dist-info → capytaine-2.3.1.dist-info}/entry_points.txt +0 -0
capytaine/__about__.py
CHANGED
|
@@ -5,7 +5,7 @@ __all__ = ["__title__", "__description__", "__version__", "__author__", "__uri__
|
|
|
5
5
|
__title__ = "capytaine"
|
|
6
6
|
__description__ = """Python BEM solver for linear potential flow, based on Nemoh"""
|
|
7
7
|
|
|
8
|
-
__version__ = "2.
|
|
8
|
+
__version__ = "2.3.1"
|
|
9
9
|
|
|
10
10
|
__author__ = "Matthieu Ancellin"
|
|
11
11
|
__uri__ = "https://github.com/capytaine/capytaine"
|
capytaine/__init__.py
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
# start delvewheel patch
|
|
6
|
-
def
|
|
6
|
+
def _delvewheel_patch_1_11_2():
|
|
7
7
|
import os
|
|
8
8
|
if os.path.isdir(libs_dir := os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'capytaine.libs'))):
|
|
9
9
|
os.add_dll_directory(libs_dir)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
del
|
|
12
|
+
_delvewheel_patch_1_11_2()
|
|
13
|
+
del _delvewheel_patch_1_11_2
|
|
14
14
|
# end delvewheel patch
|
|
15
15
|
|
|
16
16
|
from .__about__ import (
|
|
@@ -36,11 +36,12 @@ from capytaine.bem.problems_and_results import RadiationProblem, DiffractionProb
|
|
|
36
36
|
from capytaine.bem.solver import BEMSolver
|
|
37
37
|
from capytaine.bem.engines import BasicMatrixEngine, HierarchicalToeplitzMatrixEngine, HierarchicalPrecondMatrixEngine
|
|
38
38
|
from capytaine.green_functions.delhommeau import Delhommeau, XieDelhommeau
|
|
39
|
+
from capytaine.green_functions.hams import LiangWuNoblesseGF, FinGreen3D, HAMS_GF
|
|
39
40
|
|
|
40
41
|
from capytaine.post_pro.free_surfaces import FreeSurface
|
|
41
42
|
|
|
42
43
|
from capytaine.io.mesh_loaders import load_mesh
|
|
43
|
-
from capytaine.io.xarray import assemble_dataset
|
|
44
|
+
from capytaine.io.xarray import assemble_dataframe, assemble_dataset, assemble_matrices, export_dataset
|
|
44
45
|
|
|
45
46
|
from capytaine.ui.rich import set_logging
|
|
46
47
|
|
capytaine/bem/airy_waves.py
CHANGED
|
@@ -22,6 +22,9 @@ def airy_waves_potential(points, pb):
|
|
|
22
22
|
"""
|
|
23
23
|
points, output_shape = _normalize_points(points)
|
|
24
24
|
|
|
25
|
+
if float(pb.wavenumber) == 0.0 or float(pb.wavenumber) == np.inf:
|
|
26
|
+
return np.nan * np.ones(output_shape)
|
|
27
|
+
|
|
25
28
|
x, y, z = points.T
|
|
26
29
|
k = pb.wavenumber
|
|
27
30
|
h = pb.water_depth
|
|
@@ -54,9 +57,11 @@ def airy_waves_velocity(points, pb):
|
|
|
54
57
|
array of shape (3) or (N x 3)
|
|
55
58
|
the velocity vectors
|
|
56
59
|
"""
|
|
57
|
-
|
|
58
60
|
points, output_shape = _normalize_points(points)
|
|
59
61
|
|
|
62
|
+
if float(pb.wavenumber) == 0.0 or float(pb.wavenumber) == np.inf:
|
|
63
|
+
return np.nan * np.ones((*output_shape, 3))
|
|
64
|
+
|
|
60
65
|
x, y, z = points.T
|
|
61
66
|
k = pb.wavenumber
|
|
62
67
|
h = pb.water_depth
|
|
@@ -79,7 +84,7 @@ def airy_waves_velocity(points, pb):
|
|
|
79
84
|
|
|
80
85
|
|
|
81
86
|
def airy_waves_pressure(points, pb):
|
|
82
|
-
return 1j * pb.omega * pb.rho * airy_waves_potential(points, pb)
|
|
87
|
+
return 1j * float(pb.omega) * pb.rho * airy_waves_potential(points, pb)
|
|
83
88
|
|
|
84
89
|
|
|
85
90
|
def froude_krylov_force(pb):
|
|
@@ -24,8 +24,8 @@ _default_parameters = {'rho': 1000.0, 'g': 9.81, 'omega': 1.0,
|
|
|
24
24
|
class LinearPotentialFlowProblem:
|
|
25
25
|
"""General class of a potential flow problem.
|
|
26
26
|
|
|
27
|
-
At most one of the following
|
|
28
|
-
|
|
27
|
+
At most one of the following parameters must be provided:
|
|
28
|
+
omega, freq, period, wavenumber or wavelength.
|
|
29
29
|
|
|
30
30
|
Parameters
|
|
31
31
|
----------
|
|
@@ -39,6 +39,8 @@ class LinearPotentialFlowProblem:
|
|
|
39
39
|
The position of the sea bottom (deprecated: please prefer setting water_depth)
|
|
40
40
|
omega: float, optional
|
|
41
41
|
The angular frequency of the waves in rad/s
|
|
42
|
+
freq: float, optional
|
|
43
|
+
The frequency of the waves in Hz
|
|
42
44
|
period: float, optional
|
|
43
45
|
The period of the waves in s
|
|
44
46
|
wavenumber: float, optional
|
|
@@ -59,7 +61,7 @@ class LinearPotentialFlowProblem:
|
|
|
59
61
|
body=None,
|
|
60
62
|
free_surface=_default_parameters['free_surface'],
|
|
61
63
|
water_depth=None, sea_bottom=None,
|
|
62
|
-
omega=None, period=None, wavenumber=None, wavelength=None,
|
|
64
|
+
omega=None, freq=None, period=None, wavenumber=None, wavelength=None,
|
|
63
65
|
forward_speed=_default_parameters['forward_speed'],
|
|
64
66
|
rho=_default_parameters['rho'],
|
|
65
67
|
g=_default_parameters['g'],
|
|
@@ -76,14 +78,14 @@ class LinearPotentialFlowProblem:
|
|
|
76
78
|
self.boundary_condition = boundary_condition
|
|
77
79
|
|
|
78
80
|
self.water_depth = _get_water_depth(free_surface, water_depth, sea_bottom, default_water_depth=_default_parameters["water_depth"])
|
|
79
|
-
self.omega, self.period, self.wavenumber, self.wavelength, self.provided_freq_type = \
|
|
80
|
-
self._get_frequencies(omega=omega, period=period, wavenumber=wavenumber, wavelength=wavelength)
|
|
81
|
+
self.omega, self.freq, self.period, self.wavenumber, self.wavelength, self.provided_freq_type = \
|
|
82
|
+
self._get_frequencies(omega=omega, freq=freq, period=period, wavenumber=wavenumber, wavelength=wavelength)
|
|
81
83
|
|
|
82
84
|
self._check_data()
|
|
83
85
|
|
|
84
86
|
if forward_speed != 0.0:
|
|
85
87
|
dopplered_omega = self.omega - self.wavenumber*self.forward_speed*np.cos(self.wave_direction)
|
|
86
|
-
self.encounter_omega, self.encounter_period, self.encounter_wavenumber, self.encounter_wavelength, _ = \
|
|
88
|
+
self.encounter_omega, self.encounter_freq, self.encounter_period, self.encounter_wavenumber, self.encounter_wavelength, _ = \
|
|
87
89
|
self._get_frequencies(omega=abs(dopplered_omega))
|
|
88
90
|
|
|
89
91
|
if dopplered_omega >= 0.0:
|
|
@@ -92,18 +94,19 @@ class LinearPotentialFlowProblem:
|
|
|
92
94
|
self.encounter_wave_direction = self.wave_direction + np.pi
|
|
93
95
|
else:
|
|
94
96
|
self.encounter_omega = self.omega
|
|
97
|
+
self.encounter_freq = self.freq
|
|
95
98
|
self.encounter_period = self.period
|
|
96
99
|
self.encounter_wavenumber = self.wavenumber
|
|
97
100
|
self.encounter_wavelength = self.wavelength
|
|
98
101
|
self.encounter_wave_direction = self.wave_direction
|
|
99
102
|
|
|
100
103
|
|
|
101
|
-
def _get_frequencies(self, *, omega=None, period=None, wavenumber=None, wavelength=None):
|
|
102
|
-
frequency_data = dict(omega=omega, period=period, wavenumber=wavenumber, wavelength=wavelength)
|
|
103
|
-
nb_provided_frequency_data =
|
|
104
|
+
def _get_frequencies(self, *, omega=None, freq=None, period=None, wavenumber=None, wavelength=None):
|
|
105
|
+
frequency_data = dict(omega=omega, freq=freq, period=period, wavenumber=wavenumber, wavelength=wavelength)
|
|
106
|
+
nb_provided_frequency_data = len(frequency_data) - list(frequency_data.values()).count(None)
|
|
104
107
|
|
|
105
108
|
if nb_provided_frequency_data > 1:
|
|
106
|
-
raise ValueError("Settings a problem requires at most one of the following: omega (angular frequency) OR period OR wavenumber OR wavelength.\n"
|
|
109
|
+
raise ValueError("Settings a problem requires at most one of the following: omega (angular frequency) OR freq (in Hz) OR period OR wavenumber OR wavelength.\n"
|
|
107
110
|
"Received {} of them: {}".format(nb_provided_frequency_data, {k: v for k, v in frequency_data.items() if v is not None}))
|
|
108
111
|
|
|
109
112
|
if nb_provided_frequency_data == 0:
|
|
@@ -112,27 +115,35 @@ class LinearPotentialFlowProblem:
|
|
|
112
115
|
else:
|
|
113
116
|
provided_freq_type = [k for (k, v) in frequency_data.items() if v is not None][0]
|
|
114
117
|
|
|
115
|
-
if ((float(frequency_data[provided_freq_type]) == 0.0 and provided_freq_type in {'omega', 'wavenumber'})
|
|
118
|
+
if ((float(frequency_data[provided_freq_type]) == 0.0 and provided_freq_type in {'omega', 'freq', 'wavenumber'})
|
|
116
119
|
or (float(frequency_data[provided_freq_type]) == np.inf and provided_freq_type in {'period', 'wavelength'})):
|
|
117
120
|
omega = SymbolicMultiplication("0")
|
|
121
|
+
freq = SymbolicMultiplication("0")
|
|
118
122
|
wavenumber = SymbolicMultiplication("0")
|
|
119
123
|
period = SymbolicMultiplication("∞")
|
|
120
124
|
wavelength = SymbolicMultiplication("∞")
|
|
121
125
|
elif ((float(frequency_data[provided_freq_type]) == 0.0 and provided_freq_type in {'period', 'wavelength'})
|
|
122
|
-
or (float(frequency_data[provided_freq_type]) == np.inf and provided_freq_type in {'omega', 'wavenumber'})):
|
|
126
|
+
or (float(frequency_data[provided_freq_type]) == np.inf and provided_freq_type in {'omega', 'freq', 'wavenumber'})):
|
|
123
127
|
omega = SymbolicMultiplication("∞")
|
|
128
|
+
freq = SymbolicMultiplication("∞")
|
|
124
129
|
wavenumber = SymbolicMultiplication("∞")
|
|
125
130
|
period = SymbolicMultiplication("0")
|
|
126
131
|
wavelength = SymbolicMultiplication("0")
|
|
127
132
|
else:
|
|
128
133
|
|
|
129
|
-
if provided_freq_type in {'omega', 'period'}:
|
|
134
|
+
if provided_freq_type in {'omega', 'freq', 'period'}:
|
|
130
135
|
if provided_freq_type == 'omega':
|
|
131
136
|
omega = frequency_data['omega']
|
|
132
137
|
period = 2*np.pi/omega
|
|
138
|
+
freq = omega/2/np.pi
|
|
139
|
+
elif provided_freq_type == 'freq':
|
|
140
|
+
freq = frequency_data['freq']
|
|
141
|
+
omega = 2*np.pi*freq
|
|
142
|
+
period = 1/freq
|
|
133
143
|
else: # provided_freq_type is 'period'
|
|
134
144
|
period = frequency_data['period']
|
|
135
145
|
omega = 2*np.pi/period
|
|
146
|
+
freq = 1/period
|
|
136
147
|
|
|
137
148
|
if self.water_depth == np.inf:
|
|
138
149
|
wavenumber = omega**2/self.g
|
|
@@ -150,8 +161,9 @@ class LinearPotentialFlowProblem:
|
|
|
150
161
|
|
|
151
162
|
omega = np.sqrt(self.g*wavenumber*np.tanh(wavenumber*self.water_depth))
|
|
152
163
|
period = 2*np.pi/omega
|
|
164
|
+
freq = 1/period
|
|
153
165
|
|
|
154
|
-
return omega, period, wavenumber, wavelength, provided_freq_type
|
|
166
|
+
return omega, freq, period, wavenumber, wavelength, provided_freq_type
|
|
155
167
|
|
|
156
168
|
def _check_data(self):
|
|
157
169
|
"""Sanity checks on the data."""
|
|
@@ -168,7 +180,6 @@ class LinearPotentialFlowProblem:
|
|
|
168
180
|
"If you were actually giving an angle in radians, use the modulo operator to give a value between -2π and 2π to disable this warning.")
|
|
169
181
|
|
|
170
182
|
if self.free_surface == np.inf and self.water_depth != np.inf:
|
|
171
|
-
|
|
172
183
|
raise NotImplementedError(
|
|
173
184
|
"Problems with a sea bottom but no free surface have not been implemented."
|
|
174
185
|
)
|
|
@@ -177,11 +188,6 @@ class LinearPotentialFlowProblem:
|
|
|
177
188
|
raise ValueError("`water_depth` should be strictly positive (provided water depth: {self.water_depth}).")
|
|
178
189
|
|
|
179
190
|
if float(self.omega) in {0, np.inf}:
|
|
180
|
-
if self.water_depth != np.inf:
|
|
181
|
-
LOG.warning(
|
|
182
|
-
f"Default Green function allows for {self.provided_freq_type}={float(self.__getattribute__(self.provided_freq_type))} only for infinite depth (provided water depth: {self.water_depth})."
|
|
183
|
-
)
|
|
184
|
-
|
|
185
191
|
if self.forward_speed != 0.0:
|
|
186
192
|
raise NotImplementedError(
|
|
187
193
|
f"omega={float(self.omega)} is only implemented without forward speed (provided forward speed: {self.forward_speed})."
|
|
@@ -193,6 +199,8 @@ class LinearPotentialFlowProblem:
|
|
|
193
199
|
or len(self.body.mesh.faces) == 0):
|
|
194
200
|
raise ValueError(f"The mesh of the body {self.body.__short_str__()} is empty.")
|
|
195
201
|
|
|
202
|
+
self.body._check_dofs_shape_consistency()
|
|
203
|
+
|
|
196
204
|
panels_above_fs = self.body.mesh.faces_centers[:, 2] >= self.free_surface + 1e-8
|
|
197
205
|
panels_below_sb = self.body.mesh.faces_centers[:, 2] <= -self.water_depth
|
|
198
206
|
if (any(panels_above_fs) or any(panels_below_sb)):
|
|
@@ -231,8 +239,11 @@ class LinearPotentialFlowProblem:
|
|
|
231
239
|
def _asdict(self):
|
|
232
240
|
return {"body_name": self.body_name,
|
|
233
241
|
"water_depth": self.water_depth,
|
|
242
|
+
"free_surface": self.free_surface,
|
|
234
243
|
"omega": float(self.omega),
|
|
244
|
+
"freq": float(self.freq),
|
|
235
245
|
"encounter_omega": float(self.encounter_omega),
|
|
246
|
+
"encounter_freq": float(self.encounter_freq),
|
|
236
247
|
"period": float(self.period),
|
|
237
248
|
"wavelength": float(self.wavelength),
|
|
238
249
|
"wavenumber": float(self.wavenumber),
|
|
@@ -322,8 +333,11 @@ class LinearPotentialFlowProblem:
|
|
|
322
333
|
# TODO: let the user choose the influenced dofs
|
|
323
334
|
return self.body.dofs if self.body is not None else set()
|
|
324
335
|
|
|
325
|
-
def make_results_container(self):
|
|
326
|
-
return LinearPotentialFlowResult(self)
|
|
336
|
+
def make_results_container(self, *args, **kwargs):
|
|
337
|
+
return LinearPotentialFlowResult(self, *args, **kwargs)
|
|
338
|
+
|
|
339
|
+
def make_failed_results_container(self, *args, **kwargs):
|
|
340
|
+
return FailedLinearPotentialFlowResult(self, *args, **kwargs)
|
|
327
341
|
|
|
328
342
|
|
|
329
343
|
class DiffractionProblem(LinearPotentialFlowProblem):
|
|
@@ -334,31 +348,30 @@ class DiffractionProblem(LinearPotentialFlowProblem):
|
|
|
334
348
|
body=None,
|
|
335
349
|
free_surface=_default_parameters['free_surface'],
|
|
336
350
|
water_depth=None, sea_bottom=None,
|
|
337
|
-
omega=None, period=None, wavenumber=None, wavelength=None,
|
|
351
|
+
omega=None, freq=None, period=None, wavenumber=None, wavelength=None,
|
|
338
352
|
forward_speed=_default_parameters['forward_speed'],
|
|
339
353
|
rho=_default_parameters['rho'],
|
|
340
354
|
g=_default_parameters['g'],
|
|
341
355
|
wave_direction=_default_parameters['wave_direction']):
|
|
342
356
|
|
|
343
357
|
super().__init__(body=body, free_surface=free_surface, water_depth=water_depth, sea_bottom=sea_bottom,
|
|
344
|
-
omega=omega, period=period, wavenumber=wavenumber, wavelength=wavelength, wave_direction=wave_direction,
|
|
358
|
+
omega=omega, freq=freq, period=period, wavenumber=wavenumber, wavelength=wavelength, wave_direction=wave_direction,
|
|
345
359
|
forward_speed=forward_speed, rho=rho, g=g)
|
|
346
360
|
|
|
347
|
-
if float(self.omega) in {0.0, np.inf}:
|
|
348
|
-
raise NotImplementedError("DiffractionProblem does not support zero or infinite frequency.")
|
|
349
|
-
|
|
350
361
|
if self.body is not None:
|
|
351
362
|
|
|
352
|
-
self.boundary_condition =
|
|
363
|
+
self.boundary_condition = np.zeros(
|
|
364
|
+
shape=(self.body.mesh_including_lid.nb_faces,),
|
|
365
|
+
dtype=np.complex128
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
self.boundary_condition[self.body.hull_mask] = -(
|
|
353
369
|
airy_waves_velocity(self.body.mesh.faces_centers, self)
|
|
354
370
|
* self.body.mesh.faces_normals
|
|
355
371
|
).sum(axis=1)
|
|
356
372
|
# Note that even with forward speed, this is computed based on the
|
|
357
373
|
# frequency and not the encounter frequency.
|
|
358
374
|
|
|
359
|
-
if self.body.lid_mesh is not None:
|
|
360
|
-
self.boundary_condition = np.concatenate([self.boundary_condition, np.zeros(self.body.lid_mesh.nb_faces)])
|
|
361
|
-
|
|
362
375
|
if len(self.body.dofs) == 0:
|
|
363
376
|
LOG.warning(f"The body {self.body.name} used in diffraction problem has no dofs!")
|
|
364
377
|
|
|
@@ -371,6 +384,9 @@ class DiffractionProblem(LinearPotentialFlowProblem):
|
|
|
371
384
|
def make_results_container(self, *args, **kwargs):
|
|
372
385
|
return DiffractionResult(self, *args, **kwargs)
|
|
373
386
|
|
|
387
|
+
def make_failed_results_container(self, *args, **kwargs):
|
|
388
|
+
return FailedDiffractionResult(self, *args, **kwargs)
|
|
389
|
+
|
|
374
390
|
|
|
375
391
|
class RadiationProblem(LinearPotentialFlowProblem):
|
|
376
392
|
"""Particular LinearPotentialFlowProblem whose boundary conditions have
|
|
@@ -379,7 +395,7 @@ class RadiationProblem(LinearPotentialFlowProblem):
|
|
|
379
395
|
def __init__(self, *, body=None,
|
|
380
396
|
free_surface=_default_parameters['free_surface'],
|
|
381
397
|
water_depth=None, sea_bottom=None,
|
|
382
|
-
omega=None, period=None, wavenumber=None, wavelength=None,
|
|
398
|
+
omega=None, freq=None, period=None, wavenumber=None, wavelength=None,
|
|
383
399
|
forward_speed=_default_parameters['forward_speed'],
|
|
384
400
|
wave_direction=_default_parameters['wave_direction'],
|
|
385
401
|
rho=_default_parameters['rho'],
|
|
@@ -389,7 +405,7 @@ class RadiationProblem(LinearPotentialFlowProblem):
|
|
|
389
405
|
self.radiating_dof = radiating_dof
|
|
390
406
|
|
|
391
407
|
super().__init__(body=body, free_surface=free_surface, water_depth=water_depth, sea_bottom=sea_bottom,
|
|
392
|
-
omega=omega, period=period, wavenumber=wavenumber, wavelength=wavelength,
|
|
408
|
+
omega=omega, freq=freq, period=period, wavenumber=wavenumber, wavelength=wavelength,
|
|
393
409
|
wave_direction=wave_direction, forward_speed=forward_speed, rho=rho, g=g)
|
|
394
410
|
|
|
395
411
|
if self.body is not None:
|
|
@@ -407,7 +423,16 @@ class RadiationProblem(LinearPotentialFlowProblem):
|
|
|
407
423
|
|
|
408
424
|
dof = self.body.dofs[self.radiating_dof]
|
|
409
425
|
|
|
410
|
-
self.boundary_condition =
|
|
426
|
+
self.boundary_condition = self.encounter_omega * np.zeros(
|
|
427
|
+
shape=(self.body.mesh_including_lid.nb_faces,),
|
|
428
|
+
dtype=np.complex128
|
|
429
|
+
)
|
|
430
|
+
# The multiplication by encounter_omega is just a programming trick to ensure that boundary_condition
|
|
431
|
+
# is implemented with the correct type (for zero and infinite frequencies), it does not affect the value.
|
|
432
|
+
# Below the value is update on the hull. It remains zero on the lid.
|
|
433
|
+
|
|
434
|
+
displacement_on_face = np.sum(dof * self.body.mesh.faces_normals, axis=1) # This is a dot product on each face
|
|
435
|
+
self.boundary_condition[self.body.hull_mask] = -1j * self.encounter_omega * displacement_on_face
|
|
411
436
|
|
|
412
437
|
if self.forward_speed != 0.0:
|
|
413
438
|
if self.radiating_dof.lower() == "pitch":
|
|
@@ -422,10 +447,9 @@ class RadiationProblem(LinearPotentialFlowProblem):
|
|
|
422
447
|
"Only radiating dofs with name in {'Surge', 'Sway', 'Heave', 'Roll', 'Pitch', 'Yaw'} are supported.\n"
|
|
423
448
|
f"Got instead `radiating_dof={self.radiating_dof}`"
|
|
424
449
|
)
|
|
425
|
-
self.boundary_condition += self.forward_speed * ddofdx_dot_n
|
|
426
450
|
|
|
427
|
-
|
|
428
|
-
|
|
451
|
+
self.boundary_condition[self.body.hull_mask] += self.forward_speed * ddofdx_dot_n
|
|
452
|
+
|
|
429
453
|
|
|
430
454
|
|
|
431
455
|
def _astuple(self):
|
|
@@ -448,6 +472,9 @@ class RadiationProblem(LinearPotentialFlowProblem):
|
|
|
448
472
|
def make_results_container(self, *args, **kwargs):
|
|
449
473
|
return RadiationResult(self, *args, **kwargs)
|
|
450
474
|
|
|
475
|
+
def make_failed_results_container(self, *args, **kwargs):
|
|
476
|
+
return FailedRadiationResult(self, *args, **kwargs)
|
|
477
|
+
|
|
451
478
|
|
|
452
479
|
class LinearPotentialFlowResult:
|
|
453
480
|
|
|
@@ -465,12 +492,14 @@ class LinearPotentialFlowResult:
|
|
|
465
492
|
self.body = self.problem.body
|
|
466
493
|
self.free_surface = self.problem.free_surface
|
|
467
494
|
self.omega = self.problem.omega
|
|
495
|
+
self.freq = self.problem.freq
|
|
468
496
|
self.period = self.problem.period
|
|
469
497
|
self.wavenumber = self.problem.wavenumber
|
|
470
498
|
self.wavelength = self.problem.wavelength
|
|
471
499
|
self.forward_speed = self.problem.forward_speed
|
|
472
500
|
self.wave_direction = self.problem.wave_direction
|
|
473
501
|
self.encounter_omega = self.problem.encounter_omega
|
|
502
|
+
self.encounter_freq = self.problem.encounter_freq
|
|
474
503
|
self.encounter_period = self.problem.encounter_period
|
|
475
504
|
self.encounter_wavenumber = self.problem.encounter_wavenumber
|
|
476
505
|
self.encounter_wavelength = self.problem.encounter_wavelength
|
|
@@ -495,6 +524,13 @@ class LinearPotentialFlowResult:
|
|
|
495
524
|
__rich_repr__ = LinearPotentialFlowProblem.__rich_repr__
|
|
496
525
|
|
|
497
526
|
|
|
527
|
+
class FailedLinearPotentialFlowResult(LinearPotentialFlowResult):
|
|
528
|
+
def __init__(self, problem, exception):
|
|
529
|
+
LinearPotentialFlowResult.__init__(self, problem)
|
|
530
|
+
self.forces = {dof: np.nan + 1j*np.nan for dof in self.influenced_dofs}
|
|
531
|
+
self.exception = exception
|
|
532
|
+
|
|
533
|
+
|
|
498
534
|
class DiffractionResult(LinearPotentialFlowResult):
|
|
499
535
|
|
|
500
536
|
def __init__(self, problem, *args, **kwargs):
|
|
@@ -510,10 +546,18 @@ class DiffractionResult(LinearPotentialFlowResult):
|
|
|
510
546
|
return [dict(**params,
|
|
511
547
|
influenced_dof=dof,
|
|
512
548
|
diffraction_force=self.forces[dof],
|
|
513
|
-
Froude_Krylov_force=FK[dof]
|
|
549
|
+
Froude_Krylov_force=FK[dof],
|
|
550
|
+
kind="DiffractionResult")
|
|
514
551
|
for dof in self.influenced_dofs]
|
|
515
552
|
|
|
516
553
|
|
|
554
|
+
class FailedDiffractionResult(DiffractionResult):
|
|
555
|
+
def __init__(self, problem, exception):
|
|
556
|
+
DiffractionResult.__init__(self, problem)
|
|
557
|
+
self.forces = {dof: np.nan for dof in self.influenced_dofs}
|
|
558
|
+
self.exception = exception
|
|
559
|
+
|
|
560
|
+
|
|
517
561
|
class RadiationResult(LinearPotentialFlowResult):
|
|
518
562
|
|
|
519
563
|
def __init__(self, problem, *args, **kwargs):
|
|
@@ -544,5 +588,13 @@ class RadiationResult(LinearPotentialFlowResult):
|
|
|
544
588
|
return [dict(params,
|
|
545
589
|
influenced_dof=dof,
|
|
546
590
|
added_mass=self.added_mass[dof],
|
|
547
|
-
radiation_damping=self.radiation_damping[dof]
|
|
591
|
+
radiation_damping=self.radiation_damping[dof],
|
|
592
|
+
kind="RadiationResult")
|
|
548
593
|
for dof in self.influenced_dofs]
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
class FailedRadiationResult(RadiationResult):
|
|
597
|
+
def __init__(self, problem, exception):
|
|
598
|
+
RadiationResult.__init__(self, problem)
|
|
599
|
+
self.forces = {dof: np.nan + 1j*np.nan for dof in self.influenced_dofs}
|
|
600
|
+
self.exception = exception
|