Skip to content
LIBRARY
examples.BuoyantBody.md

examples.BuoyantBody

Floating spar-buoy platform: a rigid body with buoyancy and hydrodynamic forces from the surrounding water.

The hull is a vertical cylinder (spar) of length length_spar and radius radius_spar, aligned with the local y-axis of frame_a, which sits at the geometric center of the spar. An internal Body carries the mass and inertia properties; the center of mass is typically placed below the frame origin (ballast) so that buoyancy above and weight below produce a restoring moment in roll and pitch.

The water surface is a monochromatic traveling wave of amplitude wave_amplitude and angular frequency wave_omega, propagating along the world x-axis with the deep-water dispersion relation. The buoyancy force equals the weight of the displaced water column, computed from the submerged length of the (possibly tilted) spar below the local wave elevation, and acts at the center of the submerged section. Linear hydrodynamic drag pulls the body toward the local wave orbital velocity, which both damps the platform motion and excites surge and pitch as waves pass by. A linear rotational drag damps angular motion.

The body is a free-floating 6-DOF root with quaternion orientation state: do not connect frame_a to the world through any joint; moor it with force elements or Cables instead.

Model assumptions and validity limits:

  • Hydrostatics use the submerged length along the spar axis divided by the vertical component of the spar axis; the model is invalid for tilt angles approaching 90 degrees (the axis projection is guarded below 0.1).

  • No added mass, no wave diffraction/radiation, linear drag only.

  • The wave field is not affected by the body.

This component extends from MultibodyComponents.Renderable

Usage

MultibodyComponents.examples.BuoyantBody(render=true, color=[0.95, 0.6, 0.1, 1], specular_coefficient=1.5, m=2e4, r_cm=[0, -3, 0], I_11=2e5, I_22=1e4, I_33=2e5, radius_spar=1, length_spar=10, rho_water=1000, d_translation=2e4, d_rotation=2e5, wave_amplitude=0.5, wave_omega=1.05, r_0_start=[0, -1.4, 0], A_c=π * radius_spar ^ 2, wave_k=wave_omega ^ 2 / 9.80665)

Parameters:

NameDescriptionUnitsDefault value
point_gravityWhether to use a point-gravity field, see Worldfalse
initialize_orientationSpecify identity initial orientation; set to false to let initialization solve for the orientationtrue
rendertrue
color[0.95, 0.6, 0.1, 1]
specular_coefficient1.5
mMass of the platform including ballastkg2e4
r_cmCenter of mass relative to frame_a, resolved in frame_a; place below the origin for ballast stabilitym[0, -3, 0]
I_11Moment of inertia about the local x-axiskg.m22e5
I_22Moment of inertia about the local y-axis (spar axis)kg.m21e4
I_33Moment of inertia about the local z-axiskg.m22e5
radius_sparRadius of the spar cylinderm1
length_sparLength of the spar cylinder along the local y-axism10
rho_waterDensity of the water1000
d_translationLinear translational drag coefficient toward the local water velocity2e4
d_rotationLinear rotational drag coefficient2e5
wave_amplitudeAmplitude of the traveling surface wavem0.5
wave_omegaAngular frequency of the traveling surface wave1.05
r_0_startInitial position of frame_a in the world framem[0, -1.4, 0]

Connectors

  • frame_a - Frame3D is the fundamental 3D connector used for 6DOF motion. Most components have one or several Frame

connectors that can be connected together (Frame3D)

Variables

NameDescriptionUnits
h_localLocal wave elevation at the platform x-position
v_waterWave orbital velocity at the platform, resolved in the world frame
e_axis_0Spar axis direction resolved in the world frame
r_botPosition of the spar bottom end in the world frame
L_subSubmerged length of the spar, measured along the spar axis
r_cbCenter of buoyancy (centroid of the submerged section) in the world frame
f_hydroTotal hydrodynamic force resolved in the world frame
tau_hydroTotal hydrodynamic torque about frame_a, resolved in the world frame

Behavior

Source

