This is a somewhat very specific corner case, which so far I've only been able to trigger this in the following conditions:

1. If the mesh has cells that let the particles travel in reverse;
2. And if a particle starts exactly from the centre of a cell;
3. And if the particle happens to travel in a critical direction.

Then that particle gets stuck traveling back and forth between the same group of tets, blocking the solver in an infinite loop at 'Foam::particle::trackToFace'.

More specifically regarding condition 1, the provided test case demonstrates a situation where a polyhedral cell decomposes into tetrahedral cells in such a way that one of those tets has a negative determinant, which leads the particle getting stuck in an infinite travel loop between tets... essentially because it was meant to travel through that tet and instead is sent back to where it started from.

I have studied the code to try and fix this, but the only way I've been able to break the infinite loop is to count and limit for how long the particle stays in the same exact place while traveling between several tets in method 'Foam::particle::trackToFace', e.g. if it travels between 10 tets and never leaves the same position, then break the loop. Once it exits the method, it reports back that the tracking had to be canceled and is then handled accordingly by the caller method.

Another possible workaround was to try and change the precision of the position of the particle (because in the tests I've done I start from the centre of the cells), but it does not always work, since it can still get stuck when traveling in another direction.

I did not manage to understand if removing negative determinant tets from the search space would somewhat solve this issue, because when setting the debug flag for 'particle', we can see the tet-particles it travels in-between and doesn't seem to always land on the negative tets...

Steps To Reproduce

The attached package 'particleTrackingTestCase_v1.tar.gz' provides the case 'particleTrackingTestCase', which allows to reproduce the error, as well as demonstrates a workaround that happened to work. The mesh is a biopsy with 90 cells from a much larger mesh on which this was first found.
The large mesh works just fine with the existing flaws, both for fluid flow and conventional particle transport, but breaks the barycentric algorithm on this specific scenario (and with other directions).

Scripts for running the case:

* To run the case in a way that gets the particle stuck in a travel loop, run the script 'Allrun.infiniteLoop'.
* To clean up the case, run 'Allclean'.
* To run the case without problems, run 'Allrun.runs'.

The only difference is that the write precision is set to 16 on the infinite loop and 8 for running without problems.

The work flow of the case is as follows:

1. The flow field is as good as frozen, with flow velocity set to 0 on the whole field and with no pressure gradients.
2. The solver 'pisoFoam' is used to run for 2 seconds, with deltaT set to 1, writing every 1 second.
3. The function object 'icoUncoupledKinematicCloud' is used to transport the particles for just one time step.
4. The particles are injected with the injector 'manualInjection', with the positions file set to 'C' and with a fixed velocity vector.
5. The 'C' file is generated with "postProcess -func writeCellCentres" and then manipulated with the 'sed' command (done by the 'Allrun.*' scripts).

You can add the following block to 'controlDict' to get a log of where the particles travel through:

DebugSwitches
{
particle 1;
}

With or without that flag, the following warning is repeated several times until it silences it:

First reproduced this issue with OpenFOAM 5.x, but also occurs with the latest OpenFOAM 6 (b38715c4b19) and OpenFOAM dev (8ed92de98).

This was only possible to trigger because of the runs done with an in-house solver that does a sort of ray-tracing with OpenFOAM's particle transport algorithms. It was this way that we tripped over an old bug when running in parallel and then we provided the bug fix for it in report #2315 for the old transport algorithm.

Activities

> Then that particle gets stuck traveling back and forth between the same group
> of tets, blocking the solver in an infinite loop at
> 'Foam::particle::trackToFace'.

You have found the fundamental problem with tracking through negative space; that it opens the possibility of closed loops which never terminate.

> I have studied the code to try and fix this, but the only way I've been able
> to break the infinite loop is to count and limit for how long the particle
> stays in the same exact place while travelling between several tets in method
> 'Foam::particle::trackToFace',

I have avoided counters thus far as that sort of logic has masked all sorts of sins in the past, and also because these loops can cross between cyclics/AMI-s/processors/..., so the counter actually needs to be stored by the particle, which increases storage.

The hack that is in there at the moment is a factor which artificially decreases the (negative) amount of time that a particle spends in negative space. This means that as a particle moves around a loop the positive and negative space fractions don't cancel out and the track terminates. This isn't really any better than a counter in that the particle still ends up in the wrong place, but it does at least avoid additional storage.

Your case hangs despite this protection because you injected exactly at the cell centre, so the loop is of zero size, so even with the factor there is no difference in the time-accumulated in positive and negative space (they are both zero).

> I did not manage to understand if removing negative determinant tets from the
> search space would somewhat solve this issue, because when setting the debug
> flag for 'particle', we can see the tet-particles it travels in-between and
> doesn't seem to always land on the negative tets...

This isn't guaranteed to help. A particle can be injected into a positive tet and still subsequently enter one of these loops.

...

I'm happy to try a counter. That will fix your problem, and it should put a stop to these sorts of hangs (the time-hack doesn't really work on moving meshes either). The exact break condition will need a bit of care to write and locate within the various loops, if it is to catch all eventualities.

I would still like to come up with some means of fixing the deficit of time-not-tracked as a result of abandoned tracks, even if that means more storage. That issue can be considered separately from this one, though.

I still have to double-check what's going on when I run the case in parallel, because with the original case it locks up at the very first time step.
I'll try to diagnose the parallel case as soon as I can, to assess if the same issue is occurring and resulting in the prolonged/endless loop through a processor patch. Because if this is the situation, then that way we should have the two test cases needed for double-checking if the counter mechanism always works in serial and parallel modes.

This patch is my attempt at fixing this with a counter. It applies to dev 3a7b7619. I will run most of the Lagrangian tutorials to completion over the weekend to make sure that it doesn't introduce any other failure mechanisms, and I'll push it next week if all goes well. I thought the patch might be appreciated in the meantime, should you want run any more of your own tests.

You might notice that it wasn't enough just to add a counter. If it was a steady tracking case, or something with long time-steps, just cutting off after a fixed number of inverted tetrahedra might stop valid tracks prematurely. The logic I decided on was to cut off after a number of steps behind the maximum currently achieved step fraction. Once the particle achieves a new maximum step fraction the counter resets. This means it is only when we are not progressing do we stop the track.

Unfortunately, this meant adding storage for the maximum currently achieved step fraction. I can't think of another way which avoids this extra storage. I would be very pleased if someone else did.

Fix pushed to dev as 1c26ed2d. The particleTrackingTestCase_v1.tar.gz case now runs, and will spit a warning to indicate that a particle got stuck.

The fix is roughly as outlined in my earlier commit, except that it is working with a maximum achieved distance, rather than the step fraction. Often we track without changing the step fraction, so this change was needed to fix those cases, too. All post-processing tracks are done this way, so that the mesh doesn't move. The pros and cons of the change haven't changed; the implementation is similar, and the memory usage is identical.

I also cleaned up the warnings a bit. Rather than printing N times and then going silent, they now print only when the particle or tet ID is different from last time. I think this is a bit more log-friendly.