capytaine 2.3.1__cp314-cp314t-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.
Files changed (92) 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 +16 -0
  5. capytaine/__init__.py +36 -0
  6. capytaine/bem/__init__.py +0 -0
  7. capytaine/bem/airy_waves.py +111 -0
  8. capytaine/bem/engines.py +441 -0
  9. capytaine/bem/problems_and_results.py +600 -0
  10. capytaine/bem/solver.py +594 -0
  11. capytaine/bodies/__init__.py +4 -0
  12. capytaine/bodies/bodies.py +1221 -0
  13. capytaine/bodies/dofs.py +19 -0
  14. capytaine/bodies/predefined/__init__.py +6 -0
  15. capytaine/bodies/predefined/cylinders.py +151 -0
  16. capytaine/bodies/predefined/rectangles.py +111 -0
  17. capytaine/bodies/predefined/spheres.py +70 -0
  18. capytaine/green_functions/FinGreen3D/.gitignore +1 -0
  19. capytaine/green_functions/FinGreen3D/FinGreen3D.f90 +3589 -0
  20. capytaine/green_functions/FinGreen3D/LICENSE +165 -0
  21. capytaine/green_functions/FinGreen3D/Makefile +16 -0
  22. capytaine/green_functions/FinGreen3D/README.md +24 -0
  23. capytaine/green_functions/FinGreen3D/test_program.f90 +39 -0
  24. capytaine/green_functions/LiangWuNoblesse/.gitignore +1 -0
  25. capytaine/green_functions/LiangWuNoblesse/LICENSE +504 -0
  26. capytaine/green_functions/LiangWuNoblesse/LiangWuNoblesseWaveTerm.f90 +751 -0
  27. capytaine/green_functions/LiangWuNoblesse/Makefile +16 -0
  28. capytaine/green_functions/LiangWuNoblesse/README.md +2 -0
  29. capytaine/green_functions/LiangWuNoblesse/test_program.f90 +28 -0
  30. capytaine/green_functions/__init__.py +2 -0
  31. capytaine/green_functions/abstract_green_function.py +64 -0
  32. capytaine/green_functions/delhommeau.py +507 -0
  33. capytaine/green_functions/hams.py +204 -0
  34. capytaine/green_functions/libs/Delhommeau_float32.cpython-314t-darwin.so +0 -0
  35. capytaine/green_functions/libs/Delhommeau_float64.cpython-314t-darwin.so +0 -0
  36. capytaine/green_functions/libs/__init__.py +0 -0
  37. capytaine/io/__init__.py +0 -0
  38. capytaine/io/bemio.py +153 -0
  39. capytaine/io/legacy.py +328 -0
  40. capytaine/io/mesh_loaders.py +1086 -0
  41. capytaine/io/mesh_writers.py +692 -0
  42. capytaine/io/meshio.py +38 -0
  43. capytaine/io/wamit.py +479 -0
  44. capytaine/io/xarray.py +668 -0
  45. capytaine/matrices/__init__.py +16 -0
  46. capytaine/matrices/block.py +592 -0
  47. capytaine/matrices/block_toeplitz.py +325 -0
  48. capytaine/matrices/builders.py +89 -0
  49. capytaine/matrices/linear_solvers.py +232 -0
  50. capytaine/matrices/low_rank.py +395 -0
  51. capytaine/meshes/__init__.py +6 -0
  52. capytaine/meshes/clipper.py +465 -0
  53. capytaine/meshes/collections.py +342 -0
  54. capytaine/meshes/geometry.py +409 -0
  55. capytaine/meshes/mesh_like_protocol.py +37 -0
  56. capytaine/meshes/meshes.py +890 -0
  57. capytaine/meshes/predefined/__init__.py +6 -0
  58. capytaine/meshes/predefined/cylinders.py +314 -0
  59. capytaine/meshes/predefined/rectangles.py +261 -0
  60. capytaine/meshes/predefined/spheres.py +62 -0
  61. capytaine/meshes/properties.py +276 -0
  62. capytaine/meshes/quadratures.py +80 -0
  63. capytaine/meshes/quality.py +448 -0
  64. capytaine/meshes/surface_integrals.py +63 -0
  65. capytaine/meshes/symmetric.py +462 -0
  66. capytaine/post_pro/__init__.py +6 -0
  67. capytaine/post_pro/free_surfaces.py +88 -0
  68. capytaine/post_pro/impedance.py +92 -0
  69. capytaine/post_pro/kochin.py +54 -0
  70. capytaine/post_pro/rao.py +60 -0
  71. capytaine/tools/__init__.py +0 -0
  72. capytaine/tools/cache_on_disk.py +26 -0
  73. capytaine/tools/deprecation_handling.py +18 -0
  74. capytaine/tools/lists_of_points.py +52 -0
  75. capytaine/tools/lru_cache.py +49 -0
  76. capytaine/tools/optional_imports.py +27 -0
  77. capytaine/tools/prony_decomposition.py +150 -0
  78. capytaine/tools/symbolic_multiplication.py +149 -0
  79. capytaine/tools/timer.py +66 -0
  80. capytaine/ui/__init__.py +0 -0
  81. capytaine/ui/cli.py +28 -0
  82. capytaine/ui/rich.py +5 -0
  83. capytaine/ui/vtk/__init__.py +3 -0
  84. capytaine/ui/vtk/animation.py +329 -0
  85. capytaine/ui/vtk/body_viewer.py +28 -0
  86. capytaine/ui/vtk/helpers.py +82 -0
  87. capytaine/ui/vtk/mesh_viewer.py +461 -0
  88. capytaine-2.3.1.dist-info/LICENSE +674 -0
  89. capytaine-2.3.1.dist-info/METADATA +750 -0
  90. capytaine-2.3.1.dist-info/RECORD +92 -0
  91. capytaine-2.3.1.dist-info/WHEEL +6 -0
  92. capytaine-2.3.1.dist-info/entry_points.txt +3 -0
