22. AGX Hydrodynamics

When an object is moving through air, water or another fluid it is affected by hydrodynamic effects such as lift, drag and added mass. These effects can be simulated using a WindAndWaterController. Only one (1) controller is needed for each simulation.

// Create wind and water controller and add it to the simulation.
agxModel::WindAndWaterControllerRef controller = new agxModel::WindAndWaterController();
simulation->add( controller );

The WindAndWaterController performs hydro- and aerodynamic calculations on the following objects:

  • Shapes

    • Based on a mesh

    • Boxes

    • Spheres

    • Capsules

    • Cylinders

    • Cones

    • Hollow cones

    • Hollow cylinders

  • Wires

  • Cables

For shapes, the effects hydro- and aerodynamic are calculated for each triangle in a mesh. If it is a primitive shape, e.g. a sphere, the calculations are made on a tessellation of that primitive shape.

Wires and cables are not tessellated but divided by the segments. For each segment, the calculations are performed assuming the segment consists of the curved surface of a cylinder.

Hydrodynamic effects will be calculated if the object is in contact with water, otherwise aerodynamic calculations will be performed. For a shape this means that each triangle will be tested. If the triangle is partially submerged the triangle will be clipped creating new triangles so that each triangle is considered to be either completely in air or completely in water. For a wire or cable each segment is tested and if it is partially submerged the segment is divided in two parts, one for hydrodynamics and one for aerodynamics.

The effects that can be calculated is:

  • Buoyancy - a floating force in negative gravity direction.

  • Pressure drag – a force depending on the normal relative velocity.

  • Viscous drag – a force depending on the tangential relative velocity.

  • Lift – a force in the normal direction depending on the tangential relative velocity.

  • Added mass – from the surrounding fluid. (Only for shapes, not for wires or cables)

It is possible to access the calculated forces from the different effects in the following way:

// Access all the forces/torques added together for a geometry
agx::Vec3 totalForce = windAndWaterController->getForce(geometry);
agx::Vec3 totalTorque = windAndWaterController->getTorque(geometry);

// Access the buoyancy force on a geometry
// Changing the ForceType to PRESSURE_DRAG, VISCOUS_DRAG, or LIFT will give us those forces instead
agx::Vec3 buoyancyForce = windAndWaterController->getForceComponent(
  geometry, agxModel::WindAndWaterController::ForceType::BUOYANCY);
// In the same way we can access torque components from different effects
agx::Vec3 buoyancyTorque = windAndWaterController->getTorqueComponent(
  geometry, agxModel::WindAndWaterController::ForceType::BUOYANCY);

Note

For hydrodynamic calculations the lift forces of wires and cables will be zero due to symmetry and will therefore be omitted.

Note

For the aerodynamic calculations, buoyancy, center of buoyancy and added mass will be omitted due to the low density of air.

The default values for relevant constants are specified in Table 22.1.

Table 22.1 Default values used in the simulation

Name

Value

Water density

1000

Air density

1.2

Pressure drag coefficient

0.6

Viscous drag coefficient

0.1

Lift coefficient

0.01

By changing the density of the fluid and the coefficients of the object, the WindAndWaterController can simulate many fluids other than water and air.

Attention

For multi threaded applications the results of hydro- and aerodynamics may be nondeterministic.

22.1. Parameters

For each shape, there are hydro- and aerodynamic parameters. These can be used to calibrate the behavior of objects in air or water. Note that the interaction between an object and a fluid is defined only by these parameters and not contact materials.

For a shape or wire, these can be reached in the following way:

// Hydrodynamic parameter for a shape
agxModel::WindAndWaterParametersRef hydrodynamicParameters = controller->
  getOrCreateHydrodynamicsParameters( shape );

// Aerodynamic parameters for a shape
agxModel::WindAndWaterParametersRef aerodynamicParameters = controller->
  getOrCreateAerodynamicsParameters( shape );

