Boats and Submarines#
A hull floats when the water it pushes aside weighs more than the hull does.
Flight.displacement is the volume of water the vessel pushes aside when it is
fully submerged, and Flight.buoyant_force_at() is the force that volume pushes back
with:
body = vessel.orbit.body
flight = vessel.flight()
displacement = flight.displacement
buoyancy = flight.buoyant_force_at(body.ocean_density, body.surface_gravity)
Compare that force with the vessel’s weight. A hull with more buoyancy than its weight
floats. A submarine trims its tanks to move between the two.
Part.displacement and Part.buoyant_force_at() give the same figures for one
part.
Note
Flight.buoyant_force_at() applies SpaceCenter.buoyancy_scalar, which is 1.2
in stock KSP. A buoyant force in the game is a fifth larger than
Archimedes’ principle alone
gives.
Trim#
A hull at rest carries its center of buoyancy on the same vertical line as its center of mass. A horizontal offset between the two turns the weight and the buoyancy into a couple that rolls or pitches the hull. That offset is the trim.
Read both in the vessel’s surface reference frame. Its x axis points up, so the trim is what the other two axes carry:
flight = vessel.flight(vessel.surface_reference_frame)
center_of_buoyancy = flight.center_of_buoyancy
center_of_mass = flight.center_of_mass
Flight.center_of_buoyancy is None when no part of the vessel is in the water.
The couple is also available as a torque about the center of mass. A hull trimmed level carries zero torque:
torque = flight.buoyant_torque_at(body.ocean_density, body.surface_gravity)
Flight.buoyant_torque_at() is the torque with the hull fully submerged. It holds
steady while the hull rides the waves, where Flight.buoyant_torque follows the
live waterline.
In the Editor#
A hull can be trimmed before it is launched. EditorVessel.flight() returns a
SimulatedFlight, which has the fully submerged members of Flight. Its
SimulatedFlight.surface_reference_frame sets which way is up. Here a hull built
nose-up in the vehicle assembly building is simulated floating on its bottom:
vessel = space_center.editor.vessel
flight = vessel.flight(vessel.reference_frame)
# The vessel frame's z axis points out of the bottom, so up is its -z axis
side = math.sqrt(0.5)
flight.surface_reference_frame = space_center.ReferenceFrame.create_relative(
vessel.reference_frame, rotation=(0, side, 0, side)
)
torque = flight.buoyant_torque_at(body.ocean_density, body.surface_gravity)
Gravity acts along the negative x axis of the surface reference frame. A frame created
relative to EditorVessel.reference_frame follows the hull as it is edited.
Submersion#
Flight.submerged_portion is the fraction of the vessel below the waterline, and
Flight.depth is how far its center of mass sits below the surface. The same pair
is available per part, alongside Part.splashed and the depth of the part’s
shallowest and deepest points.
Pressure#
Below the waterline, Flight.static_pressure and Part.static_pressure
include the weight of the water. With part pressure limits enabled in the difficulty
settings, a part is destroyed when its pressure exceeds Part.max_pressure:
for part in vessel.parts.all:
if part.static_pressure + part.dynamic_pressure > part.max_pressure:
print(part.title, "is over its pressure limit")
The loop reads the parts with separate calls, which can land in different physics ticks. A server side function reads every part in the same tick, with a single call:
over_limit = conn.run(
lambda: [
part.title
for part in vessel.parts.all
if part.static_pressure + part.dynamic_pressure > part.max_pressure
]
)
CelestialBody.pressure_at() takes a negative altitude for a point under the sea, to
plan how deep a dive can go.
Example#
This script reports whether the active vessel floats, and how far its center of buoyancy sits from its center of mass:
import math
import krpc
conn = krpc.connect(name="Buoyancy Trim")
vessel = conn.space_center.active_vessel
body = vessel.orbit.body
if not body.has_ocean:
raise RuntimeError(body.name + " has no ocean")
flight = vessel.flight(vessel.surface_reference_frame)
# The fully submerged figures, which do not depend on where the vessel is
buoyancy = flight.buoyant_force_at(body.ocean_density, body.surface_gravity)
torque = flight.buoyant_torque_at(body.ocean_density, body.surface_gravity)
weight = vessel.mass * body.surface_gravity
print("Displacement: %.1f m^3" % flight.displacement)
print("Buoyancy: %.0f N fully submerged" % buoyancy)
print("Weight: %.0f N" % weight)
print("Floats: %s" % (buoyancy > weight))
print("Torque: %.0f N m fully submerged" % math.hypot(*torque))
# The trim, read in the surface reference frame. Its x axis points up, so the y and z
# axes span the horizontal. A horizontal offset between the two centers tips the hull
# until they line up again.
center_of_buoyancy = flight.center_of_buoyancy
if center_of_buoyancy is None:
print("Trim: vessel is out of the water")
else:
center_of_mass = flight.center_of_mass
north = center_of_buoyancy[1] - center_of_mass[1]
east = center_of_buoyancy[2] - center_of_mass[2]
print("Submerged: %.0f%%" % (100 * flight.submerged_portion))
print("Trim: %.2f m horizontally" % math.hypot(north, east))
print("Trim north: %+.2f m" % north)
print("Trim east: %+.2f m" % east)