Source code for capytaine.bodies.abstract_bodies

# Copyright 2026 Capytaine developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Abstract base class for floating bodies."""

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Literal, List, Union, Tuple

import numpy as np

from capytaine.bodies.dofs import (
    add_dofs_labels_to_vector,
    add_dofs_labels_to_matrix,
)
from capytaine.bodies.visualization import show_3d


[docs] class AbstractBody(ABC): """Abstract base class for FloatingBody and Multibody. Defines the shared interface that both single bodies and collections of bodies must implement. Subclasses must provide the following attributes (either as instance attributes set in __init__, or as properties/cached_properties): * name: str * mesh * lid_mesh * mesh_including_lid * hull_mask * dofs: dict * mass * center_of_mass """ name: str
[docs] @abstractmethod def rename(self, name): ...
def __lt__(self, other: AbstractBody) -> bool: """Arbitrary order. The point is to sort together the problems involving the same body.""" return self.name < other.name # --- Dof labelling (identical in both subclasses) ---
[docs] def add_dofs_labels_to_vector(self, vector): """Helper function turning a bare vector into a vector labelled by the name of the dofs of the body, to be used for instance for the computation of RAO.""" return add_dofs_labels_to_vector(self.dofs.keys(), vector)
[docs] def add_dofs_labels_to_matrix(self, matrix): """Helper function turning a bare matrix into a matrix labelled by the name of the dofs of the body, to be used for instance for the computation of RAO.""" return add_dofs_labels_to_matrix(self.dofs.keys(), matrix)
# --- Body joining (identical in both subclasses) --- def __add__(self, body_to_add: AbstractBody) -> AbstractBody: return self.join_bodies(body_to_add)
[docs] def join_bodies(*bodies, name=None) -> Multibody: # noqa: F821 from capytaine.bodies.multibodies import Multibody return Multibody(bodies, name=name)
# --- Abstract methods (different implementations) ---
[docs] @abstractmethod def integrate_pressure(self, pressure): ...
[docs] @abstractmethod def minimal_computable_wavelength(self): ...
[docs] @abstractmethod def first_irregular_frequency_estimate(self, *args, **kwargs): ...
[docs] @abstractmethod def compute_hydrostatic_stiffness(self, *, rho=1000.0, g=9.81): ...
[docs] @abstractmethod def compute_rigid_body_inertia(self, rho=1000.0): ...
@abstractmethod def _check_dofs_shape_consistency(self): ... # --- Geometric transforms ---
[docs] @abstractmethod def translated(self, shift, *, name=None) -> AbstractBody: ...
[docs] def translated_x(self, dx: float, *, name=None) -> AbstractBody: return self.translated([dx, 0.0, 0.0], name=name)
[docs] def translated_y(self, dy: float, *, name=None) -> AbstractBody: return self.translated([0.0, dy, 0.0], name=name)
[docs] def translated_z(self, dz: float, *, name=None) -> AbstractBody: return self.translated([0.0, 0.0, dz], name=name)
[docs] @abstractmethod def rotated_with_matrix(self, R, *, name=None) -> AbstractBody: ...
[docs] def rotated_x(self, angle: float, *, name=None) -> AbstractBody: c, s = np.cos(angle), np.sin(angle) R = np.array([[1, 0, 0], [0, c, -s], [0, s, c]]) return self.rotated_with_matrix(R, name=name)
[docs] def rotated_y(self, angle: float, *, name=None) -> AbstractBody: c, s = np.cos(angle), np.sin(angle) R = np.array([[c, 0, s], [0, 1, 0], [-s, 0, c]]) return self.rotated_with_matrix(R, name=name)
[docs] def rotated_z(self, angle: float, *, name=None) -> AbstractBody: c, s = np.cos(angle), np.sin(angle) R = np.array([[c, -s, 0], [s, c, 0], [0, 0, 1]]) return self.rotated_with_matrix(R, name=name)
[docs] @abstractmethod def mirrored(self, plane: Literal['xOz', 'yOz']) -> AbstractBody: ...
[docs] @abstractmethod def clipped(self, *, origin, normal, name=None) -> AbstractBody: ...
[docs] @abstractmethod def immersed_part(self, free_surface=0.0, *, sea_botton=None, water_depth=None, name=None) -> AbstractBody: ...
[docs] @abstractmethod def copy(self, name=None) -> AbstractBody: ...
# --- Display --- @abstractmethod def __str__(self): ... @abstractmethod def __short_str__(self): ... def __repr__(self): return str(self)
[docs] def show(self, *, backend=None, **kwargs): """Visualize the mesh using the specified backend. Parameters ---------- backend : str, optional Visualization backend to use. Options are 'pyvista' or 'matplotlib'. By default, try several until an installed one is found. **kwargs Additional keyword arguments passed to the visualization backend. See :mod:`~capytaine.meshes.visualization` Returns ------- object Visualization object returned by the backend (e.g., matplotlib figure). Raises ------ NotImplementedError If the specified backend is not supported. """ return show_3d(self, backend=backend, **kwargs)
[docs] def show_pyvista(self, **kwargs): """ Equivalent to show(backend="pyvista"). See also :func:`~capytaine.bodies.visualization.show_pyvista` """ return self.show(backend="pyvista", **kwargs)
[docs] def show_matplotlib(self, **kwargs): """ Equivalent to show(backend="matplotlib"). See also :func:`~capytaine.bodies.visualization.show_matplotlib` """ return self.show(backend="matplotlib", **kwargs)