// Hydrodynamic parameter for a wire
agxModel::WindAndWaterParametersRef hydrodynamicParameters = controller->
  getOrCreateHydrodynamicsParameters( wire );

// Aerodynamic parameters for a wire
agxModel::WindAndWaterParametersRef aerodynamicParameters = controller->
  getOrCreateAerodynamicsParameters( wire );

For a cable, parameters can be stored both by cable and by segment of the cable.

// Hydrodynamic parameter for a cable
agxModel::WindAndWaterParametersRef hydrodynamicParameters = controller->
  getOrCreateHydrodynamicsParameters( cable );

// Aerodynamic parameters for a cable
agxModel::WindAndWaterParametersRef aerodynamicParameters = controller->
  getOrCreateAerodynamicsParameters( cable );

// Hydro- and aerodynamic parameters by segment of cable
agxCable::CableIterator iterator = cable->begin();
while ( !iterator.isEnd() )
{
  agxCollide::ShapeRef shape = iterator.getGeometry()->getShape();
  agxModel::WindAndWaterParametersRef aerodynamicParameters = controller->
    getOrCreateAerodynamicsParameters( shape );

  // Do something with the parameters

  iterator.advance();
}

The priority of parameters for a cable is as follows:

  1. If parameters are set for a segment these parameters will be used.

  2. If no parameters exists for the segment the parameters for the cable it belongs to will be used.

  3. If no parameters exists for the cable default parameters will be used.

For a shape, the hydro- and aerodynamic effects will be calculated for each triangle in a mesh. If it is a primitive shape, e.g. a sphere, the tessellation can be chosen to be low, medium, high or ultra high.

// Tessellation level set to hydro- or aerodynamic parameters
parameters->setShapeTessellationLevel(
  agxModel::WindAndWaterParameters::ShapeTessellation::ULTRA_HIGH );

For a wire or a cable, the segments for calculation are defined by the resolution.

The hydro- and aerodynamic coefficients of pressure drag, viscous drag and lift can be set in the parameters, as well as a scaling parameter for buoyancy. Note that lift will not be calculated for wires and cables due to symmetry.

// Change the pressure drag coefficient to 0.5
parameters->setCoefficient( agxModel::WindAndWaterParameters::PRESSURE_DRAG, 0.5 );

// Change the viscous drag coefficient to 0.5
parameters->setCoefficient( agxModel::WindAndWaterParameters::VISCOUS_DRAG, 0.5 );

// Change the lift coefficient to 0.5
parameters->setCoefficient( agxModel::WindAndWaterParameters::LIFT, 0.5 );

// Change the buoyancy scaling to 0.8
parameters->setCoefficient( agxModel::WindAndWaterParameters::BUOYANCY, 0.8 );

To change a coefficient for all shapes of a rigid body the following methods can be used:

//Setting the hydrodynamic lift coefficient to zero.
agxModel::WindAndWaterParameters::setHydrodynamicCoefficient( controller, rigidBody,
  agxModel::WindAndWaterParameters::LIFT, 0 );

//Setting the aerodynamic lift coefficient to zero.
agxModel::WindAndWaterParameters::setAerodynamicCoefficient( controller, rigidBody,
  agxModel::WindAndWaterParameters::LIFT, 0 );

The default values for the coefficients are specified in Table 22.1. Since the viscous drag becomes negligible compared to pressure drag with increasing velocity it might be a good idea to have the viscous drag coefficient decrease, and even become zero, at high velocities.

22.2. Hydrodynamics

To enable hydrodynamic calculations a water geometry has to be defined. The geometry has to contain exactly one shape that can be either a box, a plane, a height field or a sphere.

// Let the water geometry to be an box of size 60x60x30 meters.
agxCollide::ShapeRef waterShape = new agxCollide::Box( 30, 30, 15 );
agxCollide::GeometryRef waterGeometry = new agxCollide::Geometry( waterShape );