dyad
"""
Floating spar-buoy platform: a rigid body with buoyancy and hydrodynamic
forces from the surrounding water.

The hull is a vertical cylinder (spar) of length `length_spar` and radius
`radius_spar`, aligned with the local y-axis of `frame_a`, which sits at the
geometric center of the spar. An internal `Body` carries the mass and inertia
properties; the center of mass is typically placed below the frame origin
(ballast) so that buoyancy above and weight below produce a restoring moment
in roll and pitch.

The water surface is a monochromatic traveling wave of amplitude
`wave_amplitude` and angular frequency `wave_omega`, propagating along the
world x-axis with the deep-water dispersion relation. The buoyancy force
equals the weight of the displaced water column, computed from the submerged
length of the (possibly tilted) spar below the local wave elevation, and acts
at the center of the submerged section. Linear hydrodynamic drag pulls the
body toward the local wave orbital velocity, which both damps the platform
motion and excites surge and pitch as waves pass by. A linear rotational drag
damps angular motion.

The body is a free-floating 6-DOF root with quaternion orientation state:
do not connect `frame_a` to the world through any joint; moor it with force
elements or `Cable`s instead.

Model assumptions and validity limits:
- Hydrostatics use the submerged length along the spar axis divided by the
  vertical component of the spar axis; the model is invalid for tilt angles
  approaching 90 degrees (the axis projection is guarded below 0.1).
- No added mass, no wave diffraction/radiation, linear drag only.
- The wave field is not affected by the body.
"""
component BuoyantBody
  extends MultibodyComponents.Renderable(color = [0.95, 0.6, 0.1, 1])
  frame_a = Frame3D()
  body = MultibodyComponents.Body(m = m, r_cm = r_cm, I_11 = I_11, I_22 = I_22, I_33 = I_33, orientation_state = OrientationState.Quaternion(), statePriority = 100, linearStatePriority = 100, render = false, r_0(initial = r_0_start), v_0(initial = [0, 0, 0]), initialize_orientation = initialize_orientation)
  buoy_force = MultibodyComponents.WorldForce(resolve_in_frame = ResolveInFrame.World(), render = false)
  buoy_torque = MultibodyComponents.WorldTorque(resolve_in_frame = ResolveInFrame.World())
  spar_vis = MultibodyComponents.CylinderVisualizer(color = color, render = render, length = length_spar, radius = radius_spar, length_direction = [0, 1, 0], r_shape = [0, -length_spar / 2, 0])
  "Whether to use a point-gravity field, see `World`"
  structural parameter point_gravity::Boolean = false
  "Specify identity initial orientation; set to false to let initialization solve for the orientation"
  structural parameter initialize_orientation::Boolean = true
  "Mass of the platform including ballast"
  parameter m::Mass = 2e4
  "Center of mass relative to frame_a, resolved in frame_a; place below the origin for ballast stability"
  parameter r_cm::Length[3] = [0, -3, 0]
  "Moment of inertia about the local x-axis"
  parameter I_11::Inertia = 2e5
  "Moment of inertia about the local y-axis (spar axis)"
  parameter I_22::Inertia = 1e4
  "Moment of inertia about the local z-axis"
  parameter I_33::Inertia = 2e5
  "Radius of the spar cylinder"
  parameter radius_spar::Length = 1
  "Length of the spar cylinder along the local y-axis"
  parameter length_spar::Length = 10
  "Density of the water"
  parameter rho_water::Real = 1000
  "Linear translational drag coefficient toward the local water velocity"
  parameter d_translation::Real = 2e4
  "Linear rotational drag coefficient"
  parameter d_rotation::Real = 2e5
  "Amplitude of the traveling surface wave"
  parameter wave_amplitude::Length = 0.5
  "Angular frequency of the traveling surface wave"
  parameter wave_omega::Real = 1.05
  "Initial position of frame_a in the world frame"
  parameter r_0_start::Length[3] = [0, -1.4, 0]
  "Cross-section area of the spar"
  final parameter A_c::Real = π * radius_spar ^ 2
  "Wave number from the deep-water dispersion relation"
  final parameter wave_k::Real = wave_omega ^ 2 / 9.80665
  "Local wave elevation at the platform x-position"
  variable h_local::Real
  "Wave orbital velocity at the platform, resolved in the world frame"
  variable v_water::Real[3]
  "Spar axis direction resolved in the world frame"
  variable e_axis_0::Real[3]
  "Position of the spar bottom end in the world frame"
  variable r_bot::Real[3]
  "Submerged length of the spar, measured along the spar axis"
  variable L_sub::Real
  "Center of buoyancy (centroid of the submerged section) in the world frame"
  variable r_cb::Real[3]
  "Total hydrodynamic force resolved in the world frame"
  variable f_hydro::Real[3]
  "Total hydrodynamic torque about frame_a, resolved in the world frame"
  variable tau_hydro::Real[3]
