Módulo 1 fixes + Módulo 2 motor hidrostático (Tasks 13–13b)
Fixes Module 1 UI: - wizard_cruiser/sailing/planing: perfiles sin^n calibrados por Cm, V-bottom con ángulo de astilla, corrección zona sobre chine planeador - viewer_3d: buffer hull pendiente para eliminar race condition 500ms - viewer_lines: reescritura completa — waterlines visibles, control points interactivos (drag DelftShip-style), señal offsets_edited - main_window: conecta offsets_edited → slot _on_offsets_edited_from_viewer que propaga cambios a todos los visores, editor, 3D y barra hidrostática Módulo 2 — motor HydrostaticCurves (Task 13): - integrator.py: integrate() (Simpson+trapz), waterplane_strips(), section_areas() - upright.py: UprightHydrostatics (19 campos), compute_upright() single-pass - curves_of_form.py: HydrostaticCurves.compute(), at_draft(), to_csv_lines(), to_dict() - tests/test_module2_hydrostatics.py: 83 tests — Wigley V&V, monotonicidad, CSV export, IACS Rec.34 §4.3–4.5; todos los 224 tests pasan Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,2 +1,227 @@
|
||||
"""Hidrostáticos vertical. Stub — Sprint 2."""
|
||||
raise NotImplementedError("upright — Sprint 2")
|
||||
"""
|
||||
UprightHydrostatics — hidrostáticos en condición vertical (quilla recta).
|
||||
|
||||
Calcula el conjunto completo de variables hidrostáticas para un calado
|
||||
dado: volumen, desplazamiento, plano de flotación, metacentros, TPC, MCT
|
||||
y los cuatro coeficientes de forma.
|
||||
|
||||
Conforme con IACS Rec.34 §4.3 — verificación analítica y §6 — trazabilidad.
|
||||
|
||||
Autor: Álvaro Romero
|
||||
Módulo 2 — AR-ShipDesign
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
|
||||
from arshipdesign.hydrostatics.integrator import (
|
||||
integrate,
|
||||
waterplane_strips,
|
||||
section_areas_and_centroids,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Dataclass de resultado
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@dataclass
|
||||
class UprightHydrostatics:
|
||||
"""Conjunto completo de hidrostáticos upright a un calado dado.
|
||||
|
||||
Todos los valores referidos a:
|
||||
- Origen longitudinal: AP (x = 0)
|
||||
- Origen vertical: quilla (z = 0)
|
||||
- Plano de crujía: eje de simetría
|
||||
|
||||
Atributos
|
||||
---------
|
||||
draft : float
|
||||
Calado T [m].
|
||||
volume : float
|
||||
Volumen de desplazamiento V [m³].
|
||||
displacement : float
|
||||
Desplazamiento Δ [t] (agua salada ρ = 1025 kg/m³ por defecto).
|
||||
awp : float
|
||||
Área del plano de flotación Awp [m²].
|
||||
lcb : float
|
||||
Centro longitudinal de carena desde AP LCB [m].
|
||||
lcf : float
|
||||
Centro longitudinal de flotación desde AP LCF [m].
|
||||
kb : float
|
||||
Altura del centro de carena sobre la quilla KB [m].
|
||||
it : float
|
||||
Segundo momento transversal del plano de flotación IT [m⁴].
|
||||
il : float
|
||||
Segundo momento longitudinal del plano de flotación IL [m⁴].
|
||||
bmt : float
|
||||
Radio metacéntrico transversal BM_T = IT / V [m].
|
||||
bml : float
|
||||
Radio metacéntrico longitudinal BM_L = IL / V [m].
|
||||
kmt : float
|
||||
Altura del metacentro transversal KM_T = KB + BM_T [m].
|
||||
kml : float
|
||||
Altura del metacentro longitudinal KM_L = KB + BM_L [m].
|
||||
tpc : float
|
||||
Toneladas por centímetro de inmersión TPC [t/cm].
|
||||
mct : float
|
||||
Momento para cambiar asiento 1 cm MCT [t·m/cm].
|
||||
cb : float
|
||||
Coeficiente de bloque Cb [-].
|
||||
cw : float
|
||||
Coeficiente de plano de flotación Cw [-].
|
||||
cm : float
|
||||
Coeficiente de cuaderna maestra Cm [-].
|
||||
cp : float
|
||||
Coeficiente prismático Cp [-].
|
||||
"""
|
||||
|
||||
draft: float
|
||||
volume: float
|
||||
displacement: float
|
||||
awp: float
|
||||
lcb: float
|
||||
lcf: float
|
||||
kb: float
|
||||
it: float
|
||||
il: float
|
||||
bmt: float
|
||||
bml: float
|
||||
kmt: float
|
||||
kml: float
|
||||
tpc: float
|
||||
mct: float
|
||||
cb: float
|
||||
cw: float
|
||||
cm: float
|
||||
cp: float
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Función de cálculo
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def compute_upright(
|
||||
hull,
|
||||
draft: float,
|
||||
rho: float = 1025.0,
|
||||
kg: float | None = None,
|
||||
) -> UprightHydrostatics:
|
||||
"""Calcula todos los hidrostáticos upright para *hull* al calado *draft*.
|
||||
|
||||
El cálculo se realiza en una sola pasada sobre las secciones, reutilizando
|
||||
los arrays intermedios para evitar redundancia.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
hull : Hull
|
||||
Casco de referencia (objeto ``arshipdesign.core.hull.Hull``).
|
||||
draft : float
|
||||
Calado de cálculo T [m]. Si T ≤ 0, retorna ceros.
|
||||
rho : float
|
||||
Densidad del agua [kg/m³]. Default 1025 (agua salada).
|
||||
kg : float | None
|
||||
Altura del centro de gravedad KG [m]. Si None se estima
|
||||
como ``hull.depth × 0.55`` (buque en rosca, conservador).
|
||||
|
||||
Returns
|
||||
-------
|
||||
UprightHydrostatics
|
||||
Todos los hidrostáticos al calado *draft*.
|
||||
"""
|
||||
T = float(draft)
|
||||
|
||||
# Caso degenerado (calado nulo o negativo)
|
||||
if T <= 1e-6:
|
||||
return _zero_hydrostatics(T)
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 1. Secciones transversales → volumen, LCB, KB
|
||||
# ----------------------------------------------------------------
|
||||
sections = hull.offsets.to_sections()
|
||||
x_s, areas, cz = section_areas_and_centroids(sections, T)
|
||||
|
||||
vol = abs(integrate(areas, x_s))
|
||||
delta = vol * rho / 1000.0
|
||||
|
||||
if vol > 1e-12:
|
||||
lcb = integrate(areas * x_s, x_s) / vol
|
||||
kb = integrate(areas * cz, x_s) / vol
|
||||
else:
|
||||
lcb = hull.lpp / 2.0
|
||||
kb = T / 2.0
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 2. Plano de flotación → Awp, LCF, IT, IL
|
||||
# ----------------------------------------------------------------
|
||||
x_wl, y_wl = waterplane_strips(hull.offsets, T)
|
||||
strip = 2.0 * y_wl # ancho total a cada x
|
||||
|
||||
awp = abs(integrate(strip, x_wl))
|
||||
|
||||
if awp > 1e-12:
|
||||
lcf = integrate(strip * x_wl, x_wl) / awp
|
||||
else:
|
||||
lcf = hull.lpp / 2.0
|
||||
|
||||
# IT = (2/3) · ∫ y³ dx (Rawson & Tupper §3.2)
|
||||
it = abs(integrate((2.0 / 3.0) * y_wl ** 3, x_wl))
|
||||
|
||||
# IL = ∫ 2y · (x − LCF)² dx
|
||||
il = abs(integrate(strip * (x_wl - lcf) ** 2, x_wl))
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 3. Radios metacéntricos
|
||||
# ----------------------------------------------------------------
|
||||
bmt = it / vol if vol > 1e-12 else 0.0
|
||||
bml = il / vol if vol > 1e-12 else 0.0
|
||||
kmt = kb + bmt
|
||||
kml = kb + bml
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 4. TPC y MCT
|
||||
# ----------------------------------------------------------------
|
||||
tpc = awp * rho / 100_000.0
|
||||
|
||||
kg_val = hull.depth * 0.55 if kg is None else float(kg)
|
||||
gml = max(kb + bml - kg_val, 0.0)
|
||||
mct = delta * gml / (100.0 * hull.lpp) if hull.lpp > 1e-12 else 0.0
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# 5. Coeficientes de forma
|
||||
# ----------------------------------------------------------------
|
||||
cb = vol / (hull.lpp * hull.beam * T) if (hull.lpp * hull.beam * T) > 1e-12 else 0.0
|
||||
cw = awp / (hull.lpp * hull.beam) if (hull.lpp * hull.beam) > 1e-12 else 0.0
|
||||
|
||||
# Área de cuaderna maestra: interpolar en x_mid
|
||||
x_mid = hull.lpp / 2.0
|
||||
am = float(np.interp(x_mid, x_s, areas))
|
||||
cm = am / (hull.beam * T) if (hull.beam * T) > 1e-12 else 0.0
|
||||
cp = vol / (am * hull.lpp) if (am * hull.lpp) > 1e-12 else 0.0
|
||||
|
||||
return UprightHydrostatics(
|
||||
draft=T, volume=vol, displacement=delta,
|
||||
awp=awp, lcb=lcb, lcf=lcf, kb=kb,
|
||||
it=it, il=il,
|
||||
bmt=bmt, bml=bml, kmt=kmt, kml=kml,
|
||||
tpc=tpc, mct=mct,
|
||||
cb=cb, cw=cw, cm=cm, cp=cp,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Auxiliar privado
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _zero_hydrostatics(draft: float) -> UprightHydrostatics:
|
||||
"""Devuelve un UprightHydrostatics con todos los valores en cero."""
|
||||
return UprightHydrostatics(
|
||||
draft=draft, volume=0.0, displacement=0.0,
|
||||
awp=0.0, lcb=0.0, lcf=0.0, kb=0.0,
|
||||
it=0.0, il=0.0,
|
||||
bmt=0.0, bml=0.0, kmt=0.0, kml=0.0,
|
||||
tpc=0.0, mct=0.0,
|
||||
cb=0.0, cw=0.0, cm=0.0, cp=0.0,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user