// Mark as water geometry. Contacts will be disabled for the water geometry, i.e.,
// no "normal" contacts will be created, only hydrodynamic forces.
agxModel::WindAndWaterControllerRef controller = new agxModel::WindAndWaterController();
controller->addWater( waterGeometry );

The default value for the water density is 1000 kg/m3. By assigning a material to the geometry this value can be changed.

// Create material to define the density of water.
agx::MaterialRef waterMaterial = new agx::Material( "waterMaterial" );
waterMaterial->getBulkMaterial()->setDensity( agx::Real( 900 ) );

waterGeometry->setMaterial( waterMaterial );

When a water geometry is defined all geometries, wires and cables that overlap the water will be subject to hydrodynamic calculations which consist of buoyancy, pressure drag, viscous drag and for geometries also lift.

The WindAndWaterController can contain several water geometries.

Examples of hydrodynamic simulations can be seen in tutorial_hydrodynamics.

22.2.1. Added mass

To enable added mass calculations for a shape, a file containing static data of the shape has to be read:

// Initiate added mass.
hydrodynamicParameters->initializeAddedMassStorage( "fileName.dat" );

If there is no file that matches the specified file name an analysis of the shape will be performed creating a file with that name. Note that this analysis can be very time consuming, but will only be performed the first time.

For a wire or a cable there will be no added mass calculation.

22.2.2. Enabling Added mass for a rigid body

When a body moves in a fluid (e.g. water), parts of the fluid has to move around the body. When the body accelerates, so must also the fluid. Therefore, the force required to give a body certain acceleration is larger than it would have to be in vacuum. AGX supports the notation of added mass. The added mass enters the equations of motion except for when calculating the gravitational force, i.e. the gravitational mass is unchanged. It is also used in the mass matrix when AGX solves for constraint forces. The added mass can be specified for each axis through the API calls:

rigidBody->getMassProperties()->setMassCoefficients( const Vec3& coefficients );
rigidBody->getMassProperties()->setInertiaTensorCoefficients( const Vec3& coefficients );

These coefficients will enter the velocity update equation in the \(C_A\) matrix. Given the 6x6 mass matrix \(M_0\) (linear mass and inertia) and the added mass coefficients \(C_A\), the velocity from the previous time step \(V_k\), the constraint Jacobian G and the Lagrange multipliers \(\lambda\), time step \(h\) and external forces (including gravity) \(f_e\) we write the velocity update as:

\(V_{k+1}=V_k+[(1+C_A ) M_0]^{-1} [G^T \lambda+hf_e ]\)

Gravity will be calculated with the original gravitational mass as: \(F_g = mg\), where \(m\) is the original mass.

Note that \(C_A\) matrix with the added mass coefficient is a scale of the linear mass \(M_0\).

22.2.3. Water flow

Water flow can be added by creating a water flow generator. The interface can be found in agxModel::WaterFlowGenerator.

/**
Interface for generating water flow and wind.
*/
class AGXMODEL_EXPORT WindAndWaterFlowGenerator : public agx::Referenced, public agxStream::Serializable
{
  public:
    /**
    Default constructor disables serialization. Any implementation
    has to enable this explicitly.
    */
    WindAndWaterFlowGenerator();

    /**
    Calculate and return wind at a given position in world.
    \param worldPoint - point in world
    \return wind velocity in world coordinates
    */
    virtual agx::Vec3 getVelocity( const agx::Vec3& worldPoint ) const = 0;
};
/**
Interface for generating water flow.
*/
class AGXMODEL_EXPORT WaterFlowGenerator : public WindAndWaterFlowGenerator
{
  public:
  /**
  Default constructor disables serialization. Any implementation
  has to enable this explicitly.
  */
  WaterFlowGenerator();
};

An example of how to implement a WaterFlowGenerator can be seen in tutorial_hydrodynamics.cpp.

A water flow created with an implementation of this interface can then be added to the controller.

