capytaine 2.2.1__cp38-cp38-macosx_14_0_arm64.whl → 2.3.1__cp38-cp38-macosx_14_0_arm64.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/.dylibs/libgcc_s.1.1.dylib +0 -0
- capytaine/.dylibs/libgfortran.5.dylib +0 -0
- capytaine/.dylibs/libquadmath.0.dylib +0 -0
- capytaine/__about__.py +1 -1
- capytaine/__init__.py +2 -1
- 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.cpython-38-darwin.so +0 -0
- capytaine/green_functions/libs/Delhommeau_float64.cpython-38-darwin.so +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.2.1.dist-info → capytaine-2.3.1.dist-info}/METADATA +6 -10
- capytaine-2.3.1.dist-info/RECORD +92 -0
- capytaine-2.2.1.dist-info/RECORD +0 -76
- {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/io/wamit.py
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Union, Iterable, Tuple, TextIO
|
|
4
|
+
import xarray
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
DOF_INDEX = {"Surge": 1, "Sway": 2, "Heave": 3, "Roll": 4, "Pitch": 5, "Yaw": 6}
|
|
9
|
+
|
|
10
|
+
DOF_TYPE = {
|
|
11
|
+
dof: "trans" if dof in {"Surge", "Sway", "Heave"} else "rot" for dof in DOF_INDEX
|
|
12
|
+
}
|
|
13
|
+
K_LOOKUP = {
|
|
14
|
+
("trans", "trans"): 3,
|
|
15
|
+
("trans", "rot"): 4,
|
|
16
|
+
("rot", "trans"): 4,
|
|
17
|
+
("rot", "rot"): 5,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_dof_index_and_k(dof_i: str, dof_j: str) -> Tuple[int, int, int]:
|
|
22
|
+
"""Get the degree of freedom indices and the corresponding stiffness matrix index.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
dof_i: str
|
|
27
|
+
The name of the first degree of freedom.
|
|
28
|
+
dof_j: str
|
|
29
|
+
The name of the second degree of freedom.
|
|
30
|
+
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
tuple
|
|
34
|
+
A tuple containing the indices (i, j) and the stiffness matrix index k.
|
|
35
|
+
"""
|
|
36
|
+
i = DOF_INDEX[dof_i]
|
|
37
|
+
j = DOF_INDEX[dof_j]
|
|
38
|
+
t_i = DOF_TYPE[dof_i]
|
|
39
|
+
t_j = DOF_TYPE[dof_j]
|
|
40
|
+
k = K_LOOKUP[(t_i, t_j)]
|
|
41
|
+
return i, j, k
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def check_dataset_ready_for_export(ds: xarray.Dataset) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Sanity checks to validate that the dataset is exportable to BEMIO/WAMIT-like formats.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
ds : xarray.Dataset
|
|
51
|
+
The dataset to be validated.
|
|
52
|
+
|
|
53
|
+
Raises
|
|
54
|
+
------
|
|
55
|
+
ValueError
|
|
56
|
+
If any unsupported coordinate has multiple values or
|
|
57
|
+
if non-rigid-body DOFs are present.
|
|
58
|
+
"""
|
|
59
|
+
# 1. Check for singleton coordinates
|
|
60
|
+
critical_coords = ["water_depth", "g", "rho"]
|
|
61
|
+
coords_with_multiple_values = [
|
|
62
|
+
k
|
|
63
|
+
for k in critical_coords
|
|
64
|
+
if k in ds.coords and len(ds.coords[k].dims) > 0 and ds.sizes[k] > 1
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
if coords_with_multiple_values:
|
|
68
|
+
msg = (
|
|
69
|
+
"Export formats like WAMIT require only one value for each of the following coordinates: "
|
|
70
|
+
f"{', '.join(critical_coords)}.\n"
|
|
71
|
+
f"Problematic dimensions: {coords_with_multiple_values}.\n"
|
|
72
|
+
"You can extract a subset using:\n"
|
|
73
|
+
f" ds_slice = ds.sel({', '.join([f'{k}={str(ds.coords[k].values[0])}' for k in coords_with_multiple_values])})"
|
|
74
|
+
)
|
|
75
|
+
raise ValueError(msg)
|
|
76
|
+
|
|
77
|
+
# 2. Check for rigid-body DOFs only
|
|
78
|
+
rigid_body_dofs = ("Surge", "Sway", "Heave", "Roll", "Pitch", "Yaw")
|
|
79
|
+
if "influenced_dof" in ds.coords:
|
|
80
|
+
dofs = set(ds.influenced_dof.values)
|
|
81
|
+
non_rigid_dofs = dofs.difference(set(rigid_body_dofs))
|
|
82
|
+
if non_rigid_dofs:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"WAMIT Export is only supported for single rigid body.\n"
|
|
85
|
+
f"Unexpected DOFs: {non_rigid_dofs}.\n"
|
|
86
|
+
f"Allowed DOFs: {rigid_body_dofs}"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def identify_frequency_axis(
|
|
91
|
+
dataset: Union[xarray.Dataset, xarray.DataArray],
|
|
92
|
+
) -> Tuple[str, np.ndarray, np.ndarray]:
|
|
93
|
+
"""
|
|
94
|
+
Identify the frequency axis in the dataset and return its values along with the periods.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
dataset : xarray.Dataset or xarray.DataArray
|
|
99
|
+
Dataset that must include 'period' coordinate and at least one of:
|
|
100
|
+
'omega', 'freq', 'period', 'wavenumber', or 'wavelength' as dimension.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
freq_key : str
|
|
105
|
+
The name of the main frequency-like coordinate.
|
|
106
|
+
freq_vals : np.ndarray
|
|
107
|
+
The values from the frequency coordinate (as present in the dataset).
|
|
108
|
+
period_vals : np.ndarray
|
|
109
|
+
The values of the 'period' coordinate in seconds.
|
|
110
|
+
|
|
111
|
+
Raises
|
|
112
|
+
------
|
|
113
|
+
ValueError
|
|
114
|
+
If 'period' is not a coordinate or if no frequency-like dimension is found.
|
|
115
|
+
"""
|
|
116
|
+
allowed_keys = {"omega", "freq", "wavenumber", "wavelength", "period"}
|
|
117
|
+
dataset_dims = set(dataset.dims)
|
|
118
|
+
keys_in_dataset = dataset_dims & allowed_keys
|
|
119
|
+
|
|
120
|
+
if "period" not in dataset.coords:
|
|
121
|
+
raise ValueError("Dataset must contain 'period' as a coordinate.")
|
|
122
|
+
|
|
123
|
+
# Prioritize 'period' if it is one of the dimensions
|
|
124
|
+
if "period" in dataset_dims:
|
|
125
|
+
freq_key = "period"
|
|
126
|
+
elif len(keys_in_dataset) >= 1:
|
|
127
|
+
freq_key = sorted(keys_in_dataset)[0] # deterministic choice
|
|
128
|
+
else:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
"Dataset must contain at least one frequency-like dimension among: "
|
|
131
|
+
"'omega', 'freq', 'wavenumber', 'wavelength', or 'period'."
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
freq_vals = np.asarray(dataset[freq_key].values)
|
|
135
|
+
period_vals = np.asarray(dataset["period"].values)
|
|
136
|
+
|
|
137
|
+
return freq_key, freq_vals, period_vals
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def export_wamit_hst(
|
|
141
|
+
dataset: xarray.Dataset, filename: str, length_scale: float = 1.0
|
|
142
|
+
) -> None:
|
|
143
|
+
"""
|
|
144
|
+
Export the nondimensional hydrostatic stiffness matrix to a WAMIT .hst file.
|
|
145
|
+
|
|
146
|
+
Format:
|
|
147
|
+
I J C(I,J)
|
|
148
|
+
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
151
|
+
dataset: xarray.Dataset
|
|
152
|
+
Must contain 'hydrostatics' field with 'hydrostatic_stiffness' (named-dict or labeled 6x6 array).
|
|
153
|
+
Must also contain 'rho' and 'g' (either in dataset or within hydrostatics).
|
|
154
|
+
filename: str
|
|
155
|
+
Output path for the .hst file.
|
|
156
|
+
length_scale: float
|
|
157
|
+
Reference length scale L for nondimensionalization.
|
|
158
|
+
"""
|
|
159
|
+
if "hydrostatic_stiffness" not in dataset:
|
|
160
|
+
raise ValueError("Dataset must contain a 'hydrostatic_stiffness' field.")
|
|
161
|
+
|
|
162
|
+
# Reduce all extra dimensions to their first value, except the last two (should be 6x6)
|
|
163
|
+
hydrostatic = dataset["hydrostatic_stiffness"]
|
|
164
|
+
C = np.asarray(hydrostatic)
|
|
165
|
+
if C is None or C.shape != (6, 6):
|
|
166
|
+
raise ValueError("'hydrostatic_stiffness' must be a 6x6 matrix.")
|
|
167
|
+
|
|
168
|
+
rho = float(np.atleast_1d(dataset.get("rho")).item())
|
|
169
|
+
g = float(np.atleast_1d(dataset.get("g")).item())
|
|
170
|
+
|
|
171
|
+
# DOF order used in Capytaine
|
|
172
|
+
dof_names = ["Surge", "Sway", "Heave", "Roll", "Pitch", "Yaw"]
|
|
173
|
+
|
|
174
|
+
with open(filename, "w") as f:
|
|
175
|
+
for i_local, dof_i in enumerate(dof_names):
|
|
176
|
+
for j_local, dof_j in enumerate(dof_names):
|
|
177
|
+
cij = C[i_local, j_local]
|
|
178
|
+
i, j, k = get_dof_index_and_k(dof_i, dof_j)
|
|
179
|
+
norm = rho * g * (length_scale**k)
|
|
180
|
+
cij_nd = cij / norm
|
|
181
|
+
f.write(f"{i:5d} {j:5d} {cij_nd:12.6e}\n")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def export_wamit_1(
|
|
185
|
+
dataset: xarray.Dataset, filename: str, length_scale: float = 1.0
|
|
186
|
+
) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Export added mass and radiation damping coefficients to a WAMIT .1 file.
|
|
189
|
+
|
|
190
|
+
Coefficients are normalized as:
|
|
191
|
+
Aij = Aij / (rho * length_scale^k)
|
|
192
|
+
Bij = Bij / (omega * rho * length_scale^k)
|
|
193
|
+
|
|
194
|
+
Format:
|
|
195
|
+
PER I J Aij [Bij]
|
|
196
|
+
|
|
197
|
+
Special handling:
|
|
198
|
+
- For PER = -1 (omega = inf), only Aij is written.
|
|
199
|
+
- For PER = 0 (omega = 0), only Aij is written.
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
dataset: xarray.Dataset
|
|
204
|
+
Must contain 'added_mass', 'radiation_damping', and either 'omega' or 'period'.
|
|
205
|
+
filename: str
|
|
206
|
+
Output .1 file.
|
|
207
|
+
length_scale: float
|
|
208
|
+
Reference length scale (L) used for normalization.
|
|
209
|
+
|
|
210
|
+
Raises
|
|
211
|
+
------
|
|
212
|
+
ValueError
|
|
213
|
+
If required fields are missing or forward speed is not zero.
|
|
214
|
+
"""
|
|
215
|
+
if "added_mass" not in dataset or "radiation_damping" not in dataset:
|
|
216
|
+
raise ValueError("Missing 'added_mass' or 'radiation_damping' in dataset.")
|
|
217
|
+
|
|
218
|
+
if not np.isclose(dataset["forward_speed"].item(), 0.0):
|
|
219
|
+
raise ValueError("Forward speed must be zero for WAMIT export.")
|
|
220
|
+
|
|
221
|
+
rho = dataset["rho"].item()
|
|
222
|
+
added_mass = dataset["added_mass"]
|
|
223
|
+
damping = dataset["radiation_damping"]
|
|
224
|
+
omegas = dataset["omega"]
|
|
225
|
+
dofs = list(added_mass.coords["influenced_dof"].values)
|
|
226
|
+
|
|
227
|
+
# Determine main frequency coordinate
|
|
228
|
+
freq_key, freq_vals, period_vals = identify_frequency_axis(dataset=added_mass)
|
|
229
|
+
|
|
230
|
+
# Separate lines into blocks depending on period type
|
|
231
|
+
period_blocks = {
|
|
232
|
+
"T_zero": [], # period == 0 → omega = inf → PER = -1
|
|
233
|
+
"T_inf": [], # period == inf → omega = 0 → PER = 0
|
|
234
|
+
"T_regular": [], # finite, non-zero periods
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
for omega, freq_val, period in zip(omegas, freq_vals, period_vals):
|
|
238
|
+
for dof_i in dofs:
|
|
239
|
+
for dof_j in dofs:
|
|
240
|
+
j_dof, i_dof, k = get_dof_index_and_k(dof_i, dof_j)
|
|
241
|
+
A = added_mass.sel(
|
|
242
|
+
{
|
|
243
|
+
freq_key: freq_val,
|
|
244
|
+
"influenced_dof": dof_i,
|
|
245
|
+
"radiating_dof": dof_j,
|
|
246
|
+
}
|
|
247
|
+
).item()
|
|
248
|
+
norm = rho * (length_scale**k)
|
|
249
|
+
A_norm = A / norm
|
|
250
|
+
|
|
251
|
+
if np.isclose(period, 0.0):
|
|
252
|
+
# Case PER = -1 (omega = inf)
|
|
253
|
+
line = f"{-1.0:12.6e}\t{i_dof:5d}\t{j_dof:5d}\t{A_norm:12.6e}\n"
|
|
254
|
+
period_blocks["T_zero"].append(line)
|
|
255
|
+
elif np.isinf(period):
|
|
256
|
+
# Case PER = 0 (omega = 0)
|
|
257
|
+
line = f"{0.0:12.6e}\t{i_dof:5d}\t{j_dof:5d}\t{A_norm:12.6e}\n"
|
|
258
|
+
period_blocks["T_inf"].append(line)
|
|
259
|
+
else:
|
|
260
|
+
B = damping.sel(
|
|
261
|
+
{
|
|
262
|
+
freq_key: freq_val,
|
|
263
|
+
"influenced_dof": dof_i,
|
|
264
|
+
"radiating_dof": dof_j,
|
|
265
|
+
}
|
|
266
|
+
).item()
|
|
267
|
+
B_norm = B / (omega * norm)
|
|
268
|
+
line = f"{period:12.6e}\t{i_dof:5d}\t{j_dof:5d}\t{A_norm:12.6e}\t{B_norm:12.6e}\n"
|
|
269
|
+
period_blocks["T_regular"].append((period, line))
|
|
270
|
+
|
|
271
|
+
# Sort regular lines by increasing period
|
|
272
|
+
sorted_regular = sorted(period_blocks["T_regular"], key=lambda t: t[0])
|
|
273
|
+
sorted_lines = [line for _, line in sorted_regular]
|
|
274
|
+
|
|
275
|
+
# Write to file
|
|
276
|
+
with open(filename, "w") as f:
|
|
277
|
+
f.writelines(period_blocks["T_zero"])
|
|
278
|
+
f.writelines(period_blocks["T_inf"])
|
|
279
|
+
f.writelines(sorted_lines)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _format_excitation_line(
|
|
283
|
+
period: float, beta_deg: float, i_dof: int, force: complex
|
|
284
|
+
) -> str:
|
|
285
|
+
"""Format a WAMIT excitation line.
|
|
286
|
+
|
|
287
|
+
Parameters
|
|
288
|
+
----------
|
|
289
|
+
period: float
|
|
290
|
+
Wave period.
|
|
291
|
+
beta_deg: float
|
|
292
|
+
Wave direction (degrees).
|
|
293
|
+
i_dof: int
|
|
294
|
+
Degree of freedom index.
|
|
295
|
+
force: complex
|
|
296
|
+
Excitation force (complex).
|
|
297
|
+
|
|
298
|
+
Returns
|
|
299
|
+
-------
|
|
300
|
+
str
|
|
301
|
+
Formatted excitation line.
|
|
302
|
+
"""
|
|
303
|
+
force_conj = np.conj(force)
|
|
304
|
+
mod_f = np.abs(force_conj)
|
|
305
|
+
phi_f = np.degrees(np.angle(force_conj))
|
|
306
|
+
return "{:12.6e}\t{:12.6f}\t{:5d}\t{:12.6e}\t{:12.3f}\t{:12.6e}\t{:12.6e}\n".format(
|
|
307
|
+
period, beta_deg, i_dof, mod_f, phi_f, force_conj.real, force_conj.imag
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _write_wamit_excitation_line(
|
|
312
|
+
f: TextIO,
|
|
313
|
+
freq_key: str,
|
|
314
|
+
freq_val: float,
|
|
315
|
+
period: float,
|
|
316
|
+
beta: float,
|
|
317
|
+
dof: str,
|
|
318
|
+
field: xarray.DataArray,
|
|
319
|
+
rho: float,
|
|
320
|
+
g: float,
|
|
321
|
+
wave_amplitude: float,
|
|
322
|
+
length_scale: float,
|
|
323
|
+
):
|
|
324
|
+
"""Write a single line for WAMIT .3 file format, using freq_key (omega or period)."""
|
|
325
|
+
beta_deg = np.degrees(beta)
|
|
326
|
+
i_dof = DOF_INDEX.get(dof)
|
|
327
|
+
if i_dof is None:
|
|
328
|
+
raise KeyError(f"DOF '{dof}' is not recognized in DOF_INDEX mapping.")
|
|
329
|
+
|
|
330
|
+
dof_type = DOF_TYPE.get(dof, "trans")
|
|
331
|
+
m = 2 if dof_type == "trans" else 3
|
|
332
|
+
norm = rho * g * wave_amplitude * (length_scale**m)
|
|
333
|
+
|
|
334
|
+
# Select value using appropriate key (omega or period)
|
|
335
|
+
force = field.sel(
|
|
336
|
+
{freq_key: freq_val, "wave_direction": beta, "influenced_dof": dof}
|
|
337
|
+
).item()
|
|
338
|
+
force_normalized = force / norm
|
|
339
|
+
|
|
340
|
+
line = _format_excitation_line(period, beta_deg, i_dof, force_normalized)
|
|
341
|
+
f.write(line)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _export_wamit_excitation_force(
|
|
345
|
+
dataset: xarray.Dataset,
|
|
346
|
+
field_name: str,
|
|
347
|
+
filename: str,
|
|
348
|
+
length_scale: float = 1.0,
|
|
349
|
+
wave_amplitude: float = 1.0,
|
|
350
|
+
):
|
|
351
|
+
"""
|
|
352
|
+
Export excitation-like forces to a WAMIT .3-style file.
|
|
353
|
+
|
|
354
|
+
Format:
|
|
355
|
+
PER BETA I Fmagnitude Fphase Freal Fimaginary
|
|
356
|
+
"""
|
|
357
|
+
forward_speed = dataset["forward_speed"].values
|
|
358
|
+
if not np.isclose(forward_speed, 0.0):
|
|
359
|
+
raise ValueError("Forward speed must be zero for WAMIT export.")
|
|
360
|
+
|
|
361
|
+
if field_name not in dataset:
|
|
362
|
+
raise ValueError(f"Missing field '{field_name}' in dataset.")
|
|
363
|
+
|
|
364
|
+
field = dataset[field_name]
|
|
365
|
+
rho = dataset["rho"].item()
|
|
366
|
+
betas = field.coords["wave_direction"].values
|
|
367
|
+
dofs = list(field.coords["influenced_dof"].values)
|
|
368
|
+
g = dataset["g"].item()
|
|
369
|
+
|
|
370
|
+
# Determine main frequency coordinate
|
|
371
|
+
freq_key, freq_vals, period_vals = identify_frequency_axis(dataset=dataset)
|
|
372
|
+
|
|
373
|
+
# Sort by increasing period
|
|
374
|
+
sorted_indices = np.argsort(period_vals)
|
|
375
|
+
sorted_periods = period_vals[sorted_indices]
|
|
376
|
+
sorted_freqs = freq_vals[sorted_indices]
|
|
377
|
+
|
|
378
|
+
with open(filename, "w") as f:
|
|
379
|
+
for freq_val, period in zip(sorted_freqs, sorted_periods):
|
|
380
|
+
# Skip WAMIT special cases
|
|
381
|
+
if np.isclose(freq_val, 0.0) or np.isinf(freq_val):
|
|
382
|
+
continue
|
|
383
|
+
for beta in betas:
|
|
384
|
+
for dof in dofs:
|
|
385
|
+
_write_wamit_excitation_line(
|
|
386
|
+
f=f,
|
|
387
|
+
freq_key=freq_key,
|
|
388
|
+
freq_val=freq_val,
|
|
389
|
+
period=period,
|
|
390
|
+
beta=beta,
|
|
391
|
+
dof=dof,
|
|
392
|
+
field=field,
|
|
393
|
+
rho=rho,
|
|
394
|
+
g=g,
|
|
395
|
+
wave_amplitude=wave_amplitude,
|
|
396
|
+
length_scale=length_scale,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def export_wamit_3(dataset: xarray.Dataset, filename: str) -> None:
|
|
401
|
+
"""Export total excitation to WAMIT .3 file.
|
|
402
|
+
|
|
403
|
+
Parameters
|
|
404
|
+
----------
|
|
405
|
+
dataset: xarray.Dataset
|
|
406
|
+
Dataset containing the desired complex-valued force field.
|
|
407
|
+
filename: str
|
|
408
|
+
Output path for the .3 file.
|
|
409
|
+
"""
|
|
410
|
+
_export_wamit_excitation_force(dataset, "excitation_force", filename)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def export_wamit_3fk(dataset: xarray.Dataset, filename: str) -> None:
|
|
414
|
+
"""Export Froude-Krylov contribution to WAMIT .3fk file.
|
|
415
|
+
|
|
416
|
+
Parameters
|
|
417
|
+
----------
|
|
418
|
+
dataset: xarray.Dataset
|
|
419
|
+
Dataset containing the desired complex-valued force field.
|
|
420
|
+
filename: str
|
|
421
|
+
Output path for the .3fk file.
|
|
422
|
+
"""
|
|
423
|
+
_export_wamit_excitation_force(dataset, "Froude_Krylov_force", filename)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def export_wamit_3sc(dataset: xarray.Dataset, filename: str) -> None:
|
|
427
|
+
"""Export scattered (diffraction) contribution to WAMIT .3sc file.
|
|
428
|
+
|
|
429
|
+
Parameters
|
|
430
|
+
----------
|
|
431
|
+
dataset: xarray.Dataset
|
|
432
|
+
Dataset containing the desired complex-valued force field.
|
|
433
|
+
filename: str
|
|
434
|
+
Output path for the .3sc file.
|
|
435
|
+
"""
|
|
436
|
+
_export_wamit_excitation_force(dataset, "diffraction_force", filename)
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def export_to_wamit(
|
|
440
|
+
dataset: xarray.Dataset,
|
|
441
|
+
problem_name: str,
|
|
442
|
+
exports: Iterable[str] = ("1", "3", "3fk", "3sc", "hst"),
|
|
443
|
+
) -> None:
|
|
444
|
+
"""
|
|
445
|
+
Master function to export a Capytaine dataset to WAMIT-format files.
|
|
446
|
+
|
|
447
|
+
Parameters
|
|
448
|
+
----------
|
|
449
|
+
dataset: xarray.Dataset
|
|
450
|
+
Dataset containing the desired complex-valued force field.
|
|
451
|
+
problem_name: str
|
|
452
|
+
Base filename for WAMIT files (e.g. "output" → output.1, output.3fk, etc.).
|
|
453
|
+
exports: iterable of str
|
|
454
|
+
Which files to export: any combination of "1", "3", "3fk", "3sc", "hst".
|
|
455
|
+
"""
|
|
456
|
+
export_map = {
|
|
457
|
+
"1": ("radiation coefficients", export_wamit_1, ".1"),
|
|
458
|
+
"3": ("total excitation force", export_wamit_3, ".3"),
|
|
459
|
+
"3fk": ("Froude-Krylov force", export_wamit_3fk, ".3fk"),
|
|
460
|
+
"3sc": ("diffraction force", export_wamit_3sc, ".3sc"),
|
|
461
|
+
"hst": ("hydrostatics", export_wamit_hst, ".hst"),
|
|
462
|
+
}
|
|
463
|
+
check_dataset_ready_for_export(dataset)
|
|
464
|
+
|
|
465
|
+
for key in exports:
|
|
466
|
+
if key not in export_map:
|
|
467
|
+
logger.warning(
|
|
468
|
+
f"Export to WAMIT format: unknown option '{key}' — skipping."
|
|
469
|
+
)
|
|
470
|
+
continue
|
|
471
|
+
|
|
472
|
+
description, func, ext = export_map[key]
|
|
473
|
+
filepath = f"{problem_name}{ext}"
|
|
474
|
+
|
|
475
|
+
try:
|
|
476
|
+
func(dataset, filepath)
|
|
477
|
+
logger.info(f"Export to WAMIT format: exported {filepath} ({description})")
|
|
478
|
+
except Exception as e:
|
|
479
|
+
logger.warning(f"Export to WAMIT format: did not export {filepath}: {e}")
|