Exemple #1
0
def hull_residuary_resistance_mit(
        boatspeed: float,
        Vc: float,
        lwl: float,
        bwl: float,
        Tc: float,
        coe: Union[Tuple[float, float, float], None] = None,
        rho_water: float = RHO_SEA_WATER_20C) -> Force:
    """Hull upright residuary resistance [N] according to the MIT regression.

    boatspeed : boat speed [m/s]
    Vc : Canoe body volume of displaced water [m**3] , must be > 0.
    lwl : Length at Waterline [m] , must be > 0.
    bwl : Beam at Waterline [m] , must be > 0.
    Tc : Canoe body draft [m], must be >= 0
    coe : tuple of 3 floats, optional (default [lwl / 2, 0, -Tc / 3]).
        Represents the x, y, z coordinates of the centre of effort.
        For residuary resistance, the surfacic barycentre of the underwater hull
        is an acceptable estimate.
        x=lwl / 2 , y = 0., z = -Tc/3 is another acceptable estimate.
    rho_water : Water density [kg/m**3], must be >= 0.

    Returns a Force object, representing the residuary resistance [N] on the hull.

    """
    if Vc <= 0:
        raise ValueError("Vc should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between"
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")

    coe = (lwl / 2., 0., -Tc / 3.) if coe is None else coe

    interp = _get_hull_residuary_resistance_mit_interpolator(
        Vc, lwl, bwl, Tc, rho_water)

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.
    froude = froude_number(speed=boatspeed, lwl=lwl)
    if froude <= 0.6168006:
        residuary_resistance = max([0., interp(froude)])
    else:
        at_0_6068006 = interp(0.6068006)
        at_0_6168006 = interp(0.6168006)
        residuary_resistance = (at_0_6168006 + (froude - 0.6168006) *
                                (at_0_6168006 - at_0_6068006) / 0.01)

    return Force(-residuary_resistance * boatspeed_sign, 0., 0., coe[0],
                 coe[1], coe[2])
def savitsky_force(boatspeed: float,
                   m: float,
                   LCG: float,
                   VCG: float,
                   b: float,
                   epsilon: float,
                   beta: float,
                   f: float,
                   rho_water: float = RHO_SEA_WATER_20C,
                   kinematic_viscosity: float = KINEMATIC_VISCOSITY_WATER_20C,
                   gravity: float = GRAVITY_STANDARD) -> Force:
    r"""Savitsky output formatted as a Force"""
    _, _, _, _, _, R, _, _, _, _, _, _, _, _, _, Lcp, _ = \
        savitsky(boatspeed, m, LCG, VCG, b, epsilon, beta, f, rho_water, kinematic_viscosity, gravity)
    return Force(-R, 0., 0., Lcp, 0, 0)
def sideforce_by_element_fossati(boatspeed: float,
                                 heel_angle: float,
                                 leeway_angle: float,
                                 rudder_angle: float,
                                 coe: Tuple[float, float, float],
                                 projected_area: float,
                                 span: float,
                                 aspect_ratio_multiplier: float = 2.,
                                 use_cos_heel_angle: bool = True,
                                 rudder_angle_influence: float = 0.,
                                 speed_multiplier: float = 1.0,
                                 rho_water: float = RHO_SEA_WATER_20C,
                                 # for real 2D sections
                                 filename_2d_section: str = None,
                                 ncrit: float = 1.0,
                                 add_viscous_drag: bool = False) -> Force:
    """Lift (i.e. sideforce) and drag due to lift [N] of an underwater element,
    mostly due to leeway.
    Viscous effects (friction and pressure drag) are not accounted for.

    boatspeed : boat speed [m/s]
    heel_angle : The heel_angle [degrees], between -90 and 90.
    leeway_angle : The leeway angle
    rudder_angle : The rudder angle

    coe : tuple of 3 floats
        Represents the x, y, z coordinates of the centre of effort.
        The centre of effort can be estimated by projecting the projected
        surface barycentre to the 1/4 chord line.
    projected_area : Projected area [m**2] of the underwater element,
        must be >= 0
    span : Span [m] of the underwater element, must be >= 0.
    aspect_ratio_multiplier : Modifier of the geometric aspect ratio calculated
        from projected area and span to get effective aspect ratio
        Must be between 0.5 and 2.5
        Use 2. for a lifting plane with a plate effect at one end.
    use_cos_heel_angle : If True, multiply the angle of attack by the cosine of the heel angle.
        If False, use the raw angle of attack.
        Use False for the hull / canoe body; use True for keel and rudder.
    rudder_angle_influence : Fraction of the rudder angle to add to the angle of attack.
        Must be between 0 and 1.
        Normally, use 1. for a rudder, 0. otherwise.
    speed_multiplier : Multiplier of the free flow speed.
        Must be between 0.6 and 1.4.
        A current practice is, for example, to use a 0.9 speed multiplier for the rudder.
    rho_water : Water density [kg/m**3], must be >= 0

    Returns a Force object, representing the sideforce and induced drag [N] on the hull.

    References
    ----------
    Aero-Hydrodynamics and the performance of sailing yachts - Fabio Fossati - p143/144

    """
    if projected_area < 0:
        raise ValueError("projected_area should be positive or zero")
    if span <= 0:
        raise ValueError("span should be strictly positive")
    if not 0.5 <= aspect_ratio_multiplier <= 2.5:
        raise ValueError("unrealistic aspect_ratio_multiplier")
    if not 0 <= rudder_angle_influence <= 1.:
        raise ValueError("unrealistic rudder_angle_influence")
    if not 0.6 <= speed_multiplier <= 1.4:
        raise ValueError("unrealistic speed_multiplier")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between {RHO_WATER_MIN} and {RHO_WATER_MAX}")

    if filename_2d_section is None and add_viscous_drag is True:
        raise ValueError("Cannot compute viscous Cd without a 2d foil dat file")

    geometric_aspect_ratio = span**2 / projected_area
    effective_aspect_ratio = geometric_aspect_ratio * aspect_ratio_multiplier
    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.

    if use_cos_heel_angle is True:
        aoa = (leeway_angle + rudder_angle_influence * rudder_angle) * cos(radians(heel_angle))
    else:
        aoa = (leeway_angle + rudder_angle_influence * rudder_angle)

    if filename_2d_section is None:
        slope = 0.105
    else:
        raise NotImplementedError

    cl_alpha_3d = slope / (1. + (1.92 / effective_aspect_ratio))

    Cl = cl_alpha_3d * aoa

    lift = Cl * 0.5 * rho_water * projected_area * (boatspeed * speed_multiplier)**2

    # induced drag coefficient
    oswald_efficiency_factor = 0.9
    Cdi = Cl**2 / (pi * effective_aspect_ratio * oswald_efficiency_factor)

    induced_drag = 0.5 * Cdi * rho_water * projected_area * (boatspeed * speed_multiplier)**2

    if add_viscous_drag is True:
        # 2d section dat file viscous drag
        raise NotImplementedError
    else:
        # 3d induced drag only
        drag = induced_drag

    px, py, pz = shift_coe_around_x(coe, heel_angle)

    return Force(-drag * boatspeed_sign,
                 -lift * cos(radians(heel_angle)) * boatspeed_sign,
                 lift * sin(radians(heel_angle)) * boatspeed_sign,
                 px, py, pz)
def sideforce_production_keuning_verwerft(boatspeed: float,
                                          heel_angle: float,
                                          leeway_angle: float,
                                          rudder_angle: float,
                                          keel_projected_area: float,
                                          keel_span: float,
                                          rudder_projected_area: float,
                                          rudder_span: float,
                                          lwl: float,
                                          bwl: float,
                                          Tc: float,
                                          hull_coe: Optional[Tuple[float, float, float]] = None,
                                          keel_coe: Optional[Tuple[float, float, float]] = None,
                                          rudder_coe: Optional[Tuple[float, float, float]] = None,
                                          keel_sweep_back_angle: float = 0.,
                                          rudder_sweep_back_angle: float = 0.,
                                          rho_water: float = RHO_SEA_WATER_20C) -> Tuple[Force, Force, Force]:
    """Sideforce the Keuning Verwerft way.

    Calculate the lift (i.e. sideforce) and drag due to lift [N] according
    to the 2009 publication by Keuning and Verwerft.
    Viscous effects are not accounted for;
    other components are available to represent the viscous drag of the hull and its appendages.

    boatspeed : boat speed [m/s]
    heel_angle : The heel_angle [degrees], between -90 and 90.
    leeway_angle : The leeway angle [degrees]
    rudder_angle : The rudder angle [degrees]
    keel_projected_area : Projected area of the keel [m**2], must be >= 0
    keel_span : Span of the keel [m] , must be >= 0.
    rudder_projected_area : Projected area of the rudder [m**2], must be >= 0
    rudder_span : Span of the rudder [m] , must be >= 0.
    lwl : Length at Waterline [m] , must be > 0.
    bwl : Beam at Waterline [m] , must be > 0.
    Tc : Canoe body draft [m], must be >= 0
    hull_coe : tuple of 3 floats, optional (default [lwl * 3/4, 0., -Tc / 3])
        Represents the x, y, z coordinates of the centre of effort.
    keel_coe : tuple of 3 floats
               optional (default [0., 0., -(Tc + keel_span) * 0.43])
        NOT PROVIDING A COE MAKE SOLVING BEYOND 3 DOF IMPOSSIBLE.
        Represents the x, y, z coordinates of the centre of effort.
        The centre of effort can be estimated by projecting the projected
        surface barycentre to the 1/4 chord line.
    rudder_coe : tuple of 3 floats
                 optional (default [0., 0., - rudder_span * 0.43])
        NOT PROVIDING A COE MAKE SOLVING BEYOND 3 DOF IMPOSSIBLE.
        Represents the x, y, z coordinates of the centre of effort.
        The centre of effort can be estimated by projecting the
        projected surface barycentre to the 1/4 chord line.
    keel_sweep_back_angle : The sweep back angle [deg.] of the 1/4 chord line of the keel.
        Must be between -45 and 45
    rudder_sweep_back_angle : The sweep back angle [deg.] of the 1/4 chord line of the rudder.
        Must be between -45 and 45
    rho_water : Water density [kg/m**3], must be >= 0

    Returns a list of Force objects, representing the sideforce and induced drag [N]
    of the keel, rudder and hull in this order

    References
    ----------
    A new Method for the Prediction of the Side Force on Keel and Rudder
    of a Sailing Yacht based on the Results of the Delft Systematic Yacht
    Hull Series by J. A. Keuning and B. Verwerft (March 2009)
    File : CSYS19Keuning.pdf (web name)

    """
    if keel_projected_area < 0:
        raise ValueError("keel_projected_area should be positive or zero")
    if keel_span <= 0:
        raise ValueError("keel_span should be strictly positive")
    if rudder_projected_area < 0:
        raise ValueError("rudder_projected_area should be positive or zero")
    if rudder_span <= 0:
        raise ValueError("rudder_span should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if not -45. <= keel_sweep_back_angle <= 45.:
        raise ValueError("unrealistic keel_sweep_back_angle")
    if not -45. <= rudder_sweep_back_angle <= 45.:
        raise ValueError("unrealistic rudder_sweep_back_angle")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between {RHO_WATER_MIN} and {RHO_WATER_MAX}")

    keel_geometric_aspect_ratio = keel_span ** 2 / keel_projected_area
    rudder_geometric_aspect_ratio = rudder_span ** 2 / rudder_projected_area

    keel_effective_aspect_ratio = 2. * keel_geometric_aspect_ratio
    rudder_effective_aspect_ratio = 2 * rudder_geometric_aspect_ratio

    # lift_curve_slope is the lift per unit angle of attack (Whicker and Fehlner)
    keel_lift_curve_slope = lift_curve_slope(keel_effective_aspect_ratio,
                                             keel_sweep_back_angle)
    rudder_lift_curve_slope = lift_curve_slope(rudder_effective_aspect_ratio,
                                               rudder_sweep_back_angle)

    # hull influence coefficient
    c_hull = (1.80 * Tc / keel_span) + 1

    if hull_coe is None:
        # keel_coe = [0., 0., -Tc - keel_span / 2.]
        hull_coe = (lwl * 3. / 4., 0., -Tc / 3.)

    if keel_coe is None:
        # keel_coe = [0., 0., -Tc - keel_span / 2.]
        keel_coe = (0., 0., -(Tc + keel_span) * 0.43)

    if rudder_coe is None:
        # rudder_coe = [0., 0., - rudder_span / 2.]
        rudder_coe = (0., 0., - rudder_span * 0.43)

    # Signs
    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.
    leeway_angle_sign = leeway_angle / abs(leeway_angle) if leeway_angle != 0. else 0.
    heel_angle_sign = heel_angle / abs(heel_angle) if heel_angle != 0. else 0.

    # make sure no solution can be found for leeway > 15.
    leeway_angle = leeway_angle if abs(leeway_angle) < 15. else 0.

    # heel influence coefficient (c_heel = 1 @ 0° heel, c_heel = 0.4 @ 90° heel)
    c_heel = 1 - 0.382 * radians(abs(heel_angle))

    angle_of_attack_on_keel = ((leeway_angle -
                                (beta_zero(bwl / Tc, heel_angle) *
                                 heel_angle_sign)) * cos(radians(heel_angle)))

    keel_Cl = keel_lift_curve_slope * angle_of_attack_on_keel
    # print('keel : %s' % str(keel_Cl))

    # downwash angle of the keel on the rudder
    # keel_downwash_on_rudder = degrees((0.136 + 0.001 * heel_angle / 15.)
    #                           * sqrt( keel_Cl / keel_effective_aspect_ratio))
    keel_downwash = keel_downwash_on_rudder(keel_Cl,
                                            keel_effective_aspect_ratio,
                                            heel_angle)

    angle_of_attack_on_rudder = ((leeway_angle -
                                  (beta_zero(bwl / Tc, heel_angle) *
                                   heel_angle_sign)
                                  - (keel_downwash * leeway_angle_sign) +
                                  rudder_angle)
                                 * cos(radians(heel_angle)))
    rudder_Cl = rudder_lift_curve_slope * angle_of_attack_on_rudder
    # print('rudder : %s' % str(rudder_Cl))

    lift_keel = (c_hull * c_heel * 0.5 * rho_water * keel_projected_area *
                 boatspeed ** 2 * keel_Cl)
    # * leeway_angle_sign
    lift_rudder = (c_hull * c_heel * 0.5 * rho_water * rudder_projected_area *
                   (0.9 * boatspeed) ** 2 * rudder_Cl)
    # * leeway_angle_sign

    # 0.9 is an approximation of wing span efficiency
    # 15.  Perkins, C.D. and Ciale, R.E.,
    #                             "Airplane  Performance Stability and Control,"
    # JohnWiley and Sons, Inc.  (1949).
    oswald_efficiency_factor = 0.9
    induced_drag_keel = (lift_keel ** 2
                         /
                         (0.5 * rho_water * keel_projected_area *
                          boatspeed ** 2 * pi * oswald_efficiency_factor *
                          keel_effective_aspect_ratio)) if boatspeed != 0. else 0.

    induced_drag_rudder = (lift_rudder ** 2
                           /
                           (0.5 * rho_water * rudder_projected_area *
                            (0.9 * boatspeed) ** 2 * pi
                            * oswald_efficiency_factor *
                            rudder_effective_aspect_ratio)) if boatspeed != 0. else 0.

    lift_hull = (lift_keel + lift_rudder) - ((lift_keel + lift_rudder) / c_hull)
    # induced_drag = induced_drag_keel + induced_drag_rudder

    px_k, py_k, pz_k = shift_coe_around_x(keel_coe, heel_angle)

    keel_force = Force(-induced_drag_keel * boatspeed_sign,
                       -lift_keel * boatspeed_sign / c_hull,
                       lift_keel * tan(radians(heel_angle)) * boatspeed_sign,
                       px_k, py_k, pz_k)

    px_r, py_r, pz_r = shift_coe_around_x(rudder_coe, heel_angle)

    rudder_force = Force(-induced_drag_rudder * boatspeed_sign,
                         -lift_rudder * boatspeed_sign / c_hull,
                         (lift_rudder * tan(radians(heel_angle)) * boatspeed_sign),
                         px_r, py_r, pz_r)

    px_h, py_h, pz_h = shift_coe_around_x(hull_coe, heel_angle)

    hull_force = Force(0., -lift_hull * boatspeed_sign, 0., px_h, py_h, pz_h)

    return keel_force, rudder_force, hull_force
Exemple #5
0
def ballast_viscous(boatspeed: float,
                    heel_angle: float,
                    coe: Tuple[float, float, float],
                    bulb_length: float,
                    bulb_diameter: float,
                    bulb_wetted_area: float,
                    transition_reynolds_number: float = 5e5,
                    rho_water: float = RHO_SEA_WATER_20C,
                    kinematic_viscosity: float = KINEMATIC_VISCOSITY_WATER_20C,
                    turbulent_friction_line: Callable = ittc57) -> Force:
    """Calculates the viscous resistance [N] on a bulb.

    The centre of effort position is approximated to rotate
    around the X axis with heel.

    boatspeed : boat speed [m/s]
    heel_angle : The heel_angle [degrees], between -90 and 90.
    coe : tuple of 3 floats
        Represents the x, y, z coordinates of the centre of effort.
        For viscous resistance, the surfacic barycentre of the bulb
        is an acceptable estimate.
    bulb_length : length of the bulb [m] , must be > 0.
    bulb_diameter : Maximum diameter [m] of the bulb,
                   if the cross section is circular
                   Otherwise, use : sqrt (4 * Amax / pi) where Amax is the
                   maximum cross section area
                   bulb_diameter / bulb_length must be between 0 and 0.4
    bulb_wetted_area : Total Bulb Wetted Surface Area [m**2], must be >= 0
    transition_reynolds_number : The Reynolds number at which the flow
        transitions from laminar to turbulent.
        (default 500 000 (5e5)). Must be >=0.
        A value of 0 denotes a fully turbulent flow.
    rho_water : Water density [kg/m**3], must be >= 0 (default RHO_SEA_WATER)
    kinematic_viscosity : defaults to water at 20°C : 1,004*10E-6 [m**2/s]
    turbulent_friction_line : the function used to compute the turbulent Cf
        (e.g.    ittc57, hughes), used in prandtl

    Returns a Force object, representing the viscous resistance [N]
    on an appendage.

    References
    ----------
    http://www.boatdesign.net/forums/boat-design/keel-bulb-form-factors-4569.html
    http://books.google.fr/books?id=XtU4HVnWeZIC&pg=PA702#v=onepage&q&f=false

    """
    if bulb_length <= 0:
        raise ValueError("bulb_length should be strictly positive")
    if bulb_diameter <= 0:
        raise ValueError("bulb_diameter should be strictly positive")
    if bulb_diameter / bulb_length >= 0.4:
        raise ValueError(
            "The bulb_diameter / bulb_length ratio is unrealistic")
    if bulb_wetted_area <= 0:
        raise ValueError("bulb_wetted_area should be strictly positive")
    if transition_reynolds_number < 0:
        raise ValueError(
            "transition_reynolds_number should be positive or zero")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between "
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")
    if kinematic_viscosity <= 0:
        raise ValueError("kinematic viscosity should be strictly positive")
    if turbulent_friction_line not in [ittc57, hughes]:
        raise ValueError("Unknown turbulent friction line")

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.

    c_friction = prandtl(boatspeed, bulb_length, transition_reynolds_number,
                         kinematic_viscosity, turbulent_friction_line)

    form_factor = hoerner_streamlined_body(bulb_diameter / bulb_length)

    viscous_resistance = form_factor * 0.5 * rho_water * boatspeed**2 * bulb_wetted_area * c_friction
    px, py, pz = shift_coe_around_x(coe, heel_angle)

    return Force(-viscous_resistance * boatspeed_sign, 0., 0., px, py, pz)
Exemple #6
0
def appendage_viscous(
        boatspeed: float,
        heel_angle: float,
        coe: Tuple[float, float, float],
        average_chord: float,
        thickness_to_chord: float,
        wetted_area: float,
        transition_reynolds_number: float = 5e5,
        rho_water: float = RHO_SEA_WATER_20C,
        kinematic_viscosity: float = KINEMATIC_VISCOSITY_WATER_20C,
        turbulent_friction_line: Callable = ittc57) -> Force:
    """Calculate the viscous resistance [N] on an appendage.

    (For example, on a keel fin, a rudder, a centreboard ....)
    The centre of effort position is approximated to rotate
    around the X axis with heel.

    boatspeed : boat speed [m/s]
    heel_angle : The heel_angle [degrees], between -90 and 90.
    coe :  tuple of 3 floats
        Represents the x, y, z coordinates of the centre of effort.
        For viscous resistance, the surfacic barycentre of the appendage
        is an acceptable estimate.
    average_chord : Average chord of the appendage [m] , must be > 0.
    thickness_to_chord : Thickness to chord ratio of keel
        (e.g.0.1 for NACA0010). Must be between 0 and 0.4
    wetted_area : WSA - Wetted Surface Area of the appendage [m**2],
                  must be >= 0
    transition_reynolds_number : The Reynolds number at which the flow
        transitions from laminar to turbulent.
        A value of 0 denotes a fully turbulent flow.
        Optional (default 500 000 (5e5)). Must be >=0.
    rho_water : Water density [kg/m**3], must be >= 0 (default RHO_SEA_WATER)
    kinematic_viscosity : defaults to water at 20°C : 1,004*10E-6 [m**2/s]
    turbulent_friction_line : the function used to compute the turbulent Cf
        (e.g. ittc57, hughes), used in prandtl

    Returns a Force object, representing the viscous resistance [N]
    on an appendage.

    References
    ----------
    www.boatdesign.net/forums/attachments/hydrodynamics-aerodynamics/
    79702d1363296908-keel-rudder-viscous-drag-correlation-line.pdf

    """
    if average_chord <= 0:
        raise ValueError("average_chord should be strictly positive")
    if not 0. <= thickness_to_chord < 0.4:
        raise ValueError("unrealistic thickness_to_chord")
    if wetted_area <= 0:
        raise ValueError("wetted_area should be strictly positive")
    if transition_reynolds_number < 0:
        raise ValueError(
            "transition_reynolds_number should be positive or zero")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between "
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")
    if kinematic_viscosity <= 0:
        raise ValueError("kinematic viscosity should be strictly positive")
    if turbulent_friction_line not in [ittc57, hughes]:
        raise ValueError("Unknown turbulent friction line")

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.

    cf = prandtl(boatspeed, average_chord, transition_reynolds_number,
                 kinematic_viscosity, turbulent_friction_line)

    viscous_resistance = hoerner(
        thickness_to_chord) * 0.5 * rho_water * boatspeed**2 * wetted_area * cf
    px, py, pz = shift_coe_around_x(coe, heel_angle)

    return Force(-viscous_resistance * boatspeed_sign, 0., 0., px, py, pz)
def trm_estimate(trim_angle: float,
                 displacement: float,
                 lwl: float,
                 bwl: float,
                 Tc: float,
                 Gdwl: float,
                 rho_water: float = RHO_SEA_WATER_20C) -> Tuple[Force, Force]:
    """Trim righting moment estimate.

    Estimate the righting moment [N.m] from hull shape parameters
    according to the logic published in 1993 by Gerritsma

    trim_angle : trim angle [degrees].
        Negative is bow down, Positive is bow up
    displacement : The total yacht displacement [kg], must be > 0.
    lwl : The length at waterline [m], must be > 0.
    bwl : The beam at waterline [m], must be > 0.
    Tc : The canoe body draft [m], must be > 0
    Gdwl : The vertical centre of gravity position relative to datum waterline
        (negative if below waterline)
    rho_water : Water density [kg/m**3],
                must be between RHO_WATER_MIN and RHO_WATER_MAX.

    Returns a list of 2 Force objects, of equal magnitude but of vertical
    and opposite directions representing the righting moment.

    References
    ----------
    http://www.boatdesign.net/forums/attachments/sailboats/
        51748d1293586862-dr-gerritsma-righting-moment-dr.-gerritsmas-righting-moment.jpg
    http://www.boatdesign.net/forums/sailboats/ dr-gerritsma-righting-moment-36057.html

    """
    if displacement <= 0:
        raise ValueError("displacement should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between "
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")

    D3 = 0.0636 - 0.0196 * (lwl / Tc)

    trim_sign = trim_angle / abs(trim_angle) if trim_angle != 0. else 0.

    # Righting moment at 0 speed (rest)
    static_trm_estimate = (displacement * GRAVITY_STANDARD *
                           ((0.664 * Tc + 0.111 * lwl**2 / Tc - Gdwl) *
                            sin(radians(abs(trim_angle))) + bwl * D3 *
                            (radians(abs(trim_angle)))**2))

    trm_estimate_ = static_trm_estimate

    trim_righting_force = Force(0., 0., trm_estimate_ * trim_sign,
                                lwl / 2. - 1., 0., 0.)
    compensating_force = Force(0., 0., -trm_estimate_ * trim_sign, lwl / 2., 0,
                               0.)

    return trim_righting_force, compensating_force
Exemple #8
0
def hull_viscous(boatspeed: float,
                 lwl: float,
                 Sc: float,
                 coe: Optional[Tuple[float, float, float]] = None,
                 lwl_multiplier: float = 0.7,
                 transition_reynolds_number: float = 5e5,
                 form_factor: float = 1.12,
                 rho_water: float = RHO_SEA_WATER_20C,
                 kinematic_viscosity: float = KINEMATIC_VISCOSITY_WATER_20C,
                 turbulent_friction_line: Callable = ittc57) -> Force:
    """Calculate the friction resistance [N] on the hull.

    boatspeed : boat speed [m/s]
    lwl : Length at Waterline [m] , must be > 0.
    Sc : WSAc - Canoe body Wetted Surface Area [m**2], must be >= 0
    coe : tuple of 3 floats, optional (default [lwl / 2, 0, -Tc/3]).
        Represents the x, y, z coordinates of the centre of effort.
        For viscous resistance, the surfacic barycentre of the underwater
            hull is an acceptable estimate.
        x=lwl / 2 , y = 0., z = -Tc/3 is another acceptable estimate.
    lwl_multiplier : Multiplier of the waterline length
        (used for Reynolds number calculation).
        The ITTC 57 practice is to use 0.7. Must be between 0.7 and 1.0.
    transition_reynolds_number : The Reynolds number at which the flow
        transitions from laminar to turbulent.
        Must be >=0. A value of 0 denotes a fully turbulent flow.
    form_factor : form factor to account for pressure drag
        (default 1.12 (embedded ITTC57 form factor))
        Must be between 0.9 and 1.6
    rho_water : Water density [kg/m**3], must be >= 0.
    kinematic_viscosity : defaults to water at 20°C : 1,004*10E-6 [m**2/s]
    turbulent_friction_line : the function used to compute the turbulent Cf
        (e.g. ittc57, hughes), used in prandtl

    Returns a Force object, representing the friction resistance [N] on the hull.

    References
    ----------
    http://radiosailingtechnology.com/index.php/hulls/drag-measurements-on-an-international-one-metre-yacht

    """
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if Sc <= 0:
        raise ValueError("Sc should be strictly positive")
    if Sc >= lwl * lwl / 2:
        raise ValueError("Sc is unrealistic")
    if not 0.7 <= lwl_multiplier <= 1:
        raise ValueError("unrealistic lwl_multiplier")
    if transition_reynolds_number < 0:
        raise ValueError(
            "transition_reynolds_number should be positive or zero")
    if not 0.9 <= form_factor <= 1.6:
        raise ValueError("unrealistic form_factor")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(
            f"rho_water should be between {RHO_WATER_MIN} and {RHO_WATER_MAX}")
    if kinematic_viscosity <= 0:
        raise ValueError("kinematic viscosity should be strictly positive")
    if turbulent_friction_line not in [ittc57, hughes]:
        raise ValueError("Unknown turbulent friction line")

    if coe is None:
        coe = (lwl / 2., 0., 0.)

    boatspeed_sign = 1. if boatspeed == 0. else boatspeed / abs(boatspeed)

    # TODO: where does the division of the form factor by 1.12 come from?
    Cf = (form_factor / 1.12) * prandtl(
        boatspeed, lwl_multiplier * lwl, transition_reynolds_number,
        kinematic_viscosity, turbulent_friction_line)

    friction_resistance = 0.5 * rho_water * boatspeed**2 * Sc * Cf

    return Force(-friction_resistance * boatspeed_sign, 0., 0., coe[0], coe[1],
                 coe[2])
def rm_orc2013(heel_angle: float,
               displacement: float,
               lwl: float,
               bwl: float,
               Tc: float,
               rho_water: float = RHO_SEA_WATER_20C) -> Tuple[Force, Force]:
    """Heeling righting moment estimate, ORC.

    Righting moment estimate, inspired by the formulation at page 30 of
    the 'ORC VPP Documentation 2013, updated july 4th'

    DSPM    Displacement in measurement trim [kg]
    IMSL    Effective sailing length [m] weighted average of lengths
            for 3 conditions of flotation
    BTR     Beam Depth Ratio [] - effective beam divided by
            the effective hull depth
    VOL     Canoe body volume [m**3]
    SA      Sail area upwind [m**2]
    HA      heeling arm [N*m], defined as :
            (CEH main*AREA main + CEH jib*AREA jib) / SA + HBI + DHKA*0.45
    HBI     Height [m] of base of I
    DHKA    Draft [m] of keel and hull adjusted
    B       Effective beam [m]
            @see www.orc.org/rules/ORC%20Rating%20Rules%202010.pdf (page 6)

    """
    if displacement <= 0:
        raise ValueError("displacement should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(
            f"rho_water should be between {RHO_WATER_MIN} and {RHO_WATER_MAX}")

    a0 = -0.00410481
    a1 = -0.00003999
    a2 = -0.00017008
    a3 = 0.000019183
    a4 = 0.003602739

    heel_sign = heel_angle / abs(heel_angle) if heel_angle != 0. else 0.

    DSPM = displacement
    IMSL = lwl
    BTR = bwl / Tc
    VOL = displacement / rho_water
    B = bwl
    SA = 0
    HA = 0.7
    rm_default = 1.025 * DSPM * IMSL * (a0 + a1 * BTR + a2 *
                                        (VOL**(1. / 3.) / IMSL) + a3 * SA *
                                        HA / B**3 + a4 * B / VOL**(1. / 3.))

    righting_force = Force(0., 0., -rm_default * heel_sign, lwl / 2., -1., 0.)
    compensating_force = Force(0., 0., rm_default * heel_sign, lwl / 2., 0, 0.)

    return righting_force, compensating_force
def rm_estimate_gerritsma(
        boatspeed: float,
        heel_angle: float,
        displacement: float,
        lwl: float,
        bwl: float,
        Tc: float,
        Gdwl: float,
        rho_water: float = RHO_SEA_WATER_20C) -> Tuple[Force, Force]:
    """Heeling righting moment estimate, Delft.

    Estimate the righting moment [N.m] from hull shape parameters
    according to the logic published in 1993 by Gerritsma

    boatspeed : boat speed [m/s]
    heel_angle : The heel_angle [degrees], between -90 and 90.
        A negative value represents heel to windward
    displacement : The total yacht displacement [kg], must be > 0.
    lwl : The length at waterline [m], must be > 0.
    bwl : The beam at waterline [m], must be > 0.
    Tc : The canoe body draft [m], must be > 0
    Gdwl : The vertical centre of gravity position relative to datum waterline
        (negative if below waterline)
    rho_water : Water density [kg/m**3],
                must be between RHO_WATER_MIN and RHO_WATER_MAX.

    Returns a list of 2 Force objects, of equal magnitude but of vertical
    and opposite directions representing the righting moment.

    References
    ----------
    http://www.boatdesign.net/forums/attachments/sailboats/
    51748d1293586862-dr-gerritsma-righting-moment-dr.-
                                                  gerritsmas-righting-moment.jpg

    http://www.boatdesign.net/forums/sailboats/
                                         dr-gerritsma-righting-moment-36057.html

    """
    if displacement <= 0:
        raise ValueError("displacement should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between "
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")

    D2 = -0.0406 + 0.0109 * (bwl / Tc) - 0.00105 * (bwl / Tc)**2
    D3 = 0.0636 - 0.0196 * (bwl / Tc)

    heel_sign = heel_angle / abs(heel_angle) if heel_angle != 0. else 0.

    froude = froude_number(speed=boatspeed, lwl=lwl)

    # Righting moment at 0 speed (rest)
    static_rm_estimate = (displacement * GRAVITY_STANDARD *
                          ((0.664 * Tc + 0.111 * bwl**2 / Tc - Gdwl) *
                           sin(radians(abs(heel_angle))) + lwl * D3 *
                           (radians(abs(heel_angle)))**2))

    # Righting moment modification due to forward motion
    dynamic_rm_estimate = (displacement * GRAVITY_STANDARD *
                           (lwl * (D2 * (radians(abs(heel_angle))) * froude)))

    rm_estimate = static_rm_estimate + dynamic_rm_estimate
    # return ForceAndMoment([0.,0.,0.],
    #                       [-rm_estimate * heel_sign,0.,0.],[0.,0.,0.])

    # Approximation of the point of application of the righting force:
    # X: 1/2 of lwl
    # Y: - UNIT LENGTH (gives the right moment when multiplied
    #                   by the Z component of the force)
    # Z: 0

    righting_force = Force(0., 0., -rm_estimate * heel_sign, lwl / 2., -1., 0.)
    compensating_force = Force(
        0.,
        0.,
        rm_estimate * heel_sign,
        lwl / 2.,
        0,
        0.,
    )

    return righting_force, compensating_force
Exemple #11
0
def hull_residuary_resistance_la_series3(
        boatspeed: float,
        Vc: float,
        lwl: float,
        bwl: float,
        Tc: float,
        Aw: float,
        lcb_fraction: float = 0.53,
        Cp: float = 0.56,
        coe: Union[Tuple[float, float, float], None] = None,
        rho_water: float = RHO_SEA_WATER_20C) -> Force:
    """Hull upright residuary resistance (N), DSYHS Series 3 regression.

    Calculates the residuary resistance [N] on the hull.
    LA stands for Larsson, one of the authors of Principles of Yacht Design.
    The calculation is described on pages 75 and 76 of the book
    Principles of Yacht Design

    boatspeed : boat speed [m/s]
    Vc : Canoe body volume of displaced water [m**3] , must be > 0.
    lwl : Length at Waterline [m] , must be > 0.
    bwl : Beam at Waterline [m] , must be > 0.
    Tc : Canoe body draft [m], must be >= 0
    Aw : Waterplane area [m**2] , must be > 0.
    lcb_fraction : Longitudinal position of Center of Buoyancy from bow.
        It is in fraction format: e.g. 0.560. Must be > 0 and < 1.
        The DSYHS range is 0.500 to 0.582.
    Cp : Prismatic Coefficient. Must be > 0 and < 1.
    The DSYHS range is 0.520 to 0.600.
    coe : tuple of 3 floats, optional (default [lwl / 2, 0, -Tc / 3]).
        Represents the x, y, z coordinates of the centre of effort.
        For residuary resistance, the surfacic barycentre of the underwater
        hull is an acceptable estimate.
        x=lwl / 2 , y = 0., z = -Tc/3 is another acceptable estimate.
    rho_water : Water density [kg/m**3], must be >= 0.

    Returns a Force object, representing the residuary resistance [N]
    on the hull.

    References
    ----------
    Principles of Yacht Design - Larsson & Eliasson
    http://radiosailingtechnology.com/index.php/hulls/
                           drag-measurements-on-an-international-one-metre-yacht
    http://klz-resistance.blogspot.fr/

    """
    if Vc <= 0:
        raise ValueError("Vc should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if Aw <= 0:
        raise ValueError("Aw should be strictly positive")
    if not 0. < lcb_fraction < 1.:
        raise ValueError("lcb_fraction should be between 0 and 1")
    if not 0. < Cp < 1.:
        raise ValueError("Cp should be between 0 and 1")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between "
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")

    # DSYHS checks (cf page 74 of principles of yacht design
    check_lwl_to_bwl_s3(value=lwl / bwl)
    check_bwl_to_draft_s3(value=bwl / Tc)
    check_lwl_to_volume_s3(value=lwl / Vc**(1. / 3.))
    check_lcb_fraction_s3(value=lcb_fraction)
    check_prismatic_s3(value=Cp)

    coe = (lwl / 2., 0., -Tc / 3.) if coe is None else coe

    interp = _get_hull_residuary_resistance_la_series3_interpolator(
        Vc, lwl, bwl, Tc, Aw, lcb_fraction, Cp, rho_water)

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.
    froude = froude_number(speed=boatspeed, lwl=lwl)
    if froude <= 0.75:
        residuary_resistance = max([0., interp(froude)])
    else:
        at_0_74 = interp(0.74)
        at_0_75 = interp(0.75)
        residuary_resistance = (at_0_75 + (froude - 0.75) *
                                (at_0_75 - at_0_74) / 0.01)

    return Force(-residuary_resistance * boatspeed_sign, 0., 0., coe[0],
                 coe[1], coe[2])
Exemple #12
0
def keel_residuary_delta_heeled_ks(
        boatspeed: float,
        heel_angle: float,
        Vc: float,
        Vk: float,
        Tc: float,
        T: float,
        lwl: float,
        bwl: float,
        coe: Union[Tuple[float, float, float], None] = None,
        rho_water: float = RHO_SEA_WATER_20C) -> Force:
    """Keel residuary resistance [N] delta at a given heel angle.

    KS stands for Keuning Sonnenberg.
    Keuning and Sonnenberg wrote "Approximation of the Hydrodynamic Forces
    on a Sailing Yacht based on the 'Delft Systematic Yacht Hull Series'" in 1998.
    keel_residuary_resistance_delta_heeled_KS uses the results from this publication.

    boatspeed : boat speed [m/s]
    heel_angle : The heel_angle [degrees], between -90 and 90.
    Vc : Canoe body volume of displaced water [m**3] , must be > 0.
    Vk : Keel volume of displaced water [m**3] , must be > 0.
    Tc : Canoe body draft [m], must be >= 0
    T : Total draft [m], must be >= 0
    lwl : Length at Waterline [m] , must be > 0.
    bwl : Beam at Waterline [m] , must be > 0.
    coe : tuple of 3 floats, optional (default [lwl / 2, 0, -Tc/3]).
        Represents the x, y, z coordinates of the centre of effort.
        For residuary resistance, the surfacic barycentre of the underwater
        hull is an acceptable estimate.
        x=lwl / 2 , y = 0., z = -Tc/3 is another acceptable estimate.
    rho_water :  Water density [kg/m**3], must be >= 0 (default RHO_SEA_WATER)

    Returns a Force object, representing the residuary resistance [N] delta of
    the keel at any heel angle.

    """
    if Vc <= 0:
        raise ValueError("Vc should be strictly positive")
    if Vk <= 0:
        raise ValueError("Vk should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if T <= 0:
        raise ValueError("T should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be "
                         f"between {RHO_WATER_MIN} and {RHO_WATER_MAX}")

    # DSYHS checks
    # midship area coefficient cannot be checked here
    check_lwl_to_bwl(value=lwl / bwl)
    check_bwl_to_draft(value=bwl / Tc)
    check_lwl_to_volume(value=lwl / Vc**(1. / 3.))

    if coe is None:
        coe = (lwl / 2., 0., -Tc / 3.)

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.

    froude = froude_number(speed=boatspeed, lwl=lwl)

    rrk_delta_at_heel_angle = (Vk * rho_water * GRAVITY_STANDARD) * \
                              (-3.5837 * Tc / T +
                               -0.0518 * bwl / Tc +
                               0.5958 * bwl / T +
                               0.2055 * lwl / Vc ** (1. / 3.)) \
                              * froude ** 2 * radians(abs(heel_angle))

    return Force(-rrk_delta_at_heel_angle * boatspeed_sign, 0., 0., coe[0],
                 coe[1], coe[2])
Exemple #13
0
def hull_residuary_resistance_ks_series4(
        boatspeed: float,
        Vc: float,
        lwl: float,
        bwl: float,
        Aw: float,
        Sc: float,
        lcb_fraction: float = 0.53,
        lcf_fraction: float = 0.56,
        Cp: float = 0.56,
        coe: Union[Tuple[float, float, float], None] = None,
        rho_water: float = RHO_SEA_WATER_20C) -> Force:
    """Residuary resistance [N] on the hull, DSYHS Series 4 regression.

    KS stands for Keuning Sonnenberg.
    Keuning and Sonnenberg wrote "Approximation of the Hydrodynamic Forces
    on a Sailing Yacht based on the 'Delft Systematic Yacht Hull Series'"
    in 1998. hull_residuary_resistance_KS uses the results from this publication

    boatspeed : boat speed [m/s]
    Vc : Canoe body volume of displaced water [m**3], must be > 0.
    lwl : Length at Waterline [m], must be > 0.
    bwl : Beam at Waterline [m] , must be > 0.
    Aw : Waterplane area [m**2] , must be > 0.
    Sc : WSAc - Canoe body Wetted Surface Area [m**2], must be >= 0
    lcb_fraction : Longitudinal position of Center of Buoyancy from bow.
        It is in fraction format e.g. 0.560. Must be > 0 and < 1.
        The DSYHS range is 0.500 to 0.582.
    lcf_fraction : Longitudinal position of Center of Flotation from bow.
        It is in fraction format e.g. 0.590. Must be > 0 and < 1.
        The DSYHS range is 0.518 to 0.595.
    Cp : Prismatic Coefficient, Must be > 0 and < 1.
    The DSYHS range is 0.520 to 0.600.
    coe : tuple of 3 floats, optional (default [lwl / 2, 0, 0])
        Represents the x, y, z coordinates of the centre of effort.
        For residuary resistance, the surfacic barycentre of the underwater hull
        is an acceptable estimate.
        x=lwl / 2 , y = 0., z = -Tc/3 is another acceptable estimate.
    rho_water : Water density [kg/m**3], must be >= 0.

    References
    ----------
    Approximation of the Hydrodynamic Forces on a Sailing Yacht
    based on the 'Delft Systematic Yacht Hull Series (by Keuning & Sonnenberg)

    http://radiosailingtechnology.com/index.php/hulls/
                           drag-measurements-on-an-international-one-metre-yacht

    http://klz-resistance.blogspot.fr/

    """
    if Vc <= 0:
        raise ValueError("Vc should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Aw <= 0:
        raise ValueError("Aw should be strictly positive")
    if Sc <= 0:
        raise ValueError("Sc should be strictly positive")
    if not 0. < lcb_fraction < 1.:
        raise ValueError("lcb_fraction should be between 0 and 1")
    if not 0. < lcf_fraction < 1.:
        raise ValueError("lcf_fraction should be between 0 and 1")
    if not 0. < Cp < 1.:
        raise ValueError("Cp should be between 0 and 1")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between "
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")

    # DSYHS checks
    # bwl / Tc cannot be checked here
    # midship area coefficient cannot be checked here
    check_lwl_to_bwl_s4(value=lwl / bwl)
    check_lwl_to_volume_s4(value=lwl / Vc**(1. / 3.))
    check_lcb_fraction_s4(value=lcb_fraction)
    check_lcf_fraction_s4(value=lcf_fraction)
    check_prismatic_s4(value=Cp)
    check_waterplane_to_volume_s4(value=Aw / Vc**(2. / 3.))

    coe = (lwl / 2., 0., 0.) if coe is None else coe

    interp = _get_hull_residuary_resistance_ks_series4_interpolator(
        Vc, lwl, bwl, Aw, Sc, lcb_fraction, lcf_fraction, Cp, rho_water)

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.

    froude = froude_number(speed=boatspeed, lwl=lwl)
    if froude <= 0.6:
        residuary_resistance = max([0., interp(froude)])
    else:
        # Rough extrapolation using the derivative at the end of the domain
        at_0_59 = interp(0.59)
        at_0_60 = interp(0.60)
        residuary_resistance = at_0_60 + (froude - 0.6) * (at_0_60 -
                                                           at_0_59) / 0.01

    return Force(-residuary_resistance * boatspeed_sign, 0., 0., coe[0],
                 coe[1], coe[2])
Exemple #14
0
def hull_residuary_resistance_2008(
        boatspeed: float,
        Vc: float,
        lwl: float,
        bwl: float,
        Tc: float,
        Aw: float,
        Cm: float,
        lcb_fraction: float = 0.53,
        lcf_fraction: float = 0.56,
        Cp: float = 0.56,
        coe: Union[Tuple[float, float, float], None] = None,
        rho_water: float = RHO_SEA_WATER_20C) -> Force:
    """Calculate the residuary resistance [N] on the hull.

    Source: p24 of Fossati's Aero-hydrodynamics ...

    boatspeed : boat speed [m/s]
    Vc : Canoe body volume of displaced water [m**3], must be > 0.
    lwl : Length at Waterline [m], must be > 0.
    bwl : Beam at Waterline [m] , must be > 0.
    Aw : Waterplane area [m**2] , must be > 0.
    Cm : Midship section coefficient, must be >= 0
    lcb_fraction : Longitudinal position of Center of Buoyancy from bow.
        It is in fraction format e.g. 0.560. Must be > 0 and < 1.
        The DSYHS range is 0.500 to 0.582.
    lcf_fraction : Longitudinal position of Center of Flotation from bow.
        It is in fraction format e.g. 0.590. Must be > 0 and < 1.
        The DSYHS range is 0.518 to 0.595.
    Cp : Prismatic Coefficient, Must be > 0 and < 1.
         The DSYHS range is 0.520 to 0.600.
    coe : tuple of 3 floats, optional (default [lwl / 2, 0, 0])
        Represents the x, y, z coordinates of the centre of effort.
        For residuary resistance, the surfacic barycentre of the underwater hull
        is an acceptable estimate.
        x=lwl / 2 , y = 0., z = -Tc/3 is another acceptable estimate.
    rho_water : Water density [kg/m**3], must be >= 0.

    References
    ----------
    Aero-hydrodynamics and the performance of sailing yachts. p24

    """
    if Vc <= 0:
        raise ValueError("Vc should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Aw <= 0:
        raise ValueError("Aw should be strictly positive")
    if Cm <= 0:
        raise ValueError("Cm should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if not 0. < lcb_fraction < 1.:
        raise ValueError("lcb_fraction should be between 0 and 1")
    if not 0. < lcf_fraction < 1.:
        raise ValueError("lcf_fraction should be between 0 and 1")
    if not 0. < Cp < 1.:
        raise ValueError("Cp should be between 0 and 1")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between "
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")

    # DSYHS checks
    # bwl / Tc cannot be checked here
    # midship area coefficient cannot be checked here
    check_lwl_to_bwl_2008(value=lwl / bwl)
    check_lwl_to_volume_2008(value=lwl / Vc**(1. / 3.))
    check_lcb_fraction_2008(value=lcb_fraction)
    check_lcf_fraction_2008(value=lcf_fraction)
    check_prismatic_2008(value=Cp)
    check_midship_2008(value=Cm)

    coe = (lwl / 2., 0., 0.) if coe is None else coe

    interp = _get_hull_residuary_resistance_2008_interpolator(
        Vc, lwl, bwl, Tc, Aw, Cm, lcb_fraction, lcf_fraction, Cp, rho_water)

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.

    froude = froude_number(speed=boatspeed, lwl=lwl)

    if froude <= 0.75:
        residuary_resistance = max([0., interp(froude)])
    else:
        # Rough extrapolation using the derivative at the end of the domain
        at_0_74 = interp(0.74)
        at_0_75 = interp(0.75)
        residuary_resistance = at_0_75 + (froude - 0.75) * (at_0_75 -
                                                            at_0_74) / 0.01

    return Force(-residuary_resistance * boatspeed_sign, 0., 0., coe[0],
                 coe[1], coe[2])
Exemple #15
0
def hull_residuary_delta_heeled_ks(
        boatspeed: float,
        heel_angle: float,
        Vc: float,
        lwl: float,
        bwl: float,
        Tc: float,
        lcb_fraction: float = 0.53,
        coe: Union[Tuple[float, float, float], None] = None,
        rho_water: float = RHO_SEA_WATER_20C) -> Force:
    """Hull residuary resistance [N] delta at a given heel angle.

    KS stands for Keuning Sonnenberg.
    Keuning and Sonnenberg wrote "Approximation of the Hydrodynamic Forces
    on a Sailing Yacht based on the 'Delft Systematic Yacht Hull Series'"
    in 1998.
    hull_residuary_resistance_delta_heeled_KS uses the results from this publication.

    boatspeed : boat speed [m/s]
    heel_angle : The heel_angle [degrees], between -90 and 90.
    Vc : Canoe body volume of displaced water [m**3] , must be > 0.
    lwl : Length at Waterline [m] , must be > 0.
    bwl : Beam at Waterline [m] , must be > 0.
    Tc : Canoe body draft [m], must be >= 0
    lcb_fraction : Longitudinal position of Center of Buoyancy from bow.
        It is in fraction format: e.g. 0.560. Must be > 0 and < 1.
        The DSYHS range is 0.500 to 0.582.
    coe : tuple of 3 floats, optional (default [lwl / 2, 0, -Tc / 3])
        Represents the x, y, z coordinates of the centre of effort.
        For residuary resistance, the surfacic barycentre of the underwater hull is an acceptable estimate.
        x=lwl / 2 , y = 0., z = -Tc/3 is another acceptable estimate.
    rho_water : float, optional (default RHO_SEA_WATER)
        Water density [kg/m**3], must be >= 0.

    """
    if Vc <= 0:
        raise ValueError("Vc should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if not 0. < lcb_fraction < 1.:
        raise ValueError("lcb_fraction should be between 0 and 1")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between "
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")

    # DSYHS checks
    # midship area coefficient cannot be checked here
    check_lwl_to_bwl(value=lwl / bwl)
    check_bwl_to_draft(value=bwl / Tc)
    check_lwl_to_volume(value=lwl / Vc**(1. / 3.))
    check_lcb_fraction(value=lcb_fraction)

    coe = (lwl / 2., 0., -Tc / 3.) if coe is None else coe

    interp = _get_hull_residuary_delta_heeled_ks_interpolator(
        Vc, lwl, bwl, Tc, lcb_fraction, rho_water)

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.

    froude = froude_number(speed=boatspeed, lwl=lwl)
    if froude <= 0.55:
        rrh_delta_at_20_deg_heel = max([0., interp(froude)])
        rrh_delta_at_heel_angle = (rrh_delta_at_20_deg_heel * 6.0 *
                                   (radians(abs(heel_angle)))**1.7)
    else:
        at_0_54 = interp(0.54)
        at_0_55 = interp(0.55)
        rrh_delta_at_20_deg_heel = (at_0_55 + (froude - 0.55) *
                                    (at_0_55 - at_0_54) / 0.01)
        rrh_delta_at_heel_angle = (rrh_delta_at_20_deg_heel * 6.0 *
                                   (radians(abs(heel_angle)))**1.7)

    return Force(-rrh_delta_at_heel_angle * boatspeed_sign, 0., 0., coe[0],
                 coe[1], coe[2])
Exemple #16
0
def due_to_heel(boatspeed: float,
                heel_angle: float,
                lwl: float,
                bwl: float,
                Sc: float,
                Tc: float,
                T: float,
                coe: Union[Tuple[float, float, float], None] = None,
                rho_water: float = RHO_SEA_WATER_20C) -> Force:
    """Drag [N] due to heel.

    LA stands for Larsson, one of the authors of Principles of Yacht Design.
    The calculation is described on page 83 of the book Principles of Yacht Design

    boatspeed : boat speed [m/s]
    heel_angle : The heel_angle [degrees], between -90 and 90.
    lwl : Length at Waterline [m] , must be > 0.
    bwl : Beam at Waterline [m] , must be > 0.
    Sc : WSAc - Canoe body Wetted Surface Area [m**2], must be >= 0
    Tc : Canoe body draft [m], must be >= 0
    T : Total draft [m], must be >= 0
    coe : tuple of 3 floats, optional (default [lwl / 2, 0, -Tc/3]).
        Represents the x, y, z coordinates of the centre of effort.
        For residuary resistance, the surfacic barycentre of the underwater hull
        is an acceptable estimate.
        x=lwl / 2 , y = 0., z = -Tc/3 is another acceptable estimate.
    rho_water : Water density [kg/m**3], must be >= 0

    Returns a Force object, representing the drag [N] due to heel.

    References
    ----------
    http://klz-resistance.blogspot.fr/

    """
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if Sc <= 0:
        raise ValueError("Sc should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if T <= 0:
        raise ValueError("T should be strictly positive")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(f"rho_water should be between "
                         f"{RHO_WATER_MIN} and {RHO_WATER_MAX}")

    if coe is None:
        coe = (lwl / 2., 0., -Tc / 3.)

    Ch = ((6.747 * (Tc / T)) + (2.517 * (bwl / Tc)) + (3.710 * (bwl / Tc) *
                                                       (Tc / T))) * 0.001

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.
    froude = abs(boatspeed / sqrt(GRAVITY_STANDARD * lwl))

    heel_resistance = (0.5 * rho_water * boatspeed**2 * Sc * Ch * froude**2 *
                       radians(abs(heel_angle)))

    return Force(-heel_resistance * boatspeed_sign, 0., 0., coe[0], coe[1],
                 coe[2])
Exemple #17
0
def keel_residuary_ks(boatspeed: float,
                      Vc: float,
                      Vk: float,
                      Tc: float,
                      T: float,
                      lwl: float,
                      bwl: float,
                      Zcbk: float,
                      coe: Union[Tuple[float, float, float], None] = None,
                      rho_water: float = RHO_SEA_WATER_20C) -> Force:
    """Residuary resistance [N] of the keel at 0° heel.

    KS stands for Keuning Sonnenberg.
    Keuning and Sonnenberg wrote "Approximation of the Hydrodynamic Forces
    on a Sailing Yacht based on the 'Delft Systematic Yacht Hull Series'"
    in 1998.
    keel_residuary_resistance_KS uses the results from this publication.

    boatspeed : boat speed [m/s]
    Vc : Canoe body volume of displaced water [m**3] , must be > 0.
    Vk : Keel volume of displaced water [m**3] , must be > 0.
    Tc : Canoe body draft [m], must be >= 0
    T : Total draft [m], must be >= 0
    lwl : Length at Waterline [m] , must be > 0.
    bwl : Beam at Waterline [m] , must be > 0.
    Zcbk : Distance to the center of buoyancy of the keel [m]n from top of keel.
           Must be > 0.
    coe : tuple of 3 floats, optional (default (lwl / 2, 0, -Tc/3)).
        Represents the x, y, z coordinates of the centre of effort.
        For residuary resistance, the surfacic barycentre of the underwater hull
        is an acceptable estimate.
        x=lwl / 2 , y = 0., z = -Tc/3 is another acceptable estimate.
    rho_water : Water density [kg/m**3], must be >= 0 (default RHO_SEA_WATER)

    Returns a Force object, representing the residuary resistance [N]
    of the keel at 0° heel.

    """
    if Vc <= 0:
        raise ValueError("Vc should be strictly positive")
    if Vk <= 0:
        raise ValueError("Vk should be strictly positive")
    if Tc <= 0:
        raise ValueError("Tc should be strictly positive")
    if T <= 0:
        raise ValueError("T should be strictly positive")
    if lwl <= 0:
        raise ValueError("lwl should be strictly positive")
    if bwl <= 0:
        raise ValueError("bwl should be strictly positive")
    if not RHO_WATER_MIN < rho_water < RHO_WATER_MAX:
        raise ValueError(
            f"rho_water should be between {RHO_WATER_MIN} and {RHO_WATER_MAX}")
    # DSYHS checks
    # midship area coefficient cannot be checked here
    check_lwl_to_bwl(value=lwl / bwl)
    check_bwl_to_draft(value=bwl / Tc)
    check_lwl_to_volume(value=lwl / Vc**(1. / 3.))

    if coe is None:
        coe = (lwl / 2., 0., -Tc / 3.) if coe is None else coe

    interp = _get_keel_residuary_ks_interpolator(Vc, Vk, Tc, T, bwl, Zcbk,
                                                 rho_water)

    boatspeed_sign = boatspeed / abs(boatspeed) if boatspeed != 0. else 0.

    froude = froude_number(speed=boatspeed, lwl=lwl)
    if froude <= 0.6:
        residuary_resistance = max(
            [0., interp(abs(boatspeed / sqrt(GRAVITY_STANDARD * lwl)))])
    else:
        at_0_59 = interp(0.59)
        at_0_60 = interp(0.60)
        residuary_resistance = at_0_60 + \
                               (froude - 0.6) * (at_0_60 - at_0_59) / 0.01

    return Force(-residuary_resistance * boatspeed_sign, 0., 0., coe[0],
                 coe[1], coe[2])