Free-Surface Fluid
A fluid in 2D is cut into
The scheme is the one of Gallouët and Mérigot (A Lagrangian scheme à la Brenier for the incompressible Euler equations, 2018), with partial transport for the free surface, as in B. Lévy's free-surface version (2022). The Diracs carry the matter and its velocity. The pressure is a spring that pulls each Dirac towards the barycenter
The spring derives from the potential
oscillates, but does not drift. Each step solves one transport, started from the weights of the previous step:
problem = sdot.OtProblem( sdot.SumOfDiracs( p, masses, target_mass = None ), domain )
for step in range( nb_steps ):
v += 0.5 * dt * a
p += dt * v
problem.source = sdot.SumOfDiracs( p, masses, target_mass = None ) # target_mass = None: the masses as given
b = np.asarray( problem.solve().barycenters )
a = g + ( b - p ) / eps**2
v += 0.5 * dt * aWith target_mass = None, the source weighs the sum of its masses, which is less than the domain: the transport is partial. The walls need nothing more. A barycenter stays inside the domain, so a Dirac that leaves it is pulled back in.
The surface
As it is, nothing in this energy costs a VOID. With fixed masses and the air at weight 0, the partial transport is the projection onto the densities sphere_areas, an output of the plan, is differentiable like the others:
def area( p ):
plan = sdot.OtProblem( sdot.SumOfDiracs( p, masses, target_mass = None ), domain ).solve()
return jnp.sum( plan.sphere_areas ), plan.barycenters # the length of the arcs in 2D, the area of the caps in 3D
( A, b ), grad_A = jax.value_and_grad( area, has_aux = True )( p )
a = g + ( b - p ) / eps**2 - sigma * grad_A / masses[ :, None ]The force derives from an energy:
In 2D the length of the arcs does not do: where a circle touches a facet, the length of the arc grows like a square root (
wider = sdot.PowerDiagram( p, plan.weights, distribution = domain, sink_weight = - c )
A = jnp.sum( wider.measures ) - jnp.sum( masses )With
The fluid starts AT REST, where the springs hold the forces:
Dam break
A column of 740 particles, 0.5 wide and 0.8 high, against the left wall of a
Each colored piece is what a particle holds: the part of its Laguerre cell inside its ball.
In 3D
The same scheme, with a 3D partial transport: the cells are polyhedra cut by balls. The volumes, barycenters and derivatives of these intersections are exact, as in 2D. A column of 200 particles, 0.5 wide, 0.8 high and 0.5 deep, in a
The pages are written by docs/examples/free_surface_fluid.py.