// Add a water flow generator belonging to a given waterGeometry.
controller->setWaterFlowGenerator( waterGeometry, waterFlowGenerator );

If there are several water geometries in the simulation, the water flow generator has to be added for each geometry, otherwise that water geometry will have no water flow. It is also possible to have different water flow generators for each water geometry.

Use ConstantWaterFlowGenerator to create constant, uniform water flow.

// Create a constant water flow over a given water geometry
const agx::Vec3 flowVelocity( 5, 0, 0 );
agxModel::ConstantWaterFlowGenerator waterFlowGenerator =
  new agxModel::WaterFlowGenerator( flowVelocity );
controller->setWaterFlowGenerator( waterGeometry, waterFlowGenerator );

22.2.4. Water wrapper

The WaterWrapper is used to extract information about the water in the simulation based on the water geometry and, if present, a FlowGenerator. It is also possible to use it as a base class to be able to modify how some of this information is extracted. The water wrapper controls velocity, density and surface height of the water. For full extent, see WindAndWaterController.h.

The water wrapper can then be assigned to a water geometry in the WindAndWaterController.

windAndWaterController->setWaterWrapper( waterGeometry, waterWrapper );

For instance, the height of the water surface is by default considered to be the same as the surface of the water geometry. Overriding this enables the use of complicated water surfaces, such as waves, without the need of a dynamic height field. An example of this can be seen in tutorial_hydrodynamics.cpp.

Note

For the collision detection to work correctly it is important that the water surface never exceeds the extents of the water geometry.

22.2.5. Center of buoyancy

The center of buoyancy describes the center of mass of the fluid displaced by a geometry or body. It is also the point where the buoyancy forces should act. The center of buoyancy calculations are done separately from the buoyancy ones, and are automatically turned off to save some computational time. To enable the center of buoyancy calculations and get the value of it:

// Enable center of buoyancy for a geometry
windAndWaterController->setEnableCenterOfBuoyancy(geometry, true);
// Access center of buoyancy value. The value in the Vec3 will be overwritten with center of
// buoyancy, if there is any value to set. The method returns true when it has set the value.
agx::Vec3 centerOfBuoyancy;
bool valueWasSet = windAndWaterController->getCenterOfBuoyancy(geometry, centerOfBuoyancy);

Note that when a geometry or body is not in the water, there is no valid value for center of buoyancy, and the method to get it cannot set any value.

22.3. Aerodynamics

Aerodynamics can be enabled to get the effect of air resistance or wind. By default, these calculations are disabled but they can be enabled for any geometry, rigid body, wire or cable.

// Enable aerodynamics for a geometry.
controller->setEnableAerodynamics( geometry, true );

// Enable aerodynamics for a rigid body.
controller->setEnableAerodynamics( rigidBody, true );

// Enable aerodynamic calculations for a wire.
controller->setEnableAerodynamics( wire, true );

// For a cable aerodynamics can be enabled by cable or by segment
// Enable aerodynamics for a cable
controller->setEnableAerodynamics( cable, true );

// Enable aerodynamics for segment of a cable.
agxCable::CableIterator iterator = cable->begin();
while ( !iterator.isEnd() )
{
  agxCollide::GeometryRef geometry = iterator.getGeometry();
  controller->setEnableAerodynamics( geometry, true );

  iterator.advance();
}

It is also possible to enable aerodynamics for all objects in the simulation.

// Enable aerodynamics for all objects in the simulation
controller->setEnableAerodynamics( true );

Note that this can be time consuming. For performance reasons it is best to limit the objects enabled for aerodynamics.

The aerodynamic calculations consist of pressure drag, viscous drag and for geometries also lift. Added mass and buoyancy are omitted due to the low density of air.

The default value for air density is 1.2 kg/m3 and can be changed in the WindAndWaterController.

// Set the air density to 1.4 instead of the default value of 1.2
controller->setAirDensity( agx::Real( 1.4 ) );