capytaine/io/legacy.py ADDED
@@ -0,0 +1,328 @@
1
+ """Import or export Nemoh.cal files for backward compatibility with Nemoh 2."""
2
+ # Copyright (C) 2017-2019 Matthieu Ancellin
3
+ # See LICENSE file at <https://github.com/mancellin/capytaine>
4
+
5
+ import os
6
+ import logging
7
+
8
+ import numpy as np
9
+
10
+ from capytaine.bem.solver import BEMSolver
11
+ from capytaine.io.xarray import assemble_dataset
12
+ from capytaine.io.mesh_writers import write_MAR
13
+ from capytaine.bodies.bodies import FloatingBody
14
+ from capytaine.bem.problems_and_results import DiffractionProblem, RadiationProblem
15
+ from capytaine.meshes.geometry import Axis
16
+
17
+ LOG = logging.getLogger(__name__)
18
+
19
+
20
+ def import_cal_file(filepath):
21
+ """Read a Nemoh.cal file and return a list of problems."""
22
+
23
+ with open(filepath, 'r') as cal_file:
24
+
25
+ cal_file.readline() # Unused line.
26
+ rho = float(cal_file.readline().split()[0])
27
+ g = float(cal_file.readline().split()[0])
28
+ water_depth = float(cal_file.readline().split()[0])
29
+ if water_depth == 0.0:
30
+ water_depth = np.inf
31
+ xeff, yeff = (float(x) for x in cal_file.readline().split()[0:2])
32
+
33
+ bodies = []
34
+
35
+ cal_file.readline() # Unused line.
36
+ nb_bodies = int(cal_file.readline().split()[0])
37
+ for _ in range(nb_bodies):
38
+ cal_file.readline() # Unused line.
39
+ mesh_file = cal_file.readline().split()[0].strip()
40
+ mesh_file = os.path.join(os.path.dirname(filepath), mesh_file) # mesh path are relative to Nemoh.cal
41
+ cal_file.readline() # Number of points, number of panels (unused)
42
+
43
+ if os.path.splitext(mesh_file)[1] == '.py':
44
+ from importlib.util import spec_from_file_location, module_from_spec
45
+ spec = spec_from_file_location("body_initialization_module", mesh_file)
46
+ body_initialization = module_from_spec(spec)
47
+ spec.loader.exec_module(body_initialization)
48
+ body = body_initialization.body
49
+ else:
50
+ body = FloatingBody.from_file(mesh_file)
51
+
52
+ nb_dofs = int(cal_file.readline().split()[0])
53
+ for i_dof in range(nb_dofs):
54
+ dof_data = cal_file.readline().split()
55
+ if int(dof_data[0]) == 1:
56
+ direction = np.array([float(x) for x in dof_data[1:4]])
57
+ body.add_translation_dof(direction=direction)
58
+ elif int(dof_data[0]) == 2:
59
+ direction = np.array([float(x) for x in dof_data[1:4]])
60
+ center_of_mass = np.array([float(x) for x in dof_data[4:7]])
61
+ body.add_rotation_dof(Axis(vector=direction, point=center_of_mass))
62
+
63
+ nb_forces = int(cal_file.readline().split()[0])
64
+ for i_force in range(nb_forces):
65
+ force_data = cal_file.readline().split()
66
+ if int(force_data[0]) == 1:
67
+ direction = np.array([float(x) for x in force_data[1:4]])
68
+ elif int(force_data[0]) == 2:
69
+ direction = np.array([float(x) for x in force_data[1:4]])
70
+ center_of_mass = np.array([float(x) for x in force_data[4:7]])
71
+ # TODO: use the generalized forces.
72
+
73
+ nb_additional_lines = int(cal_file.readline().split()[0])
74
+ for _ in range(nb_additional_lines):
75
+ cal_file.readline() # The additional lines are just ignored.
76
+
77
+ bodies.append(body)
78
+
79
+ if nb_bodies > 1:
80
+ bodies = FloatingBody.join_bodies(*bodies)
81
+ else:
82
+ bodies = bodies[0]
83
+
84
+ cal_file.readline() # Unused line.
85
+ frequency_data_string_without_comment = cal_file.readline().split('!')[0]
86
+ frequency_data = frequency_data_string_without_comment.split()
87
+ if len(frequency_data) == 3: # Nemoh v2 format
88
+ omega_range = np.linspace(float(frequency_data[1]), float(frequency_data[2]), int(frequency_data[0]))
89
+ else:
90
+ type_of_frequency_data = int(frequency_data[0])
91
+ if type_of_frequency_data == 1: # angular frequency
92
+ omega_range = np.linspace(float(frequency_data[2]), float(frequency_data[3]), int(frequency_data[1]))
93
+ elif type_of_frequency_data == 2: # frequency
94
+ omega_range = 2*np.pi*np.linspace(float(frequency_data[2]), float(frequency_data[3]), int(frequency_data[1]))
95
+ elif type_of_frequency_data == 3: # period
96
+ omega_range = 2*np.pi/np.linspace(float(frequency_data[2]), float(frequency_data[3]), int(frequency_data[1]))
97
+ else:
98
+ raise ValueError(f"Cannot parse the frequency data \"{frequency_data_string_without_comment}\" in {filepath}.")
99
+
100
+
101
+ direction_data = cal_file.readline().split()
102
+ direction_range = np.linspace(float(direction_data[1]), float(direction_data[2]), int(direction_data[0]))
103
+ direction_range = np.pi/180*direction_range # conversion from degrees to radians.
104
+
105
+ # The options below are not implemented yet.
106
+
107
+ cal_file.readline() # Unused line.
108
+ irf_data = cal_file.readline()
109
+ show_pressure = cal_file.readline().split()[0] == "1"
110
+ kochin_data = cal_file.readline().split()
111
+ kochin_range = np.linspace(float(kochin_data[1]), float(kochin_data[2]), int(kochin_data[0]))
112
+ free_surface_data = cal_file.readline().split()
113
+
114
+ # Generate Capytaine's problem objects
115
+ env_args = dict(body=bodies, rho=rho, water_depth=water_depth, g=g)
116
+ problems = []
117
+ for omega in omega_range:
118
+ for direction in direction_range:
119
+ problems.append(DiffractionProblem(wave_direction=direction, omega=omega, **env_args))
120
+ for dof in bodies.dofs:
121
+ problems.append(RadiationProblem(radiating_dof=dof, omega=omega, **env_args))
122
+
123
+ return problems
124
+
125
+
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
+ def write_dataset_as_tecplot_files(results_directory, data):
226
+ """Write some of the data from a xarray dataset into legacy tecplot file outputs."""
227
+
228
+ if 'added_mass' in data:
229
+ with open(os.path.join(results_directory, 'RadiationCoefficients.tec'), 'w') as fi:
230
+ for i in range(len(data['radiating_dof'])+1):
231
+ fi.write(f'...\n')
232
+ for dof in data.radiating_dof:
233
+ fi.write(f'{dof.values}\n')
234
+ for o in data.omega:
235
+ fi.write(f' {o.values:e} ')
236
+ for dof2 in data.influenced_dof:
237
+ fi.write(f"{data['added_mass'].sel(omega=o, radiating_dof=dof, influenced_dof=dof2).values:e}")
238
+ fi.write(' ')
239
+ fi.write(f"{data['radiation_damping'].sel(omega=o, radiating_dof=dof, influenced_dof=dof2).values:e}")
240
+ fi.write(' ')
241
+ fi.write('\n')
242
+
243
+ if 'diffraction_force' in data:
244
+ data['excitation_force'] = data['Froude_Krylov_force'] + data['diffraction_force']
245
+ with open(os.path.join(results_directory, 'ExcitationForce.tec'), 'w') as fi:
246
+ for i in range(len(data.influenced_dof)+1):
247
+ fi.write(f'...\n')
248
+ for wave_direction in data.wave_direction.values:
249
+ fi.write(f'angle={wave_direction}\n')
250
+ for o in data.omega.values:
251
+ fi.write(f' {o:e} ')
252
+ for dof in data.influenced_dof:
253
+ val = data['excitation_force'].sel(omega=o, wave_direction=wave_direction, influenced_dof=dof).values
254
+ fi.write(f'{np.abs(val):e}')
255
+ fi.write(' ')
256
+ fi.write(f'{np.angle(val):e}')
257
+ fi.write(' ')
258
+ fi.write('\n')
259
+
260
+
261
+ def _hydrostatics_writer(hydrostatics_file_path, kh_file_path, body):
262
+ """Write the Hydrostatics.dat and KH.dat files"""
263
+ with open(hydrostatics_file_path, 'w') as hf:
264
+ for j in range(3):
265
+ line = f'XF = {body.center_of_buoyancy[j]:7.4f} - XG = {body.center_of_mass[j]:7.4f} \n'
266
+ hf.write(line)
267
+ line = f'Displacement = {body.volume:1.6E}'
268
+ hf.write(line)
269
+ hf.close()
270
+ np.savetxt(kh_file_path, body.hydrostatic_stiffness.values, fmt='%1.6E')
271
+
272
+
273
+ def export_hydrostatics(hydrostatics_directory, bodies):
274
+ """Export rigid body hydrostatics in Nemoh's format (KH.dat and Hydrostatics.dat).
275
+
276
+ Parameters
277
+ ----------
278
+ hydrostatics_directory: string
279
+ Path to the directory in which the data will be written (two files per body)
280
+ bodies: FloatingBody or list of FloatingBody
281
+ The body or the list of bodies. Each body is assumed to be a single
282
+ rigid body with 6 dofs. Each FloatingBody object is expected to have an
283
+ `inertia_matrix` and a `hydrostatic_stiffness` parameter.
284
+
285
+ Return
286
+ ------
287
+ None
288
+ """
289
+
290
+ if os.path.isdir(hydrostatics_directory):
291
+ LOG.warning(f"""Exporting problem in already existing directory: {hydrostatics_directory}
292
+ You might be overwriting existing files!""")
293
+ else:
294
+ os.makedirs(hydrostatics_directory)
295
+
296
+ if isinstance(bodies, FloatingBody):
297
+ bodies = [bodies]
298
+
299
+ hydrostatics_file_name = "Hydrostatics.dat"
300
+ kh_file_name = "KH.dat"
301
+
302
+ body_count = len(bodies)
303
+ if body_count == 1:
304
+ body = bodies[0]
305
+ hydrostatics_file_path = os.path.join(hydrostatics_directory, hydrostatics_file_name)
306
+ kh_file_path = os.path.join(hydrostatics_directory, kh_file_name)
307
+ _hydrostatics_writer(hydrostatics_file_path, kh_file_path, body)
308
+ else:
309
+ for (i, body) in enumerate(bodies):
310
+ hydrostatics_file_path = os.path.join(hydrostatics_directory, f"Hydrostatics_{i}.dat")
311
+ kh_file_path = os.path.join(hydrostatics_directory, f"KH_{i}.dat")
312
+ _hydrostatics_writer(hydrostatics_file_path, kh_file_path, body)
313
+
314
+
315
+ def run_cal_file(paramfile):
316
+ problems = import_cal_file(paramfile)
317
+ solver = BEMSolver()
318
+ results = solver.solve_all(problems)
319
+ data = assemble_dataset(results)
320
+
321
+ results_directory = os.path.join(os.path.dirname(paramfile), 'results')
322
+ try:
323
+ os.mkdir(results_directory)
324
+ except FileExistsError:
325
+ LOG.warning(f"The output directory ({results_directory}) already exists. You might be overwriting existing data.")
326
+
327
+ LOG.info("Write results in legacy tecplot format.")
328
+ write_dataset_as_tecplot_files(results_directory, data)