Skip to content

rotateMesh performs faulty operations in some boundary conditions

Summary

When applied to cases containing specific boundary conditions defined for vectorial or tensorial fields, the utility rotateMesh performs a double rotation, due to inadequate behaviour when writing transformed points into the polyMesh directory. If multiple time directories are present, only the first one is affected.

Steps to reproduce

It is possible to reproduce the bug by setting an OpenFOAM case including specific boundary conditions for vectorial (or tensorial) fields that need to read geometric data from the mesh. For instance, in the simpleFoam tutorial pitzDaily, if we replace the inlet boundaryField condition with a flowRateInletVelocity condition equivalent to the original fixedValue condition, in the 0/U file, like:

/*--------------------------------*- C++ -*----------------------------------*\
| =========                 |                                                 |
| \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox           |
|  \\    /   O peration     | Version:  v2106                                 |
|   \\  /    A nd           | Website:  www.openfoam.com                      |
|    \\/     M anipulation  |                                                 |
\*---------------------------------------------------------------------------*/
FoamFile
{
    version     2.0;
    format      ascii;
    class       volVectorField;
    object      U;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

dimensions      [0 1 -1 0 0 0 0];

internalField   uniform (0 0 0);

boundaryField
{
    /*inlet
    {
        type            fixedValue;
        value           uniform (10 0 0);
    }*/

    inlet
    {
        type                  flowRateInletVelocity;
        volumetricFlowRate    2.54e-4;
    }

    outlet
    {
        type            zeroGradient;
    }

    upperWall
    {
        type            noSlip;
    }

    lowerWall
    {
        type            noSlip;
    }

    frontAndBack
    {
        type            empty;
    }
}


// ************************************************************************* //

And then perform a simple +45 degree rotation along the z-axis using the utility rotateMesh (could be any arbitrary rotation), the velocity vector defined in the inlet boundaryField for U is erroneously rotated twice, completing a 90 degree rotation.

This +45 degree rotation along the z-axis can be achieved by this Allrun shell script:

#!/bin/sh
cd "${0%/*}" || exit                                # Run from this directory
. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions        # Tutorial run functions
#------------------------------------------------------------------------------

# Create 0.orig directory for backup
[ -d 0 ] && mv 0 0.orig

cp -r 0.orig/ 0/
runApplication blockMesh
runApplication rotateMesh "(1 0 0)" "(0.707107 0.707107 0)"
runApplication $(getApplication)

#------------------------------------------------------------------------------

Example case

Find attached the example case described above.

pitzDaily_bug_rotateMesh.zip

What is the current bug behaviour?

The utility rotateMesh performs a double rotation to the velocity vector defined in the inlet boundaryField for U.

This is due to an inadequate behaviour in the rotateMesh algorithm, which writes the transformed points into the polyMesh directory before the vector and tensor fields are rotated. Vector or tensor boundary conditions that must read from the mesh points in order to write corresponding vector/tensor values are hence rotated twice.

What is the expected correct behavior?

The expected correct behaviour can be obtained by using the utility transformPoints instead, for reproducing the same rotation, with the rotateFields flag. The commands below can be employed and lead to the same +45 degree rotation, however without breaking the inlet boundaryField velocity vector:

$ transformPoints -rotate '((1 0 0) (0.707107 0.707107 0))' -rotateFields

or

$ transformPoints -rotate-angle '((0 0 1) 45)' -rotateFields

Relevant logs and/or images

Erroneous inlet boundaryField for the velocity after the rotation (produced by the rotateMesh utility):

inlet
{
    type            flowRateInletVelocity;
    volumetricFlowRate constant 0.000254;
    value           nonuniform List<vector> 
30
(
(3.730349363e-14 10 0)
(-2.069228318e-07 10 0)
(3.375077995e-14 10 0)
(1.80136758e-07 10 -8.429032341e-17)
(0 10 0)
(-1.568181549e-07 10 -7.337898766e-17)
(1.463164896e-07 10 -6.846501721e-17)
(-1.365181355e-07 10 0)
(1.273759462e-07 10 -1.192045185e-16)
(-1.188459393e-07 10 1.112217487e-16)
(1.108871617e-07 10 0)
(-1.231714002e-07 10 0)
(-3.463895837e-14 10 -1.891196916e-16)
(8.288824915e-08 10 0)
(-4.759729677e-08 10 0)
(-5.577976303e-09 10 2.088050669e-16)
(3.660640857e-08 10 0)
(-7.507386712e-09 10 -2.810307806e-16)
(-1.539641836e-08 10 0)
(1.261886595e-08 10 0)
(-1.449527609e-08 10 0)
(1.554063989e-08 10 2.077669184e-16)
(3.18776916e-09 10 -2.386615189e-16)
(1.318243825e-08 10 -2.741500913e-16)
(-1.766639723e-08 10 0)
(1.932702354e-08 10 0)
(-3.33014194e-08 10 0)
(6.375543737e-08 10 -1.193307603e-16)
(-5.858861751e-08 10 0)
(6.73006717e-08 10 0)
)
;

Correct inlet boundaryField for the velocity after the rotation (produced by the transformPoints utility):

inlet
{
    type            flowRateInletVelocity;
    volumetricFlowRate constant 0.000254;
    value           uniform (7.07107 7.07107 0);
}

Environment information

  • OpenFOAM version : v2106
  • Operating system : Ubuntu 20.04.2 LTS
  • Hardware info : Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
  • Compiler : gcc 9.3.0

Possible fixes

This issue can be fixed by constructing the pointer-list of fvMesh objects inside the rotateMesh utility before the intermediate operation of points transformation is performed.

This can be achieved by inserting the include file createNamedMeshes.H before the first program loop. The relevant section of the code would read:

// ------------------------------------------------------------------------

#include "createNamedMeshes.H"

forAll(regionNames, regioni)
{
    const word& regionName = regionNames[regioni];
    const word& regionDir =
    (
        regionName == polyMesh::defaultRegion ? word::null : regionName
    );
    const fileName meshDir = regionDir/polyMesh::meshSubDir;

    if (regionNames.size() > 1)
    {
        Info<< "region=" << regionName << nl;
    }

    pointIOField points
    (
        IOobject
        (
            "points",
            runTime.findInstance(meshDir, "points"),
            meshDir,
            runTime,
            IOobject::MUST_READ,
            IOobject::NO_WRITE,
            false
        )
    );

    points = transform(rotT, points);

    // Set the precision of the points data to 10
    IOstream::defaultPrecision(max(10u, IOstream::defaultPrecision()));

    Info<< "Writing points into directory "
        << runTime.relativePath(points.path()) << nl
        << endl;
    points.write();
}

instantList timeDirs = timeSelector::select0(runTime, args);

forAll(timeDirs, timei)
{
    // ...
}

Find attached the rotateMesh.C C++ file with the proposed code, that patches this specific bug.

rotateMesh.C

PS: I personally consider this solution to be naive and somewhat simplistic, where a more elegant and robust one would reorganise the rotateMesh algorithm to match the order of operations in the transformPoints utility (points transformation -> mesh objects construction -> fields rotation -> mesh writing). However, I could not find an example case in which the simple solution suggested above fails.