Fluids
Bodies in RaiSim can float, sink, swim and fly through water and air. A world can hold volumes of liquid under a free surface, such as a pool, a lake or the sea, and an ambient fluid, such as air, everywhere else. Every dynamic body is held up by the weight of the fluid it displaces. The fluid drags it, lifts it, and carries part of its own mass along with it. Fluids act on single bodies, compounds, meshes and every link of an articulated system, through their collision shapes.

Fluids are off by default. A world without them steps exactly as before. The equations of the forces and of the motion they produce are written out in Dynamics of Fluid Forces.
Adding water and air
raisim::World world;
// air everywhere outside the water
world.setAmbientFluid(raisim::FluidMedium::air());
// a pool 4 m by 2.4 m, filled to 0.7 m
raisim::FluidVolume pool;
pool.medium = raisim::FluidMedium::water();
pool.surfaceHeight = 0.7;
pool.bottomHeight = 0.0;
pool.min = {-2.0, -1.2};
pool.max = {2.0, 1.2};
const size_t index = world.addFluidVolume(pool);FluidMedium holds a fluid's density water(), seawater() and air() hold their values at 20 °C, 15 °C and 20 °C.
A FluidVolume holds the collision shapes whose centroids lie within its horizontal bounds and above bottomHeight, and that reach below surfaceHeight; by default it reaches the horizon. The surface cuts a shape where it crosses it. The walls and the bottom are meant to be solid, like the walls of a pool: a shape that reaches through them is still wholly in the liquid up to the surface. Where two volumes overlap, the first one added holds a shape.
To change the current, the water level or the bounds, call World::setFluidVolume(index, volume). Every call that changes a fluid wakes the sleeping bodies.
auto river = world.getFluidVolumes()[index];
river.medium.velocity = {0.5, 0.0, 0.0};
world.setFluidVolume(index, river);Waves
A volume's waves move its surface and the liquid under it. Each FluidWave is a linear wave on deep water: it travels along its direction with its wavelength
with its amplitude phase
where

