""" imo_is2008.py — IMO IS Code 2008, Capítulo 2 — criterios de estabilidad intacta. Criterios A.2.1 para buques de carga general y pequeñas embarcaciones: 2.1.1 Área 0–30° ≥ 0.055 m·rad 2.1.2 Área 0–40° ≥ 0.090 m·rad 2.1.3 Área 30–40° ≥ 0.030 m·rad 2.1.4 GZ a 30° ≥ 0.200 m 2.1.5 Ángulo de GZ máximo ≥ 25° 2.1.6 GM₀ ≥ 0.150 m Referencia: IMO MSC.267(85) — IS Code 2008, Parte A, Cap. 2. """ from __future__ import annotations from dataclasses import dataclass @dataclass class IMOCriterion: """Un criterio individual del IS Code 2008.""" code: str # e.g. "A.2.1.1" description: str # Descripción corta required: float # Valor mínimo requerido achieved: float # Valor obtenido de la curva GZ unit: str # Unidades (m·rad, m, °) passed: bool # True si achieved >= required @dataclass class IMOResult: """Resultado completo de la verificación IMO IS Code 2008.""" criteria: list[IMOCriterion] overall_passed: bool def table_rows(self) -> list[tuple[str, str, str, str, bool]]: """Devuelve filas para la tabla: (code, description, required_str, achieved_str, passed). El formato de los strings varía según las unidades: - m·rad: 4 decimales - m: 3 decimales - °: 1 decimal """ rows = [] for c in self.criteria: if c.unit == "m·rad": req_str = f"{c.required:.4f} {c.unit}" ach_str = f"{c.achieved:.4f} {c.unit}" elif c.unit == "m": req_str = f"{c.required:.3f} {c.unit}" ach_str = f"{c.achieved:.3f} {c.unit}" elif c.unit == "°": req_str = f"{c.required:.1f}{c.unit}" ach_str = f"{c.achieved:.1f}{c.unit}" else: req_str = f"{c.required} {c.unit}" ach_str = f"{c.achieved} {c.unit}" rows.append((c.code, c.description, req_str, ach_str, c.passed)) return rows def check_imo_is2008(gz) -> IMOResult: """Verifica todos los criterios IS Code 2008 Cap.2 para la curva GZ dada. Parameters ---------- gz : GZCurve Curva de estabilidad calculada. Returns ------- IMOResult Contiene la lista de criterios individuales y el resultado global. """ import numpy as np from arshipdesign.stability.gz_integrator import GZCurve def _criterion( code: str, desc: str, req: float, ach: float, unit: str, ) -> IMOCriterion: return IMOCriterion( code=code, description=desc, required=req, achieved=ach, unit=unit, passed=(ach >= req), ) criteria: list[IMOCriterion] = [] # A.2.1.1 — Área bajo la curva GZ entre 0° y 30° criteria.append(_criterion( "A.2.1.1", "Área 0–30°", 0.055, float(gz.area_0_30), "m·rad", )) # A.2.1.2 — Área bajo la curva GZ entre 0° y 40° criteria.append(_criterion( "A.2.1.2", "Área 0–40°", 0.090, float(gz.area_0_40), "m·rad", )) # A.2.1.3 — Área bajo la curva GZ entre 30° y 40° criteria.append(_criterion( "A.2.1.3", "Área 30–40°", 0.030, float(gz.area_30_40), "m·rad", )) # A.2.1.4 — GZ a 30° de escora gz30 = float(np.interp(30.0, gz.angles_deg, gz.gz_values)) criteria.append(_criterion( "A.2.1.4", "GZ a 30°", 0.200, gz30, "m", )) # A.2.1.5 — Ángulo en que se produce el GZ máximo criteria.append(_criterion( "A.2.1.5", "Ángulo GZ máx", 25.0, float(gz.phi_gz_max), "°", )) # A.2.1.6 — Altura metacéntrica inicial GM₀ criteria.append(_criterion( "A.2.1.6", "GM₀", 0.150, float(gz.gm), "m", )) overall = all(c.passed for c in criteria) return IMOResult(criteria=criteria, overall_passed=overall)