View Issue Details

IDProjectCategoryView StatusLast Update
0003494OpenFOAMFeaturepublic2020-05-16 08:31
Reporterrmoreira Assigned Tohenry  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionopen 
PlatformGNU/LinuxOSUbuntuOS Version16.04
Product Versiondev 
Summary0003494: Wrong displacement accumulation in motionSolverList
DescriptionThe dynamicFvMesh type named dynamicMotionSolverFvMesh produces unexpected mesh displacement when two or more motion solvers with non-zero displacement are included in dynamicMeshDict. The explanation would be an incorrect displacement value attribution in the code in motionSolverList.C.
Steps To ReproduceApplying two or more different solvers in dynamicMeshDict with non-zero displacement components may already cause the issue. The attatched example case illustrates the displacement miscalculation, and can be run by tiping:

1. blockMesh
2. moveDynamicMesh
3. paraFoam

In the attatched file case, which presents constant mesh movement speed for both x and y axis, points in the mesh should be moved 0.2 m in both x and y directions for every 1 s time step. Instead, the mesh is moved in the expected direction and with desired displacement variation, but only in alternated time steps. During the other time steps, the mesh is kept almost unchanged, hence halving the expected total displacement for a larger number of time steps. In more complex movement cases, the mesh is moved in more unpredictable ways.
Additional InformationThe problem was found to be in the first Member Function of the code motionSovlerList.C (located in src/dynamicFvMesh/motionSolvers/motionSolverList), more precisely at lines 73 to 80, as shown below:

        pointField disp(motionSolvers_[0].curPoints() - mesh().points());

        for (label i = 1; i < motionSolvers_.size(); i++)
        {
            disp += motionSolvers_[i].curPoints() - mesh().points();
        }

        return mesh().points() + disp;

For every motion solver, the code extract the difference between its result and the current mesh, sum all the results and add that value of displacement to the current mesh points, returning then the new mesh position. The problem is that every motion solver moves the mesh independently of other solver and its result is the respective displacement in relation to the initial time instant. Thus subtracting its result from the current mesh (which is a combination of the movement of all solvers) produce an incorrect mesh displacement. A possible correction would be adding a pointField type variable (hereby named initialMesh) to which the mesh point coordinates of the initial time instant of the simulation is atributed. The new code would be:

        pointField disp(motionSolvers_[0].curPoints() - initialMesh);

        for (label i = 1; i < motionSolvers_.size(); i++)
        {
            disp += motionSolvers_[i].curPoints() - initialMesh;
        }

        return initialMesh + disp;
TagsNo tags attached.

Activities

rmoreira

2020-05-12 03:16

reporter  

example_Case.tar.gz (1,767 bytes)

henry

2020-05-12 10:08

manager   ~0011338

Last edited: 2020-05-12 10:42

Description
    Motion of the mesh specified as a list of motion solvers.

    The motion solvers are executed in order and the resulting displacements
    accumulated into an overall displacement and the displaced point positions
    returned.

i.e. it is assumed that the regions of motion are independent and no attempt at averaging or combining the motions in a more complex way is made.

Various ideas at a more general motion combination approach have been considered but none are general enough; the averaging is likely to be dependent on the form and region of the various motions in the list.
If you have multiple motions which interact you will need to decide how they should be combined and write a specific version of motionSolverList which does what you want.

In the example you provided the two regions of motion do overlap and the simple summation of displacement is not appropriate.
While your proposal is makes sense for the displacement solvers it is not correct for the more general velocity solvers and it is not clear how it would resolve the issue of interacting motions, could you demonstrate the advantage?

rmoreira

2020-05-12 17:38

reporter   ~0011339

The current motion accumulation only doesn't work fine for movements that overlap, I forgot to mention that. Providing a common reference (the initial mesh) for the displacement calculation, would create a more intuitive and predictable way (linear combination in every timestep) to implement movement combinations since, at the end, the motionSolverList combine the displacements generated by every solver, regardless of its nature. And, at the same time, the only modified solution would be for overlaping motion cases.

I believe another option would be taking the mesh result of the previous solver in the list and using it as the "0" reference for the next solver, thus applying sequantialy the mesh motions, but I guess that is not how the motion solvers work.

henry

2020-05-12 17:45

manager   ~0011340

Could you demonstrate your proposal? I don't see how the change would help and it would only make sense for non-overlapping displacement solvers whereas the current implementation work for any non-overlapping motion solvers.

rmoreira

2020-05-14 18:15

reporter   ~0011342

Consider one point in the mesh with coordinates (0,0) for simplicity. Consider also two motion solver moving it independently (Solvers 1 and 2). Solver 1 moves the point 1 length unit in the x direction per time step, meanwhile, Solver 2 moves it 2 length units in the y direction per time step.

The current motionSolverList.C member function code is:

Foam::tmp<Foam::pointField> Foam::motionSolverList::curPoints() const
{
    if (motionSolvers_.size())
    {
        // Accumulated displacement
        pointField disp(motionSolvers_[0].curPoints() - mesh().points());

        for (label i = 1; i < motionSolvers_.size(); i++)
        {
            disp += motionSolvers_[i].curPoints() - mesh().points();
        }

        return mesh().points() + disp;
    }
    else
    {
        return mesh().points();
    }
}

Note that whenever two or more motion solvers (regardless of its kind) are added to the dynamicMeshDict when using the dynamicFvMesh type dynamicMotionSolverFvMesh, function motionSolverList is called (this can be see in motionSolver.C).

