doyubkim/fluid-engine-dev

Moving particle emitter problems

kentbarber opened this issue ยท 25 comments

I have found that if I move the Surface3 for a particle emitter (used in FLIP or PIC) then the particles don't get emitted.

I have tracked this down to the region BoundingBox calculated in VolumeParticleEmitter3::emit(...)

Specifically this code here

BoundingBox3D region = _bounds; if (_implicitSurface->isBounded()) { BoundingBox3D surfaceBBox = _implicitSurface->boundingBox(); region.lowerCorner = max(region.lowerCorner, surfaceBBox.lowerCorner); region.upperCorner = min(region.upperCorner, surfaceBBox.upperCorner); }

Since my moving emitter is an implicit surface created from a Sphere this code runs. It then calculates the lowerCorner of my box to be greater than the upperCorner.

This means that in the forEachPoint method

_pointsGen->forEachPoint(...)

It can't loop over the points in the box because lower.x > upper.x. Which means that this

double boxWidth = boundingBox.width();

Produces a negative number, so no points are emitted.

It is possible if you provided tight maxRegion to the VolumeParticleEmitter3 instance.

One option is to provide bigger maxRegion such as the whole bounding box of the solver to the emitter. Here's a code snippet from one of the Python examples:

sphere = Sphere3(center=(0.5, 1.0, 0.5), radius=0.15)
emitter = VolumeParticleEmitter3(
    implicitSurface=sphere,
    maxRegion=solver.gridSystemData.boundingBox,
    spacing=1.0 / (2 * resX),
    isOneShot=False,
    initialVelocity=(0, 0, 0))
solver.particleEmitter = emitter

Note that the code above only works when the implicitSurface is bounded, meaning we know its bounding box analytically such as a sphere. This can be checked by calling isBounded function. In case isBounded returns false, you need to move the maxRegion every frame before updating the solver by calling setMaxRegion.

Let me know if this helps!!

I am already passing in the bounding box of the solver to the emitter. The issue seems to be when the bounding box and sphere are positioned negatively.

Solver bbox is lower(1,-1,-1) upper(3,1,1).
Sphere bbox is lower(1.67,-0.5,-1.08), upper(2.67,0.5,-0.08)

The logic of the min/max to calculate the actual bounding box to test if a particle is inside may not be correct. At least that is where I am looking into now.

Huh, it does sound like a bug for sure! Let me take a look more into it. Thanks for providing a test case! :D

My hunch is that the boundbox test for width, height, depth etc... doesn't take into account if the numbers are negative. But I don't want to change these since it will impact everything else. Maybe just need to change the forEachPoint methods instead.

I am seeing a similar issue for SPH where particles are only emitted if the solver and sphere in positive space. Anywhere in negative space no particles are emitted.

Forgot to mention that the SPH issue happens even when the emitter is not moving.

But with the FLIP solver when the emitter is not moving (ie Surface3 is not being translated) then it emits fine. But when it is moving, when I translate the Surface3 before the solver update, then it doesn't emit.

Sorry I may be mixing up multiple issues here. But my main issue right now is the moving emitters. Hope some of this helps you hunt down the problem. Really enjoying working with the engine again.

No problem at all! Thanks for reporting the issues you are having. It will make the engine more robust.

Quick question: what were the emitter spacing parameter for both FLIP and SPH simulations? Trying to replicate the bug in more compact unit test-like code.

FLIP

  • Solver size is 2x2x2
  • Solver Resolution (50,50,50)
  • Solver Grid Spacing (0.04)
  • Solver offset (-1,-1,-1)
  • VolumeParticleEmitter3 withMaxRegion = same as Solver size so should be (2,2,2).
  • VolumeParticleEmitter3 withSpacing = 0.02 (half the grid spacing)

SPH
Will update this part tomorrow with the details.

Thanks for the info @kentbarber!

I made a simple test based on your setup in an attempt to reproduce the bug:

auto sphere = std::make_shared<SurfaceToImplicit3>(
    std::make_shared<Sphere3>(Vector3D(2.17, 0.0, -0.58), 0.5));

