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
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)
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
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
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])
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])
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])
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])
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])
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])
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])