In figure Current_motionSolverList, the way the mesh is moved is depicted. Time step 1 refers to the movement between instants 0 and 1, Time step 2 refers to the movement between instants 1 and 2, and so on. Blue points are the outputs of Solver 1 and green points are the outputs of Solver 2 for every time step, which are expressed in code respectively, by "motionSolvers_[0].curPoints()" and "motionSolvers_[1].curPoints()". The Red point is point position during instant 0 (a.k.a. "initialMesh"). Black horizontal and vertical vectors are the difference between Solver 1 and 2 outputs, respectively, and the initial mesh (represented in code by "motionSolvers_[0].curPoints() - initialMesh" and "motionSolvers_[1].curPoints() - initialMesh"). Black points are the sequential motionSolverList outputs, described in "return mesh().points() + disp". Magenta and Ciano vectors are the differences between Solvers 1 and 2 outputs and the current mesh (which is the previous instant motion solver output), and are described as "motionSolvers_[0].curPoints() - mesh().points()" and "motionSolvers_[0].curPoints() - mesh().points()". The yellow vector is the difference in position between the current mesh and the initial mesh. Finally, the red vector is the combined displacement of the respective time instant, which means the sum of ciano and magenta vectors, and is expressed in code by "disp".

It is worth noting that in Time Step 1, the ciano and magenta arrows overlap the black ones and the yellow one is a null vector. Besides, in Time Step 2, the red arrow is a null vector.

In the proposed motionSolverList, the variable initialMesh, that assumes the coordinates of the mesh in the first intant, is created and the peace of code written above is replaced by the one below:

Foam::tmp<Foam::pointField> Foam::motionSolverList::curPoints() const
{
    if (motionSolvers_.size())
    {
        // Accumulated displacement
        pointField disp(motionSolvers_[0].curPoints() - initialMesh);

        for (label i = 1; i < motionSolvers_.size(); i++)
        {
            disp += motionSolvers_[i].curPoints() - initialMesh;
        }

        return initialMesh + disp;
    }
    else
    {
        return mesh().points();
    }
}

The mesh motion is now depicted as in figure Proposed_motionSovlerList. Arrows and points mean the same code expressions as previously described and the code output is now "initialMesh + disp". Note that the mesh movement is now a linear combination of previous movements. Another advantage of the porposed solver is that an added motion solver that doesn't move the mesh (zero displacement or zero velocity) is now a neutral element of the mesh motion, which doesn't happen in the current approach.
Current_motionSolverList.png (59,823 bytes)   
Current_motionSolverList.png (59,823 bytes)   
Proposed_motionSolverList.png (56,283 bytes)   
Proposed_motionSolverList.png (56,283 bytes)   

henry

2020-05-14 18:44

manager   ~0011343

Can you provide a test-case with non-overlapping motion which runs incorrectly with the current implementation?

rmoreira

2020-05-14 19:58

reporter   ~0011345

As far as I know, non-overlaping cases have no problems using the current implementation, my proposed fix is to improve handling of cases in which motions overlap.

henry

2020-05-14 20:26

manager   ~0011346

The current implementation is only for cases with non-overlapping motion, the change you propose would break it for cases in which velocity rather than displacement motion is used.

It appears that you need a different motion solver for a different purpose and you will need to write one for it rather than change the existing solver which would break it for its designed purpose.

rmoreira

2020-05-16 03:00

reporter   ~0011347

In fact, that is exactly what I did. I needed to combine dynamic airfoil profile changing, rotation and translation. I created a similar new function and added the proposed correction (by introducing a solver with zero displacement that captured the initial mesh) and everything worked fine then. My goal with this post was to propose an improvement for handling the motion of overlaping mesh movements.

henry

2020-05-16 08:30

manager   ~0011348

I understand your aim but the problem is that it breaks the current design functionality which some users are relying on.

henry

2020-05-16 08:31

manager   ~0011349

The proposed change breaks existing functionality.

Issue History

Date Modified Username Field Change
2020-05-12 03:16 rmoreira New Issue
2020-05-12 03:16 rmoreira File Added: example_Case.tar.gz
2020-05-12 10:08 henry Note Added: 0011338
2020-05-12 10:34 henry Note Edited: 0011338
2020-05-12 10:42 henry Note Edited: 0011338
2020-05-12 17:38 rmoreira Note Added: 0011339
2020-05-12 17:45 henry Note Added: 0011340
2020-05-12 17:45 henry Category Bug => Feature
2020-05-12 17:45 henry Description Updated
2020-05-12 17:45 henry Steps to Reproduce Updated
2020-05-12 17:45 henry Additional Information Updated
2020-05-14 18:15 rmoreira File Added: Current_motionSolverList.png
2020-05-14 18:15 rmoreira File Added: Proposed_motionSolverList.png
2020-05-14 18:15 rmoreira Note Added: 0011342
2020-05-14 18:44 henry Note Added: 0011343
2020-05-14 19:58 rmoreira Note Added: 0011345
2020-05-14 20:26 henry Note Added: 0011346
2020-05-16 03:00 rmoreira Note Added: 0011347
2020-05-16 08:30 henry Note Added: 0011348
2020-05-16 08:31 henry Assigned To => henry
2020-05-16 08:31 henry Status new => closed
2020-05-16 08:31 henry Note Added: 0011349