22.3.1. Wind

Wind can be added to the simulation by creating a wind generator. The interface is found in agxModel::WindGenerator.

/*
Copyright 2007-2024. Algoryx Simulation AB.

All AGX source code, intellectual property, documentation, sample code,
tutorials, scene files and technical white papers, are copyrighted, proprietary
and confidential material of Algoryx Simulation AB. You may not download, read,
store, distribute, publish, copy or otherwise disseminate, use or expose this
material unless having a written signed agreement with Algoryx Simulation AB, or having been
advised so by Algoryx Simulation AB for a time limited evaluation, or having purchased a
valid commercial license from Algoryx Simulation AB.

Algoryx Simulation AB disclaims all responsibilities for loss or damage caused
from using this software, unless otherwise stated in written agreements with
Algoryx Simulation AB.
*/

/**
Interface for generating water flow and wind.
*/
class AGXMODEL_EXPORT WindAndWaterFlowGenerator : public agx::Referenced, public agxStream::Serializable
{
  public:
    /**
    Default constructor disables serialization. Any implementation
    has to enable this explicitly.
    */
    WindAndWaterFlowGenerator();

    /**
    Calculate and return wind at a given position in world.
    \param worldPoint - point in world
    \return wind velocity in world coordinates
    */
    virtual agx::Vec3 getVelocity( const agx::Vec3& worldPoint ) const;
};

/**
Interface for generating wind effects.
*/
class AGXMODEL_EXPORT WindGenerator : public WindAndWaterFlowGenerator
{
  public:
    /**
    Default constructor disables serialization. Any implementation
    has to enable this explicitly.
    */
    WindGenerator();

    /**
    Called before update of \p geometry.
    \param geometry - geometry to be updated
    */
    virtual void prepare( const agxCollide::Geometry* geometry );
};

A wind created with an implementation of this interface can then be added to the controller.

//Adding a windGenerator to the WindAndWaterController
  controller->setWindGenerator( windGenerator );

Use ContantWindGenerator to create a constant, uniform wind.

// Create a constant wind, with a given wind direction and magnitude.
const agx::Vec3 windVelocity = agx::Vec3( -15, 0, 0 );
agxModel::ConstantWindGeneratorRef windGenerator = new agxModel::ConstantWindGenerator( windVelocity );
controller->setWindGenerator( windGenerator );

An example of using a custom wind to create shadowing can be seen in tutorial_wind.cpp together with other examples. There is also an example of a windmill that is affected by aerodynamics in windmill.agxPy.

22.4. Wires and Cables

As explained above the hydro- and aerodynamic effects can be applied to wires with the effects of buoyancy, pressure drag and viscous drag. For a full example with wires and cables, see tutorial_wireWindAndWater.cpp and tutorial_cableWindAndWater.

22.5. Pressure field renderer

The pressure field renderer is used to visualize the pressure acting on a shape by the hydro- and aerodynamic forces.

// Create a pressure field renderer and give it to the controller
agx::Real scale = 1.01;
agxOSG::PressureFieldRendererRef pfr = new agxOSG::PressureFieldRenderer( root, scale );
controller->registerPressureFieldRenderer( pfr, shape );

The pressure field renderer is not available for wires and cables.

22.6. Known limitations

  • The object will not affect the water in any way. This means that:

    • An object will not be affected by another objects’ movement nearby.

    • Object movement in water will not produce waves.

  • All parts below the water surface will be subject to hydrodynamic calculations. This means that e.g. an empty bowl floating in water will experience a downward pressure from water if the inside of the bowl is beneath the surface level.

  • Any geometry or wire segment can only be affected by one water geometry at any given time.

  • Since these effects are calculated per shape, an object consisting of several shapes or geometries can lead to unwanted behavior.

  • Wires and cables are considered as lines with a thickness. This means that a segment crossing the water surface will have correct behavior but a segment laying along the water surface will not.