Working out skin friction drag from a flip sim
553 6 1-
- VortexVFX
- Member
- 64 posts
- Joined: Aug. 2014
- Offline
So, anyone here with a bit more of a formal physics/fluid-dynamics background than me?
I've been trying to roll my own high-accuracy buoyancy feedback setup (final force being a sum of locally-sampled forces on each face multiplied by their areas) - and so far have worked out how to apply the pressure-field-derived normal (buoyant) component correctly, and to some extent I've worked out how to apply the tangential skin friction drag component too... but for that, the skin friction coefficient requires a reynolds number, which is defined as: ρvx/μ - where fluid density, flow-speed and dynamic viscosity all seem straightforward enough, but trying to look up anything about x (characteristic length) just turns up answers like "it depends" and "the smallest scale that interesting behaviour happens over" and other empirical "measure it and see" style wooly stuff... but altering it seems to change the resulting drag force by entire orders of magnitude, so it seems like something that needs more than just shot-in-the-dark guesswork.
Also, the coefficient equation changes depending on whether it's laminar, transitional or turbulent flow, and even then, it seems like there are about as many different empirical equations for each as there are pages explaining it. I tried arbitrarily using the "Blasius solution": 0.664/sqrt(Re) for laminar flow but no idea which is most appropriate in the context of the flow at the surface of a floating object in a typical FLIP sim... or if I should be measuring the local turbulence and blending between them somehow.
In the specific context of a FLIP sim, with the information available (vel and pressure fields, etc.) - would anyone be able to explain to me what sort of numbers I should be shoving into a drag equation to actually model a vaguely physically correct locally-varying drag force on a (smooth) object floating in water?
For the time being, I've just set a uniform constant drag coefficient of 0.07, and it seems to work okay... but I'd like to get it closer to physically-correct if possible.
I've been trying to roll my own high-accuracy buoyancy feedback setup (final force being a sum of locally-sampled forces on each face multiplied by their areas) - and so far have worked out how to apply the pressure-field-derived normal (buoyant) component correctly, and to some extent I've worked out how to apply the tangential skin friction drag component too... but for that, the skin friction coefficient requires a reynolds number, which is defined as: ρvx/μ - where fluid density, flow-speed and dynamic viscosity all seem straightforward enough, but trying to look up anything about x (characteristic length) just turns up answers like "it depends" and "the smallest scale that interesting behaviour happens over" and other empirical "measure it and see" style wooly stuff... but altering it seems to change the resulting drag force by entire orders of magnitude, so it seems like something that needs more than just shot-in-the-dark guesswork.
Also, the coefficient equation changes depending on whether it's laminar, transitional or turbulent flow, and even then, it seems like there are about as many different empirical equations for each as there are pages explaining it. I tried arbitrarily using the "Blasius solution": 0.664/sqrt(Re) for laminar flow but no idea which is most appropriate in the context of the flow at the surface of a floating object in a typical FLIP sim... or if I should be measuring the local turbulence and blending between them somehow.
In the specific context of a FLIP sim, with the information available (vel and pressure fields, etc.) - would anyone be able to explain to me what sort of numbers I should be shoving into a drag equation to actually model a vaguely physically correct locally-varying drag force on a (smooth) object floating in water?
For the time being, I've just set a uniform constant drag coefficient of 0.07, and it seems to work okay... but I'd like to get it closer to physically-correct if possible.
Edited by VortexVFX - Sept. 16, 2026 17:16:52
Dan Wood
-
- Italimpex Productions
- Member
- 33 posts
- Joined: Aug. 2021
- Offline
Hello. Your calculations will inevitably swing by entire orders of magnitude or jitter if you map equations directly onto moving geo. I'm not sure if there's another way to get this to work without physics blowing up. Anyway, I prepared a demo scene to test proof of concept.
In Blasius solution, x represents the distance a fluid particle has traveled along a flat surface from the leading edge. For a moving object in 3D this will obviously become an issue. But here's the thing, since you are integrating forces locally per face area, your fluid solver can't resolve boundary layer behavior smaller than its own grid spacing. You basically completely substitute your length x with fluid simuluation's voxel size (for example 0.05, 0.1, 0.2). It will keep Reynolds number calc stabilized within a realistic range like 1k-500k.
So how I approached this is with RBD bullet. I first wanted to create a rigid body object like a streamlined sphere and let it fall on the grid and interact with it. Thing with bullet is that it doesn't pass v attribute so we calculate velocity with trail sop. We'll need it for calculations later.
Then I wanted some water movement to sample a vel field from. I created a quick FLIP tank sim and got velocities out of it. Then used volume rasterize attributes node to create a vel vector field.
These two components are enough for following calculations with VEX. Because we will be running this over primitives, @P automatically represents face center and @N represents face normal vector. First we set up some environment constants like water height, sample vel from second input, rho as water density, mu as viscosity and voxel size (particle separation * grid scale). Then we run a submersion check to see if sphere is below waterline. Then with primpoints() we extract the array containing all points per primitive and sample index 0 from points that had their velocities computed with Trail. Bullet solver operates based on transform matrices and doesn't calculate primitive velocity values.
Skin friction drag can't exist in a vacuum and it acts exclusively parallel to a surface. I isolated head on impact velocity (normal_speed) by projecting relative velocity onto a face normal. Then subtracting that normal component (how much does velocity lie in normal dir) leaves us with a tangential sliding vector representing fluid scraping along the skin. Then I clamped that speed for stability and filled in a Reynolds formula. At the end I calculated skin friction coefficient Cf by blending parabolic Blasius solution for smooth laminar flow and Prandtl power law exponent curve for high velocity turbulent flow. And again I clamped values for stability.
I've attached a hip file along with some references. First image is friction coefficient in water tank, second image is sliding speed, and a clip is reynolds num. On the flanks where water squeezes past the hull sliding speed spikes. Because velocity is in the denominator of the friction coefficient equation, friction coeff drops to its lowest active decimals there. Reynolds number tracks fluid intertia.
I hope it helps!
In Blasius solution, x represents the distance a fluid particle has traveled along a flat surface from the leading edge. For a moving object in 3D this will obviously become an issue. But here's the thing, since you are integrating forces locally per face area, your fluid solver can't resolve boundary layer behavior smaller than its own grid spacing. You basically completely substitute your length x with fluid simuluation's voxel size (for example 0.05, 0.1, 0.2). It will keep Reynolds number calc stabilized within a realistic range like 1k-500k.
So how I approached this is with RBD bullet. I first wanted to create a rigid body object like a streamlined sphere and let it fall on the grid and interact with it. Thing with bullet is that it doesn't pass v attribute so we calculate velocity with trail sop. We'll need it for calculations later.
Then I wanted some water movement to sample a vel field from. I created a quick FLIP tank sim and got velocities out of it. Then used volume rasterize attributes node to create a vel vector field.
These two components are enough for following calculations with VEX. Because we will be running this over primitives, @P automatically represents face center and @N represents face normal vector. First we set up some environment constants like water height, sample vel from second input, rho as water density, mu as viscosity and voxel size (particle separation * grid scale). Then we run a submersion check to see if sphere is below waterline. Then with primpoints() we extract the array containing all points per primitive and sample index 0 from points that had their velocities computed with Trail. Bullet solver operates based on transform matrices and doesn't calculate primitive velocity values.
Skin friction drag can't exist in a vacuum and it acts exclusively parallel to a surface. I isolated head on impact velocity (normal_speed) by projecting relative velocity onto a face normal. Then subtracting that normal component (how much does velocity lie in normal dir) leaves us with a tangential sliding vector representing fluid scraping along the skin. Then I clamped that speed for stability and filled in a Reynolds formula. At the end I calculated skin friction coefficient Cf by blending parabolic Blasius solution for smooth laminar flow and Prandtl power law exponent curve for high velocity turbulent flow. And again I clamped values for stability.
I've attached a hip file along with some references. First image is friction coefficient in water tank, second image is sliding speed, and a clip is reynolds num. On the flanks where water squeezes past the hull sliding speed spikes. Because velocity is in the denominator of the friction coefficient equation, friction coeff drops to its lowest active decimals there. Reynolds number tracks fluid intertia.
I hope it helps!
Italimpex Productions
CGI Studio specializing in directing and producing advertisements & films
CGI Studio specializing in directing and producing advertisements & films
-
- VortexVFX
- Member
- 64 posts
- Joined: Aug. 2014
- Offline
Big thanks for taking the time to set this up. It all seems pretty in-line with what I got going already (so a very welcome sanity-check that I haven't been barking up completely the wrong trees
) - the key bit of info I was missing was using the voxel size as the characteristic length.
So the hard-switch between the two equations:
- should be enough? (I actually doubt the sims I'm running are going to have velocities high enough to trigger the turbulent one anyway)
I'd seen 1.332 / sqrt(Re) in some places, and quite a lot of variants for the turbulent one, so wasn't sure which to choose... but advice seems to lean towards "something in the right ballpark is enough".
I've actually got a bit of an over-engineered setup, where I'm inputting bullet compound-colliders, but then using their transforms and solver data to set up separate matching surface meshes for the buoyancy calculations, plus another fluid-particle-filled proxy for each rigid object... then I'm running two FLIP passes, one with the rigid colliders as a vdb (the main sim) then a secondary copy that replaces them with fluid particles with the correct density values (the idea being that if they're re-injected each step, they wouldn't deform much from their original shape, and it'd allow the pressure and velocity fields to more "correctly" extrapolate inside the objects, so that the sampling at the face centers would avoid any potential aliasing. It then stores that info as detail attribs, resets back to the first-pass output, and then applies the forces to the RBD objects using some python on the next step.
It seems to work pretty well - it's been absolutely stable in everything I've tried with it so far, but I was just stuck on making sense of the Reynolds numbers.
All that said, I'm beginning to suspect the two-pass thing might just be massively over-complicating things and doing a lot of unnecessary work twice - now I've got everything stacking up, I'm probably going to try stripping that down and just seeing how it behaves with the regular flip solver outputs.
) - the key bit of info I was missing was using the voxel size as the characteristic length.So the hard-switch between the two equations:
if (Re < 500000.0){ Cf = 0.664 / sqrt(Re); // laminar flow }else{ Cf = 0.0592 / pow(Re, 0.2); // turbulent flow }
I'd seen 1.332 / sqrt(Re) in some places, and quite a lot of variants for the turbulent one, so wasn't sure which to choose... but advice seems to lean towards "something in the right ballpark is enough".
I've actually got a bit of an over-engineered setup, where I'm inputting bullet compound-colliders, but then using their transforms and solver data to set up separate matching surface meshes for the buoyancy calculations, plus another fluid-particle-filled proxy for each rigid object... then I'm running two FLIP passes, one with the rigid colliders as a vdb (the main sim) then a secondary copy that replaces them with fluid particles with the correct density values (the idea being that if they're re-injected each step, they wouldn't deform much from their original shape, and it'd allow the pressure and velocity fields to more "correctly" extrapolate inside the objects, so that the sampling at the face centers would avoid any potential aliasing. It then stores that info as detail attribs, resets back to the first-pass output, and then applies the forces to the RBD objects using some python on the next step.
It seems to work pretty well - it's been absolutely stable in everything I've tried with it so far, but I was just stuck on making sense of the Reynolds numbers.
All that said, I'm beginning to suspect the two-pass thing might just be massively over-complicating things and doing a lot of unnecessary work twice - now I've got everything stacking up, I'm probably going to try stripping that down and just seeing how it behaves with the regular flip solver outputs.
Edited by VortexVFX - Sept. 17, 2026 01:40:20
Dan Wood
-
- VortexVFX
- Member
- 64 posts
- Joined: Aug. 2014
- Offline
Hmm, now I'm wondering if skin friction actually contributes enough to be noticeable at these kind of scales/velocities...
The problem I had before putting a drag force in was smooth regular objects (spheres, toruses/tori?, etc) would end up spinning endlessly while bobbing on the water surface. With the characteristic length set to the voxel size, and resulting Reynolds numbers in the 500-50,000 range, it yields absolutely tiny drag forces in a 3x3m water tank, and the spinning still happens. If I just jam in a ~0.3 additional drag coefficient, the spinning stops and the objects start to behave in a believable-looking way.
I suppose at this scale, more significant drag might be coming from adhesion/surface tension stickiness... which I'm not sure where to start modelling realistically. I suppose at this point I should probably just implement as an "artistic" sticktion coefficient.
The problem I had before putting a drag force in was smooth regular objects (spheres, toruses/tori?, etc) would end up spinning endlessly while bobbing on the water surface. With the characteristic length set to the voxel size, and resulting Reynolds numbers in the 500-50,000 range, it yields absolutely tiny drag forces in a 3x3m water tank, and the spinning still happens. If I just jam in a ~0.3 additional drag coefficient, the spinning stops and the objects start to behave in a believable-looking way.
I suppose at this scale, more significant drag might be coming from adhesion/surface tension stickiness... which I'm not sure where to start modelling realistically. I suppose at this point I should probably just implement as an "artistic" sticktion coefficient.
Edited by VortexVFX - Sept. 17, 2026 04:38:32
Dan Wood
-
- VortexVFX
- Member
- 64 posts
- Joined: Aug. 2014
- Offline
Hooray! Finally solved the spinning issue. The skin friction is a good element to have solved, but as it turns out it wasn't what was causing the problem. I was applying the buoyancy forces to the bullet bodies using a Point Force, and letting that work out the linear and torque components.
Realised that of course, the Bullet solver is running its own multiple timesteps per timestep, and so presumably that point force was only correct at the start of each external timestep. As the body's COM moved, the point force would get left behind, so it would end up inducing a phantom torque behind the body. Switched to a Uniform Force and working out the force and torque components myself, and now it's finally behaving properly and stable even without any artificial drag forces or fluid velocity blending.
Realised that of course, the Bullet solver is running its own multiple timesteps per timestep, and so presumably that point force was only correct at the start of each external timestep. As the body's COM moved, the point force would get left behind, so it would end up inducing a phantom torque behind the body. Switched to a Uniform Force and working out the force and torque components myself, and now it's finally behaving properly and stable even without any artificial drag forces or fluid velocity blending.
Edited by VortexVFX - Sept. 20, 2026 08:29:56
Dan Wood
-
- Italimpex Productions
- Member
- 33 posts
- Joined: Aug. 2021
- Offline
That's great! Thanks for updating me on this. On a side note, if you didn't intend to have these water explosions that propell toruses upward, make sure rigid body solver is plugged into the first input of merge node, and flip solver into the second (assuming you're combining flip and rigid body solvers in the same dopnet).
Also did you use RBD or Bullet solver engine? I saw somewhere that bullet doesn't support mutual affection between flip and rbd but I'm not sure if that's the case. It worked for me. I simply used default bullet on rigid body solver, didn't change any parameters. Also, plugged in a flip solver making sure it's second input of merge node. Merge set to mutual affector relationship. What made toruses behave like lifebuoys is changing Volume Motion > Solver > Feedback Scale set to 0.2 on flip solver, along with setting toruses' density to 200 to somewhat replicate real world values. And while I was iterating on the look I followed these ratios: density 200 and feedback scale 0.2, density 500 and feedback scale 0.5, density 1000 and feedback scale 0.5. I stuck with 200 and 0.2. I avoided similar water explosions with this bare bones feedback scale method.
Also did you use RBD or Bullet solver engine? I saw somewhere that bullet doesn't support mutual affection between flip and rbd but I'm not sure if that's the case. It worked for me. I simply used default bullet on rigid body solver, didn't change any parameters. Also, plugged in a flip solver making sure it's second input of merge node. Merge set to mutual affector relationship. What made toruses behave like lifebuoys is changing Volume Motion > Solver > Feedback Scale set to 0.2 on flip solver, along with setting toruses' density to 200 to somewhat replicate real world values. And while I was iterating on the look I followed these ratios: density 200 and feedback scale 0.2, density 500 and feedback scale 0.5, density 1000 and feedback scale 0.5. I stuck with 200 and 0.2. I avoided similar water explosions with this bare bones feedback scale method.
Edited by Italimpex Productions - Sept. 20, 2026 15:11:35
Italimpex Productions
CGI Studio specializing in directing and producing advertisements & films
CGI Studio specializing in directing and producing advertisements & films
-
- VortexVFX
- Member
- 64 posts
- Joined: Aug. 2014
- Offline
Ah, no, the big initial explosions are intentional - I left some gaps in the fluid for it to crash into, so I could stress test it with some reasonably violent motion.
I'm not using any of Houdini's default feedback here - the FLIP and Bullet solvers are completely decoupled (while still running in parallel in the same DOP loop), and I built my own feedback system to calculate the buoyancy, which is now using accurate physical values - the bodies are set to 50 density in that test, and it's using the FLIP output pressure field 1:1 to generate per-face forces, and then uses a script solver to edit the uniform force attached to each Bullet body. On the FLIP side, I'm importing the rigid body transform data and using it to generate the collision/collisionvel fields directly from high res meshes (while the bullet bodies themselves are using compound capsule colliders).
I'm not using any of Houdini's default feedback here - the FLIP and Bullet solvers are completely decoupled (while still running in parallel in the same DOP loop), and I built my own feedback system to calculate the buoyancy, which is now using accurate physical values - the bodies are set to 50 density in that test, and it's using the FLIP output pressure field 1:1 to generate per-face forces, and then uses a script solver to edit the uniform force attached to each Bullet body. On the FLIP side, I'm importing the rigid body transform data and using it to generate the collision/collisionvel fields directly from high res meshes (while the bullet bodies themselves are using compound capsule colliders).
Dan Wood
-
- Quick Links