raisim::FluidVolume sea;
sea.medium = raisim::FluidMedium::seawater();
sea.waves.push_back({{1.0, 0.0}, 20.0, 0.3, 0.0}); // direction, wavelength, amplitude, phase
sea.waves.push_back({{0.6, 0.8}, 7.0, 0.1, 1.0});
world.addFluidVolume(sea);Rivers and other surfaces
A volume's sampler gives it a surface and a current that vary from place to place in any other way, and takes the place of its waves. It receives the centroid of a collision body and returns the height and normal of the surface over that point, and the velocity and acceleration of the liquid there. The body sees the surface as the plane through the sample, tilted by its normal, so it rides the slope of a wave. A wave field or a river simulated elsewhere plugs in this way. For example, rayrai's RayraiWindow::sampleWaterSurface() gives the waves it draws:
volume.sampler = [&viewer](const raisim::Vec<3>& point) {
const auto surface = viewer.sampleWaterSurface({point[0], point[1]});
raisim::FluidSample sample;
sample.surfaceHeight = surface.height;
sample.surfaceNormal = {surface.normal.x, surface.normal.y, surface.normal.z};
return sample;
};The sampler runs on the thread that steps the world, once for every collision body in the volume's bounds. The world stays deterministic as long as the sampler's result depends only on the point and the simulation state. A body within a sampled volume's horizontal bounds never falls asleep, wet or dry, since the surface or the current may change while it rests.
World files, scenes and Engine 2
A world file holds the fluids in its fluids element and each body's coefficients in a fluid child (see World Configuration File); World::exportToXml() writes them. A .rscene scene holds them in its ambient_fluid and fluid_volume records and the fluid* keys of its objects (see .rscene File), and rayrai draws the water of its volumes. In RaiSim Engine 2, a FluidVolume node is a pool or the sea, and the Physics settings hold the ambient fluid; the Fluid Pool template starts from a pool with a current. Its exported world.xml carries the fluids to raisimGymTorch.
Buoyancy
The fluid pushes a body up with the weight of the fluid it displaces, and this force acts at the centroid of the displaced volume:
- the cap of a sphere in closed form;
- a box or a mesh by cutting its faces at the surface;
- a cylinder or a capsule by integrating the circular segments under the surface along its axis.
Because the force acts at the centroid of the volume below the surface, the restoring moment of a floating body is that of hydrostatics. A body is stable where its metacenter lies above its center of mass. The half-dense wooden cube of the fluid pool example turns an edge up: a cube of density between 0.28 and 0.72 times that of water does not float on a face. The part of a body above the surface displaces the ambient fluid, so a light, large body in air also feels the air's buoyancy.
A body bobbing at the surface makes waves that carry its energy away, so it settles within a few periods. The volume's waveRadiation scales this damping, and 0 turns it off. At the body's natural heave frequency
A body that falls into the water sets the water under it moving. As it sinks in, its added mass (see below) grows, and the body hands that new fluid its momentum (von Kármán's water entry): a flat plate that falls flat into the water slows at once to

Drag and lift
The surface of each shape is made of small faces. The flow pushes on a face that meets it with a pressure along its normal
| Shape | |
|---|---|
| Sphere | Brown and Lawler's correlation in the Reynolds number: 0.41 at |
| Box face | 1.05 |
| Cylinder across its axis | 1.0 |
| Cylinder along its axis | 0.82 |
| Capsule | 1.0 across its axis, the sphere's on its ends |
| Mesh triangle | 1.0 |
A plate that meets the flow at an angle of attack
Every shape also feels the Stokes drag of the viscosity,
Added mass
A body that accelerates in a fluid also accelerates the fluid around it, so it moves as if it were heavier. The extra inertia
- half the displaced mass for a sphere;
- the displaced mass for a long cylinder moving across its axis;
- far more than its own mass for a thin plate moving across its face.
RaiSim adds
| Shape | Added mass |
|---|---|
| Sphere | |
| Ellipsoid | Lamb's coefficients (matches DNV-RP-C205, Table D-2, to 0.1 %) |
| Box | DNV-RP-C205 plates and prisms: |
| Cylinder | across its axis within 5 % of DNV-RP-C205; along it, its flat ends |
| Capsule, mesh | the ellipsoid that shows the same projected areas, at the shape's volume |
The fluid carried along also turns the body as it moves (Kirchhoff's equations). A long body that moves at an angle to its axis feels the Munk moment, which turns it toward broadside to its motion. A body wholly in one still, inviscid fluid, with no other force on it, keeps its kinetic energy with its added mass exactly. Part of a body above the surface carries the added mass of the fluid there.

Fluid coefficients
FluidCoefficients sets how fluids act on a collision body:
| Field | Default | Meaning |
|---|---|---|
enabled | true | whether fluids act on the shape |
buoyancy | true | whether the fluid holds it up |
drag | ||
skinFriction | 0.005 | |
magnusLift | 0.5 | |
addedMass | 1 | scale of the added mass |
raisim::FluidCoefficients hull;
hull.drag = 0.6;
world.setFluidCoefficients(boat, hull); // every collision body of an object
robot->getCollisionBody("fin/0").setFluidCoefficients(hull); // one body of an articulated systemObject::getFluidForce(localIdx) and Object::getFluidTorque(localIdx) return what the fluids did to a body in the last step, about its center of mass in the world frame. This covers buoyancy, drag, lift and the turn the added mass gives a body that moves, such as the Munk moment, at the velocity the step started with. The added mass that resists acceleration is not part of it.
Accuracy
Each value below is checked by RaiSim's tests:
| Case | Expected | RaiSim |
|---|---|---|
| Sphere, box, cylinder, capsule cut by the surface at any tilt | buoyancy of the displaced volume | exact (cylinder and capsule to |
| Ping-pong ball let go under water | 15.5 m/s² | 15.5 m/s² |
| Sphere on a spring in water, | 0.706 s | within 0.2 % |
| 10 mm steel ball sinking in water | 1.45 m/s | to |
| 1 mm steel grain settling in glycerol at a step three times its relaxation time | 2.5 mm/s, without overshoot | within 0.5 % |
| Box half as dense as water | floats at half its height | within 0.01 mm |
| Capsule moving at an angle in still, inviscid water | keeps its kinetic energy | to |
| Plate falling flat into water, | within 3.3 % at 1 ms, 0.16 % at 0.25 ms | |
| Light float on a 20 m wave of amplitude | heaves by | within 0.5 % |
| 2 m box held at the surface of a 4 m wave of amplitude 0.06 m, at an angle to it | buoyancy and yaw moment of the columns of water over its bottom | force within 0.3 %, moment within 1 % (the tangent plane at its centroid: 3 %, 18 %) |
| Sphere sinking into a pool, turned any way | the same force; no force across its motion | to |
| Sphere crossing the surface | drag without a jump as it sinks under or first touches | to |
| Upright cylinder a quarter of a wave across, held there | buoyancy of the columns of water over its disc | within 0.1 % |
| Box rolled by 0.3 rad, and one lying on its side, held at the surface of a 3 m wave | buoyancy of the water below the surface within it | within 0.15 % |
| Particle as dense as water 2 m under that wave | moves on a circle of radius | within 5 % horizontally, 8 % vertically |
Particle as dense as water 1 m or 3 m under a 10 m wave of amplitude 0.2 m, over 50 periods at a 2 ms step, as a single body or an articulated system integrating with TRAPEZOID or SEMI_IMPLICIT | keeps its depth; drifts forward at | depth within 15 % of the circle's radius, drift within 5 % |
Limits
- The fluid does not move. Bodies do not stir it, so they leave no wake. A body in another body's wake, or between two walls, feels the free stream.
- A body sees the surface as one plane: under waves, the waves averaged over its extent; under a sampler, the plane through the sample at its centroid, tilted by the sampler's normal. Waves that wet a body along part of its length and leave the rest dry, as over a raft thinner than they are high, are seen through that one plane, so its buoyancy is approximate. The velocity and acceleration of the waves are taken at the centroid, so a body longer than about a quarter of a wave feels their motion approximately.
- A floating body drifts over many waves mostly because of the waves it reflects, which these forces leave out, so its drift is not reliable. A body under the surface drifts with the Stokes drift.
- Forces are quasi-steady. The added mass of a body that pierces the surface is that of the part below it, which is approximate within a factor of order one at most wave frequencies.
- Deformable and granular objects do not feel these fluids. Cloth has its own
airDrag. - An articulated system integrating with
EULERdrifts off the orbits of the waves over many periods;SEMI_IMPLICIT(the default) andTRAPEZOIDkeep them. - The TCP viewer draws the water of every volume without a sampler as a still pool or sea, and the water of a scene file it receives in that scene's look. The waves of a sampled volume are not drawn there; rayrai draws them with
RayraiWindow::setWaterBody().
Examples
- Fluid pool: bodies of different densities float, tilt and sink.
- Fluid stream: a stream, whose speed a slider in the viewer sets, carries crates, logs and balls off and slides bricks along its bed when strong.
- Fluid stream with ANYmal: a stream strong enough tips a standing ANYmal over and carries it off.
- Floating on waves: crates, a raft and barrels ride the swell rayrai draws, through a sampler.
- Underwater ROV: a vehicle holds its station against a turning current.