relations
  connect(frame_a, body.frame_a, buoy_force.frame_b, buoy_torque.frame_b, spar_vis.frame_a)
  if !initialize_orientation
    guess body.Q_hat = [1, 0, 0, 0]
  end
  h_local = wave_amplitude * sin(wave_omega * time - wave_k * frame_a.r_0[1])
  v_water = [wave_amplitude * wave_omega * cos(wave_omega * time - wave_k * frame_a.r_0[1]), 0, 0]
  e_axis_0 = MultibodyComponents.resolve1(frame_a.R, [0, 1, 0])
  r_bot = frame_a.r_0 - (length_spar / 2) * e_axis_0
  # Distance from the spar bottom to the water surface along the spar axis,
  # clamped to the physical range; the axis projection is guarded away from
  # zero to avoid a singularity at horizontal spar orientations
  L_sub = min(max((h_local - r_bot[2]) / max(e_axis_0[2], 0.1), 0), length_spar)
  r_cb = r_bot + (L_sub / 2) * e_axis_0
  f_hydro = -rho_water * A_c * L_sub * MultibodyComponents.gravity_acceleration(r_cb, point_gravity) + d_translation * (v_water - body.v_0)
  tau_hydro = MultibodyComponents.cross(r_cb - frame_a.r_0, f_hydro) - d_rotation * MultibodyComponents.resolve1(frame_a.R, body.w_a)
  buoy_force.force_x = f_hydro[1]
  buoy_force.force_y = f_hydro[2]
  buoy_force.force_z = f_hydro[3]
  buoy_torque.torque_x = tau_hydro[1]
  buoy_torque.torque_y = tau_hydro[2]
  buoy_torque.torque_z = tau_hydro[3]
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MultibodyComponents/BuoyantBody.svg"},
    "labels": [
      {
        "label": "$(instance)",
        "x": 500,
        "y": 150,
        "rot": 0,
        "attrs": {"font-size": "160"}
      }
    ]
  }
}
end
Flattened Source
dyad
"""
Floating spar-buoy platform: a rigid body with buoyancy and hydrodynamic
forces from the surrounding water.

The hull is a vertical cylinder (spar) of length `length_spar` and radius
`radius_spar`, aligned with the local y-axis of `frame_a`, which sits at the
geometric center of the spar. An internal `Body` carries the mass and inertia
properties; the center of mass is typically placed below the frame origin
(ballast) so that buoyancy above and weight below produce a restoring moment
in roll and pitch.

The water surface is a monochromatic traveling wave of amplitude
`wave_amplitude` and angular frequency `wave_omega`, propagating along the
world x-axis with the deep-water dispersion relation. The buoyancy force
equals the weight of the displaced water column, computed from the submerged
length of the (possibly tilted) spar below the local wave elevation, and acts
at the center of the submerged section. Linear hydrodynamic drag pulls the
body toward the local wave orbital velocity, which both damps the platform
motion and excites surge and pitch as waves pass by. A linear rotational drag
damps angular motion.

The body is a free-floating 6-DOF root with quaternion orientation state:
do not connect `frame_a` to the world through any joint; moor it with force
elements or `Cable`s instead.

Model assumptions and validity limits:
- Hydrostatics use the submerged length along the spar axis divided by the
  vertical component of the spar axis; the model is invalid for tilt angles
  approaching 90 degrees (the axis projection is guarded below 0.1).
- No added mass, no wave diffraction/radiation, linear drag only.
- The wave field is not affected by the body.
"""
component BuoyantBody
  parameter render::Boolean = true
  parameter color::Real[4] = [0.5, 0.5, 0.5, 1.0]
  parameter specular_coefficient::Real = 1.5
  frame_a = Frame3D()
  body = MultibodyComponents.Body(m = m, r_cm = r_cm, I_11 = I_11, I_22 = I_22, I_33 = I_33, orientation_state = OrientationState.Quaternion(), statePriority = 100, linearStatePriority = 100, render = false, r_0(initial = r_0_start), v_0(initial = [0, 0, 0]), initialize_orientation = initialize_orientation)
  buoy_force = MultibodyComponents.WorldForce(resolve_in_frame = ResolveInFrame.World(), render = false)
  buoy_torque = MultibodyComponents.WorldTorque(resolve_in_frame = ResolveInFrame.World())
  spar_vis = MultibodyComponents.CylinderVisualizer(color = color, render = render, length = length_spar, radius = radius_spar, length_direction = [0, 1, 0], r_shape = [0, -length_spar / 2, 0])
  "Whether to use a point-gravity field, see `World`"
  structural parameter point_gravity::Boolean = false
  "Specify identity initial orientation; set to false to let initialization solve for the orientation"
  structural parameter initialize_orientation::Boolean = true
  "Mass of the platform including ballast"
  parameter m::Mass = 2e4
  "Center of mass relative to frame_a, resolved in frame_a; place below the origin for ballast stability"
  parameter r_cm::Length[3] = [0, -3, 0]
  "Moment of inertia about the local x-axis"
  parameter I_11::Inertia = 2e5
  "Moment of inertia about the local y-axis (spar axis)"
  parameter I_22::Inertia = 1e4
  "Moment of inertia about the local z-axis"
  parameter I_33::Inertia = 2e5
  "Radius of the spar cylinder"
  parameter radius_spar::Length = 1
  "Length of the spar cylinder along the local y-axis"
  parameter length_spar::Length = 10
  "Density of the water"
  parameter rho_water::Real = 1000
  "Linear translational drag coefficient toward the local water velocity"
  parameter d_translation::Real = 2e4
  "Linear rotational drag coefficient"
  parameter d_rotation::Real = 2e5
  "Amplitude of the traveling surface wave"
  parameter wave_amplitude::Length = 0.5
  "Angular frequency of the traveling surface wave"
  parameter wave_omega::Real = 1.05
  "Initial position of frame_a in the world frame"
  parameter r_0_start::Length[3] = [0, -1.4, 0]
  "Cross-section area of the spar"
  final parameter A_c::Real = π * radius_spar ^ 2
  "Wave number from the deep-water dispersion relation"
  final parameter wave_k::Real = wave_omega ^ 2 / 9.80665
  "Local wave elevation at the platform x-position"
  variable h_local::Real
  "Wave orbital velocity at the platform, resolved in the world frame"
  variable v_water::Real[3]
  "Spar axis direction resolved in the world frame"
  variable e_axis_0::Real[3]
  "Position of the spar bottom end in the world frame"
  variable r_bot::Real[3]
  "Submerged length of the spar, measured along the spar axis"
  variable L_sub::Real
  "Center of buoyancy (centroid of the submerged section) in the world frame"
  variable r_cb::Real[3]
  "Total hydrodynamic force resolved in the world frame"
  variable f_hydro::Real[3]
  "Total hydrodynamic torque about frame_a, resolved in the world frame"
  variable tau_hydro::Real[3]