BoundingBox3D box({1.0, -1.0, -1.0}, {3.0, 1.0, 1.0});

VolumeParticleEmitter3 emitter =
        VolumeParticleEmitter3::builder()
                .withSurface(sphere)
                .withMaxRegion(box)
                .withSpacing(0.02)
                .withIsOneShot(false)
                .withAllowOverlapping(true)
                .build();

auto particles = std::make_shared<ParticleSystemData3>();
emitter.setTarget(particles);

Frame frame(0, 1.0);
emitter.update(frame.timeInSeconds(), frame.timeIntervalInSeconds);

// numberOfParticles is 128893
EXPECT_GT(particles->numberOfParticles(), 0u);

but it does generate enough particles even in the negative region. Can you spot anything different from your setup?

AllowOverLapping is set to the default, so it will be false. And the other differences is that the sphere is being translated every update. This is the main difference, the sphere is moving. When its moving it doesn't emit particles. When it's static it does.

I will see if I can produce a cut down version of the code that reproduces the issue for you tomorrow.

Ah, that's right. Does it stop emitting particles immediately when the sphere starts moving, or only when it enters the negative region? Thanks for helping me out on the bug hunting!

It would be also helpful to see how you are setting the emitter.surface.transform.

In this gif you can see the sphere moving along the spline. The one where it emits particles is FLIP. The one where there are no particles emitted is PIC ( the other two are grid based LevelSet and Smoke, which work perfectly as you can see).

emitter is being changed by just keeping a pointer to the Surface3 and changing it directly before the solver->Update method is called.

surf->transform.setOrientation(jet::QuaternionD(quat.w, -quat.v.x, -quat.v.y, -quat.v.z));
surf->transform.setTranslation(jet::Vector3D(p.x, p.y, p.z));

I will spend some more time on it tonight to see if I can hunt down what the issue might be. Just seems too strange to me and maybe I am doing something wrong.

But as I mentioned before, if the emitter isn't moving, then the particles emit fine from a static sphere emitter in each of those boxes.

multi_fluid_test

There center of this is the origin (0,0,0). The Red axis is X, Blue axis is Z.

Thanks for more information! I will continue looking at this as well. Something obvious seems wrong.

I also have the following set for the emitters, which is the same as in your examples files.

emitter->setPointGenerator(std::make_sharedjet::GridPointGenerator3());

I noticed above that you didn't set this, so in your test you would have been using BccLatticePointGenerator.

Incredible find @kentbarber !! Let me work on the fix.

Last night I found a way around the problems I am having. But I haven't yet tracked down the proper solution.

I have a surface, converted to a ImplicitSurface3, this is added to a vector.
The vector is then added to an ImplicitSurfaceSet3. This set is then used in the emitter.

If I transform the original Surfaces transform then nothing happens. But if I change the transform of the ImplicitSurfaceSet3 itself then it works as intended. Although this is not the real fix since it will only then work for a single surface. Will have another look again tonight, might be something related to BoundBox calculations or the BVH.

I think I may just have to retain a pointer to the ImpicitSurfaceSet3 and call invalidateBvh() each frame on it. Will try that tonight.

Incredible find @kentbarber !! Let me work on the fix.

I changed my post. Please have another read. My initial reply was incorrect.

Ok thanks for the update!

Making ImpicitSurfaceSet3::invalidateBvh() public and calling it after changing any child surface seems to work for FLIP. I will look into SPH another day and open a new issue if that has problems.

@kentbarber I was also heading to the same route. I also have a compact repro case as well. Thanks for tracking this!

I just created a branch with a potential fix! It would be great if you can test your code with this branch.

@kentbarber please let me know if the master branch works for you. Should be fixed now.

Yes the master branch works for me now with this fix for implicit surfaces. But I am still unable to move Triangle3 surfaces. But I will open a separate issue for this at a later time.

Thanks for confirming @kentbarber. Yes, please feel free to open the issue. I will start looking for a solution as well. The dirty flag approach you suggested in the PR seems the best option so far.