relations
  connect(frame_a, body.frame_a, buoy_force.frame_b, buoy_torque.frame_b, spar_vis.frame_a)
  if !initialize_orientation
    guess body.Q_hat = [1, 0, 0, 0]
  end
  h_local = wave_amplitude * sin(wave_omega * time - wave_k * frame_a.r_0[1])
  v_water = [wave_amplitude * wave_omega * cos(wave_omega * time - wave_k * frame_a.r_0[1]), 0, 0]
  e_axis_0 = MultibodyComponents.resolve1(frame_a.R, [0, 1, 0])
  r_bot = frame_a.r_0 - (length_spar / 2) * e_axis_0
  # Distance from the spar bottom to the water surface along the spar axis,
  # clamped to the physical range; the axis projection is guarded away from
  # zero to avoid a singularity at horizontal spar orientations
  L_sub = min(max((h_local - r_bot[2]) / max(e_axis_0[2], 0.1), 0), length_spar)
  r_cb = r_bot + (L_sub / 2) * e_axis_0
  f_hydro = -rho_water * A_c * L_sub * MultibodyComponents.gravity_acceleration(r_cb, point_gravity) + d_translation * (v_water - body.v_0)
  tau_hydro = MultibodyComponents.cross(r_cb - frame_a.r_0, f_hydro) - d_rotation * MultibodyComponents.resolve1(frame_a.R, body.w_a)
  buoy_force.force_x = f_hydro[1]
  buoy_force.force_y = f_hydro[2]
  buoy_force.force_z = f_hydro[3]
  buoy_torque.torque_x = tau_hydro[1]
  buoy_torque.torque_y = tau_hydro[2]
  buoy_torque.torque_z = tau_hydro[3]
metadata {
  "Dyad": {
    "icons": {"default": "dyad://MultibodyComponents/BuoyantBody.svg"},
    "labels": [
      {
        "label": "$(instance)",
        "x": 500,
        "y": 150,
        "rot": 0,
        "attrs": {"font-size": "160"}
      }
    ]
  }
}
end


Test Cases

No test cases defined.

  • Examples

  • Experiments

  • Analyses