diff --git a/applications/test/quaternion/Test-quaternion.C b/applications/test/quaternion/Test-quaternion.C index 1317ad4bf330258a72b0a5f72a32fa231cee1cab..852a31fed17d3b6bb030f3f1acca8dcb75da3a24 100644 --- a/applications/test/quaternion/Test-quaternion.C +++ b/applications/test/quaternion/Test-quaternion.C @@ -68,6 +68,21 @@ void printRotation(const quaternion& quat) } +bool equalTensors(const tensor& rot1, const tensor& rot2) +{ + for (direction cmpt=0; cmpt < tensor::nComponents; ++cmpt) + { + // Cannot be really picky, but SMALL is reasonable + if (mag(rot1[cmpt] - rot2[cmpt]) > SMALL) + { + return false; + } + } + + return true; +} + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // int main(int argc, char *argv[]) @@ -108,8 +123,17 @@ int main(int argc, char *argv[]) "(vector angle)", "Rotate about the <vector> by <angle> degrees - eg, '((1 0 0) 45)'" ); + argList::addBoolOption + ( + "verbose", + "Additional verbosity" + ); + argList args(argc, argv); + const bool verbose = args.found("verbose"); + + vector rotVector; if (args.readIfPresent("rollPitchYaw", rotVector)) @@ -122,10 +146,18 @@ int main(int argc, char *argv[]) rotVector *= degToRad(); - const quaternion quat(quaternion::rotationSequence::XYZ, rotVector); + const quaternion quat(quaternion::eulerOrder::XYZ, rotVector); printRotation(quat); + + // Euler + const tensor rot + ( + euler::rotation(euler::eulerOrder::XYZ, rotVector, false) + ); + printRotation(rot); } + if (args.readIfPresent("yawPitchRoll", rotVector)) { Info<< nl @@ -136,9 +168,16 @@ int main(int argc, char *argv[]) rotVector *= degToRad(); - const quaternion quat(quaternion::rotationSequence::ZYX, rotVector); + const quaternion quat(quaternion::eulerOrder::ZYX, rotVector); printRotation(quat); + + // Euler + const tensor rot + ( + euler::rotation(euler::eulerOrder::ZYX, rotVector, false) + ); + printRotation(rot); } if (args.readIfPresent("euler", rotVector)) { @@ -152,7 +191,7 @@ int main(int argc, char *argv[]) rotVector *= degToRad(); - const quaternion quat(quaternion::rotationSequence::ZXZ, rotVector); + const quaternion quat(quaternion::eulerOrder::ZXZ, rotVector); printRotation(quat); } @@ -266,23 +305,44 @@ int main(int argc, char *argv[]) Info<< "Test conversion from and to Euler-angles" << nl; vector angles(0.1, 0.2, 0.3); - for (int rs=quaternion::ZYX; rs<quaternion::XZX; ++rs) + + for (int rs : quaternion::eulerOrderNames.values()) { + const quaternion::eulerOrder order = quaternion::eulerOrder(rs); + const word& orderName = quaternion::eulerOrderNames[order]; + if ( - mag - ( - angles - - quaternion(quaternion::rotationSequence(rs), angles) - .eulerAngles(quaternion::rotationSequence(rs)) - ) + mag(angles - quaternion(order, angles).eulerAngles(order)) > SMALL ) { FatalErrorInFunction - << "Inconsistent conversion for rotation sequence " - << rs << exit(FatalError) - << nl; + << "Inconsistent conversion for euler rotation order " + << orderName << nl << exit(FatalError); + } + + + tensor rotQ(quaternion(order, angles).R()); + tensor rotE(euler::rotation(order, angles, false)); + + if (verbose) + { + Info<< "euler " << orderName << angles << nl; + printRotation(rotE); + } + + if (!equalTensors(rotQ, rotE)) + { + WarningInFunction + << "Inconsistent quaternion/euler rotation matrices for " + << orderName << nl; + + printRotation(rotQ); + printRotation(rotE); + + FatalError + << nl << exit(FatalError); } } diff --git a/applications/test/sizeof/Make/options b/applications/test/sizeof/Make/options index 4e772fdf9d7bc94221d127458f9d2ca32850fe69..410c857f852235bdeccb91e12e9e1b1b919ab15b 100644 --- a/applications/test/sizeof/Make/options +++ b/applications/test/sizeof/Make/options @@ -1,2 +1,5 @@ -/* EXE_INC = -I$(LIB_SRC)/finiteVolume/lnInclude */ +EXE_INC = \ + -I$(LIB_SRC)/finiteVolume/lnInclude \ + -I$(LIB_SRC)/meshTools/lnInclude + /* EXE_LIBS = -lfiniteVolume */ diff --git a/applications/utilities/mesh/manipulation/transformPoints/Make/options b/applications/utilities/mesh/manipulation/transformPoints/Make/options index 16eb624becb6894353d6c74bb8bab5de37551b56..969020c4afaf5d784299462b9e1af282040ba6b4 100644 --- a/applications/utilities/mesh/manipulation/transformPoints/Make/options +++ b/applications/utilities/mesh/manipulation/transformPoints/Make/options @@ -1,6 +1,8 @@ EXE_INC = \ - -I$(LIB_SRC)/finiteVolume/lnInclude + -I$(LIB_SRC)/finiteVolume/lnInclude \ + -I$(LIB_SRC)/meshTools/lnInclude EXE_LIBS = \ -lfiniteVolume \ + -lmeshTools \ -lgenericPatchFields diff --git a/applications/utilities/mesh/manipulation/transformPoints/transformPoints.C b/applications/utilities/mesh/manipulation/transformPoints/transformPoints.C index 698ec7f601d811eaebea0a70502c88f1f1e3abfb..f19e726fc8d1b1f811b514b783262147cc660bc8 100644 --- a/applications/utilities/mesh/manipulation/transformPoints/transformPoints.C +++ b/applications/utilities/mesh/manipulation/transformPoints/transformPoints.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2017-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- | Copyright (C) 2011-2016 OpenFOAM Foundation @@ -57,7 +57,7 @@ Usage With -rotateFields (in combination with -rotate/yawPitchRoll/rollPitchYaw) it will also read & transform vector & tensor fields. - Note: +Note roll (rotation about x) pitch (rotation about y) yaw (rotation about z) @@ -73,9 +73,11 @@ Usage #include "pointFields.H" #include "transformField.H" #include "transformGeometricField.H" -#include "unitConversion.H" +#include "axisAngleRotation.H" +#include "EulerCoordinateRotation.H" using namespace Foam; +using namespace Foam::coordinateRotations; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -284,40 +286,38 @@ int main(int argc, char *argv[]) n1n2[0].normalise(); n1n2[1].normalise(); - const tensor rotT = rotationTensor(n1n2[0], n1n2[1]); + const tensor rot(rotationTensor(n1n2[0], n1n2[1])); - Info<< "Rotating points by " << rotT << endl; - - points = transform(rotT, points); + Info<< "Rotating points by " << rot << endl; + points = transform(rot, points); if (doRotateFields) { - rotateFields(args, runTime, rotT); + rotateFields(args, runTime, rot); } } else if (args.found("rotate-angle")) { - const Tuple2<vector, scalar> axisAngle + const Tuple2<vector, scalar> rotAxisAngle ( args.lookup("rotate-angle")() ); + const vector& axis = rotAxisAngle.first(); + const scalar& angle = rotAxisAngle.second(); + Info<< "Rotating points " << nl - << " about " << axisAngle.first() << nl - << " angle " << axisAngle.second() << nl; + << " about " << axis << nl + << " angle " << angle << nl; - const quaternion quat - ( - axisAngle.first(), - degToRad(axisAngle.second()) - ); + const tensor rot(axisAngle::rotation(axis, angle, true)); - Info<< "Rotating points by quaternion " << quat << endl; - points = transform(quat, points); + Info<< "Rotating points by " << rot << endl; + points = transform(rot, points); if (doRotateFields) { - rotateFields(args, runTime, quat.R()); + rotateFields(args, runTime, rot); } } else if (args.readIfPresent("rollPitchYaw", v)) @@ -327,16 +327,14 @@ int main(int argc, char *argv[]) << " pitch " << v.y() << nl << " yaw " << v.z() << nl; - v *= degToRad(); - - const quaternion quat(quaternion::rotationSequence::XYZ, v); + const tensor rot(euler::rotation(euler::eulerOrder::XYZ, v, true)); - Info<< "Rotating points by quaternion " << quat << endl; - points = transform(quat, points); + Info<< "Rotating points by " << rot << endl; + points = transform(rot, points); if (doRotateFields) { - rotateFields(args, runTime, quat.R()); + rotateFields(args, runTime, rot); } } else if (args.readIfPresent("yawPitchRoll", v)) @@ -346,16 +344,14 @@ int main(int argc, char *argv[]) << " pitch " << v.y() << nl << " roll " << v.z() << nl; - v *= degToRad(); - - const quaternion quat(quaternion::rotationSequence::ZYX, v); + const tensor rot(euler::rotation(euler::eulerOrder::ZYX, v, true)); - Info<< "Rotating points by quaternion " << quat << endl; - points = transform(quat, points); + Info<< "Rotating points by " << rot << endl; + points = transform(rot, points); if (doRotateFields) { - rotateFields(args, runTime, quat.R()); + rotateFields(args, runTime, rot); } } diff --git a/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C b/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C index 7232b683f45b68edae61ee78a89e4c09be9794d8..6e3c8f5aec52b80f17dd39409473484e4c4c035f 100644 --- a/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C +++ b/applications/utilities/surface/surfaceMeshExtract/surfaceMeshExtract.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2017-2018 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2017-2019 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- | Copyright (C) 2011-2016 OpenFOAM Foundation @@ -24,7 +24,7 @@ License along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. Application - surfaceMeshTriangulate + surfaceMeshExtract Group grpSurfaceUtilities @@ -47,6 +47,7 @@ Description #include "argList.H" #include "Time.H" #include "polyMesh.H" +#include "emptyPolyPatch.H" #include "processorPolyPatch.H" #include "ListListOps.H" #include "uindirectPrimitivePatch.H" @@ -58,6 +59,57 @@ using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +labelList getSelectedPatches +( + const polyBoundaryMesh& patches, + const wordRes& whitelist, + const wordRes& blacklist +) +{ + DynamicList<label> patchIDs(patches.size()); + + for (const polyPatch& pp : patches) + { + if (isType<emptyPolyPatch>(pp)) + { + continue; + } + else if (Pstream::parRun() && bool(isA<processorPolyPatch>(pp))) + { + break; // No processor patches for parallel output + } + + const word& patchName = pp.name(); + + bool accept = false; + + if (whitelist.size()) + { + const auto matched = whitelist.matched(patchName); + + accept = + ( + matched == wordRe::LITERAL + ? true + : (matched == wordRe::REGEX && !blacklist.match(patchName)) + ); + } + else + { + accept = !blacklist.match(patchName); + } + + if (accept) + { + patchIDs.append(pp.index()); + } + } + + return patchIDs.shrink(); +} + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // int main(int argc, char *argv[]) { @@ -69,6 +121,10 @@ int main(int argc, char *argv[]) ); timeSelector::addOptions(); + // Less frequently used - reduce some clutter + argList::setAdvanced("decomposeParDict"); + argList::setAdvanced("noFunctionObjects"); + argList::addArgument("output", "The output surface file"); #include "addRegionOption.H" @@ -78,18 +134,26 @@ int main(int argc, char *argv[]) "Exclude processor patches" ); argList::addOption + ( + "faceZones", + "wordRes", + "Specify single or multiple faceZones to extract\n" + "Eg, 'cells' or '( slice \"mfp-.*\" )'" + ); + argList::addOption ( "patches", - "wordRes" + "wordRes", "Specify single patch or multiple patches to extract.\n" "Eg, 'top' or '( front \".*back\" )'" ); argList::addOption ( - "faceZones", + "excludePatches", "wordRes", - "Specify single or multiple faceZones to extract\n" - "Eg, 'cells' or '( slice \"mfp-.*\" )'" + "Specify single patch or multiple patches to exclude from writing." + " Eg, 'outlet' or '( inlet \".*Wall\" )'", + true // mark as an advanced option ); #include "setRootCase.H" @@ -121,6 +185,25 @@ int main(int argc, char *argv[]) Info<< "Excluding all processor patches." << nl << endl; } + wordRes includePatches, excludePatches; + if (args.readListIfPresent<wordRe>("patches", includePatches)) + { + Info<< "Including patches " << flatOutput(includePatches) + << nl << endl; + } + if (args.readListIfPresent<wordRe>("excludePatches", excludePatches)) + { + Info<< "Excluding patches " << flatOutput(excludePatches) + << nl << endl; + } + + // Non-mandatory + const wordRes selectedFaceZones(args.getList<wordRe>("faceZones", false)); + if (selectedFaceZones.size()) + { + Info<< "Including faceZones " << flatOutput(selectedFaceZones) + << nl << endl; + } Info<< "Reading mesh from time " << runTime.value() << endl; @@ -171,53 +254,25 @@ int main(int argc, char *argv[]) // Construct table of patches to include. const polyBoundaryMesh& bMesh = mesh.boundaryMesh(); - labelList includePatches; - - if (args.found("patches")) - { - includePatches = - bMesh.patchSet(args.getList<wordRe>("patches")).sortedToc(); - } - else if (includeProcPatches) - { - includePatches = identity(bMesh.size()); - } - else - { - includePatches = identity(bMesh.nNonProcessor()); - } - + labelList patchIds = + ( + (includePatches.size() || excludePatches.size()) + ? getSelectedPatches(bMesh, includePatches, excludePatches) + : includeProcPatches + ? identity(bMesh.size()) + : identity(bMesh.nNonProcessor()) + ); - labelList includeFaceZones; + labelList faceZoneIds; const faceZoneMesh& fzm = mesh.faceZones(); - if (args.found("faceZones")) + if (selectedFaceZones.size()) { - const wordList allZoneNames(fzm.names()); - - const wordRes zoneNames(args.getList<wordRe>("faceZones")); - - labelHashSet hashed(2*fzm.size()); - - for (const wordRe& zoneName : zoneNames) - { - labelList zoneIDs = findStrings(zoneName, allZoneNames); - hashed.insert(zoneIDs); - - if (zoneIDs.empty()) - { - WarningInFunction - << "Cannot find any faceZone name matching " - << zoneName << endl; - } - } - - includeFaceZones = hashed.sortedToc(); + faceZoneIds = fzm.indices(selectedFaceZones); Info<< "Additionally extracting faceZones " - << UIndirectList<word>(allZoneNames, includeFaceZones) - << endl; + << fzm.names(selectedFaceZones) << nl; } @@ -232,7 +287,7 @@ int main(int argc, char *argv[]) // processor patches) HashTable<label> patchSize(1024); label nFaces = 0; - for (const label patchi : includePatches) + for (const label patchi : patchIds) { const polyPatch& pp = bMesh[patchi]; patchSize.insert(pp.name(), pp.size()); @@ -240,7 +295,7 @@ int main(int argc, char *argv[]) } HashTable<label> zoneSize(1024); - for (const label zonei : includeFaceZones) + for (const label zonei : faceZoneIds) { const faceZone& pp = fzm[zonei]; zoneSize.insert(pp.name(), pp.size()); @@ -289,7 +344,7 @@ int main(int argc, char *argv[]) compactZones.setCapacity(nFaces); // Collect faces on patches - for (const label patchi : includePatches) + for (const label patchi : patchIds) { const polyPatch& pp = bMesh[patchi]; forAll(pp, i) @@ -299,7 +354,7 @@ int main(int argc, char *argv[]) } } // Collect faces on faceZones - for (const label zonei : includeFaceZones) + for (const label zonei : faceZoneIds) { const faceZone& pp = fzm[zonei]; forAll(pp, i) diff --git a/applications/utilities/surface/surfaceTransformPoints/Make/options b/applications/utilities/surface/surfaceTransformPoints/Make/options index a504dd8617bfa4ff4d8fc1075d5d68d6db66db89..5ae72ebd31bdf7500846ee0dca7a4c123ff4fb43 100644 --- a/applications/utilities/surface/surfaceTransformPoints/Make/options +++ b/applications/utilities/surface/surfaceTransformPoints/Make/options @@ -1,5 +1,7 @@ EXE_INC = \ - -I$(LIB_SRC)/surfMesh/lnInclude + -I$(LIB_SRC)/surfMesh/lnInclude \ + -I$(LIB_SRC)/meshTools/lnInclude EXE_LIBS = \ - -lsurfMesh + -lsurfMesh \ + -lmeshTools diff --git a/applications/utilities/surface/surfaceTransformPoints/surfaceTransformPoints.C b/applications/utilities/surface/surfaceTransformPoints/surfaceTransformPoints.C index 229df4d1b9bb4feae949ca0c5a15f6536c59c7c0..552863b9b45340d4495cf4119597f8accfc8d861 100644 --- a/applications/utilities/surface/surfaceTransformPoints/surfaceTransformPoints.C +++ b/applications/utilities/surface/surfaceTransformPoints/surfaceTransformPoints.C @@ -33,12 +33,18 @@ Description Transform (scale/rotate) a surface. Like transformPoints but for surfaces. - The rollPitchYaw option takes three angles (degrees): - - roll (rotation about x) followed by - - pitch (rotation about y) followed by - - yaw (rotation about z) + The rollPitchYaw and yawPitchRoll options take three angles (degrees) + that describe the intrinsic Euler rotation. - The yawPitchRoll does yaw followed by pitch followed by roll. + rollPitchYaw + - roll (rotation about X) followed by + - pitch (rotation about Y) followed by + - yaw (rotation about Z) + + yawPitchRoll + - yaw (rotation about Z) followed by + - pitch (rotation about Y) followed by + - roll (rotation about X) \*---------------------------------------------------------------------------*/ @@ -48,12 +54,12 @@ Description #include "transformField.H" #include "Pair.H" #include "Tuple2.H" -#include "quaternion.H" -#include "unitConversion.H" - +#include "axisAngleRotation.H" +#include "EulerCoordinateRotation.H" #include "MeshedSurfaces.H" using namespace Foam; +using namespace Foam::coordinateRotations; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -179,31 +185,29 @@ int main(int argc, char *argv[]) n1n2[0].normalise(); n1n2[1].normalise(); - const tensor rotT = rotationTensor(n1n2[0], n1n2[1]); + const tensor rot(rotationTensor(n1n2[0], n1n2[1])); - Info<< "Rotating points by " << rotT << endl; - - points = transform(rotT, points); + Info<< "Rotating points by " << rot << endl; + points = transform(rot, points); } else if (args.found("rotate-angle")) { - const Tuple2<vector, scalar> axisAngle + const Tuple2<vector, scalar> rotAxisAngle ( args.lookup("rotate-angle")() ); + const vector& axis = rotAxisAngle.first(); + const scalar& angle = rotAxisAngle.second(); + Info<< "Rotating points " << nl - << " about " << axisAngle.first() << nl - << " angle " << axisAngle.second() << nl; + << " about " << axis << nl + << " angle " << angle << nl; - const quaternion quat - ( - axisAngle.first(), - degToRad(axisAngle.second()) - ); + const tensor rot(axisAngle::rotation(axis, angle, true)); - Info<< "Rotating points by quaternion " << quat << endl; - points = transform(quat, points); + Info<< "Rotating points by " << rot << endl; + points = transform(rot, points); } else if (args.readIfPresent("rollPitchYaw", v)) { @@ -212,12 +216,10 @@ int main(int argc, char *argv[]) << " pitch " << v.y() << nl << " yaw " << v.z() << nl; - v *= degToRad(); + const tensor rot(euler::rotation(euler::eulerOrder::XYZ, v, true)); - const quaternion quat(quaternion::rotationSequence::XYZ, v); - - Info<< "Rotating points by quaternion " << quat << endl; - points = transform(quat, points); + Info<< "Rotating points by " << rot << endl; + points = transform(rot, points); } else if (args.readIfPresent("yawPitchRoll", v)) { @@ -226,12 +228,10 @@ int main(int argc, char *argv[]) << " pitch " << v.y() << nl << " roll " << v.z() << nl; - v *= degToRad(); - - const quaternion quat(quaternion::rotationSequence::ZYX, v); + const tensor rot(euler::rotation(euler::eulerOrder::ZYX, v, true)); - Info<< "Rotating points by quaternion " << quat << endl; - points = transform(quat, points); + Info<< "Rotating points by " << rot << endl; + points = transform(rot, points); } List<scalar> scaling; diff --git a/src/OpenFOAM/primitives/quaternion/quaternion.C b/src/OpenFOAM/primitives/quaternion/quaternion.C index a2b53b8391593c4dca0ba55f464382f43f9663da..9ced3d5317fff96ecdcd3e09f63b8c426372cd5a 100644 --- a/src/OpenFOAM/primitives/quaternion/quaternion.C +++ b/src/OpenFOAM/primitives/quaternion/quaternion.C @@ -31,10 +31,30 @@ License // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // -const char* const Foam::quaternion::typeName = "quaternion"; const Foam::quaternion Foam::quaternion::zero(0, vector(0, 0, 0)); const Foam::quaternion Foam::quaternion::I(1, vector(0, 0, 0)); +const Foam::Enum<Foam::quaternion::eulerOrder> +Foam::quaternion::eulerOrderNames +({ + // Proper Euler angles + { eulerOrder::XZX, "xzx" }, + { eulerOrder::XYX, "xyx" }, + { eulerOrder::YXY, "yxy" }, + { eulerOrder::YZY, "yzy" }, + { eulerOrder::ZYZ, "zyz" }, + { eulerOrder::ZXZ, "zxz" }, + + // Tait-Bryan angles + { eulerOrder::XZY, "xzy" }, + { eulerOrder::XYZ, "xyz" }, + { eulerOrder::YXZ, "yxz" }, + { eulerOrder::YZX, "yzx" }, + { eulerOrder::ZYX, "zyx" }, + { eulerOrder::ZXY, "zxy" }, +}); + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::quaternion::quaternion(Istream& is) diff --git a/src/OpenFOAM/primitives/quaternion/quaternion.H b/src/OpenFOAM/primitives/quaternion/quaternion.H index 339253e34bc605c7bf9bb647f2374ba099015ffb..42040e0c252729774181b7ab52517ee1398351b0 100644 --- a/src/OpenFOAM/primitives/quaternion/quaternion.H +++ b/src/OpenFOAM/primitives/quaternion/quaternion.H @@ -43,6 +43,7 @@ SourceFiles #include "tensor.H" #include "word.H" #include "contiguous.H" +#include "Enum.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -90,14 +91,23 @@ class quaternion public: - //- Component type - typedef scalar cmptType; + // Public Types - //- Euler-angle rotation sequence - enum rotationSequence - { - ZYX, ZYZ, ZXY, ZXZ, YXZ, YXY, YZX, YZY, XYZ, XYX, XZY, XZX - }; + //- Component type + typedef scalar cmptType; + + //- Euler-angle rotation order + enum eulerOrder : unsigned char + { + // Proper Euler angles + XZX, XYX, YXY, YZY, ZYZ, ZXZ, + + // Tait-Bryan angles + XZY, XYZ, YXZ, YZX, ZYX, ZXY + }; + + //- The names for Euler-angle rotation order + static const Enum<eulerOrder> eulerOrderNames; // Member Constants @@ -108,7 +118,7 @@ public: // Static Data Members - static const char* const typeName; + static constexpr const char* const typeName = "quaternion"; static const quaternion zero; static const quaternion I; @@ -119,15 +129,17 @@ public: //- Construct null inline quaternion(); + //- Construct zero initialized + inline quaternion(const Foam::zero); + //- Construct given scalar and vector parts inline quaternion(const scalar w, const vector& v); - //- Construct a rotation quaternion given the direction d - // and angle theta + //- Construct rotation quaternion given direction d and angle theta inline quaternion(const vector& d, const scalar theta); - //- Construct a rotation quaternion given the direction d - //- and cosine angle cosTheta and a if d is normalized + //- Construct a rotation quaternion given direction d + //- and cosine angle cosTheta and flag if d is normalized inline quaternion ( const vector& d, @@ -135,7 +147,8 @@ public: const bool normalized ); - //- Construct a real from the given scalar part, the vector part = zero + //- Construct a real quaternion from the given scalar part, + //- the vector part = zero inline explicit quaternion(const scalar w); //- Construct a pure imaginary quaternion given the vector part, @@ -146,8 +159,8 @@ public: //- (w = sqrt(1 - |sqr(v)|)) static inline quaternion unit(const vector& v); - //- Construct from three Euler angles - inline quaternion(const rotationSequence rs, const vector& angles); + //- Construct from three Euler rotation angles + inline quaternion(const eulerOrder order, const vector& angles); //- Construct from a rotation tensor inline explicit quaternion(const tensor& rotationTensor); @@ -169,9 +182,9 @@ public: //- The rotation tensor corresponding the quaternion inline tensor R() const; - //- Return a vector of euler angles corresponding to the - //- specified rotation sequence - inline vector eulerAngles(const rotationSequence rs) const; + //- Return the Euler rotation angles corresponding to the + //- specified rotation order + inline vector eulerAngles(const eulerOrder order) const; //- Return the quaternion normalized by its magnitude inline quaternion normalized() const; @@ -238,7 +251,7 @@ inline quaternion normalize(const quaternion& q); inline quaternion inv(const quaternion& q); //- Return a string representation of a quaternion -word name(const quaternion&); +word name(const quaternion& q); //- Spherical linear interpolation of quaternions quaternion slerp diff --git a/src/OpenFOAM/primitives/quaternion/quaternionI.H b/src/OpenFOAM/primitives/quaternion/quaternionI.H index 13b4f70d9423b794228045e3bfb8e27f182d7daa..e692707c3011ce66ef8721e43bc4b267f45b5302 100644 --- a/src/OpenFOAM/primitives/quaternion/quaternionI.H +++ b/src/OpenFOAM/primitives/quaternion/quaternionI.H @@ -31,6 +31,13 @@ inline Foam::quaternion::quaternion() {} +inline Foam::quaternion::quaternion(const Foam::zero) +: + w_(Zero), + v_(Zero) +{} + + inline Foam::quaternion::quaternion(const scalar w, const vector& v) : w_(w), @@ -88,87 +95,112 @@ inline Foam::quaternion Foam::quaternion::unit(const vector& v) inline Foam::quaternion::quaternion ( - const rotationSequence rs, + const eulerOrder order, const vector& angles ) { - switch(rs) + switch (order) { case ZYX: + { operator=(quaternion(vector(0, 0, 1), angles.x())); operator*=(quaternion(vector(0, 1, 0), angles.y())); operator*=(quaternion(vector(1, 0, 0), angles.z())); break; + } case ZYZ: + { operator=(quaternion(vector(0, 0, 1), angles.x())); operator*=(quaternion(vector(0, 1, 0), angles.y())); operator*=(quaternion(vector(0, 0, 1), angles.z())); break; + } case ZXY: + { operator=(quaternion(vector(0, 0, 1), angles.x())); operator*=(quaternion(vector(1, 0, 0), angles.y())); operator*=(quaternion(vector(0, 1, 0), angles.z())); break; + } case ZXZ: + { operator=(quaternion(vector(0, 0, 1), angles.x())); operator*=(quaternion(vector(1, 0, 0), angles.y())); operator*=(quaternion(vector(0, 0, 1), angles.z())); break; + } case YXZ: + { operator=(quaternion(vector(0, 1, 0), angles.x())); operator*=(quaternion(vector(1, 0, 0), angles.y())); operator*=(quaternion(vector(0, 0, 1), angles.z())); break; + } case YXY: + { operator=(quaternion(vector(0, 1, 0), angles.x())); operator*=(quaternion(vector(1, 0, 0), angles.y())); operator*=(quaternion(vector(0, 1, 0), angles.z())); break; + } case YZX: + { operator=(quaternion(vector(0, 1, 0), angles.x())); operator*=(quaternion(vector(0, 0, 1), angles.y())); operator*=(quaternion(vector(1, 0, 0), angles.z())); break; + } case YZY: + { operator=(quaternion(vector(0, 1, 0), angles.x())); operator*=(quaternion(vector(0, 0, 1), angles.y())); operator*=(quaternion(vector(0, 1, 0), angles.z())); break; + } case XYZ: + { operator=(quaternion(vector(1, 0, 0), angles.x())); operator*=(quaternion(vector(0, 1, 0), angles.y())); operator*=(quaternion(vector(0, 0, 1), angles.z())); break; + } case XYX: + { operator=(quaternion(vector(1, 0, 0), angles.x())); operator*=(quaternion(vector(0, 1, 0), angles.y())); operator*=(quaternion(vector(1, 0, 0), angles.z())); break; + } case XZY: + { operator=(quaternion(vector(1, 0, 0), angles.x())); operator*=(quaternion(vector(0, 0, 1), angles.y())); operator*=(quaternion(vector(0, 1, 0), angles.z())); break; + } case XZX: + { operator=(quaternion(vector(1, 0, 0), angles.x())); operator*=(quaternion(vector(0, 0, 1), angles.y())); operator*=(quaternion(vector(1, 0, 0), angles.z())); break; + } default: FatalErrorInFunction - << "Unknown rotation sequence " << rs << abort(FatalError); + << "Unknown euler rotation order " + << int(order) << abort(FatalError); break; } } @@ -201,7 +233,7 @@ inline Foam::quaternion::quaternion && rotationTensor.xx() > rotationTensor.zz() ) { - scalar s = 2.0*Foam::sqrt + const scalar s = 2.0*Foam::sqrt ( 1.0 + rotationTensor.xx() @@ -219,7 +251,7 @@ inline Foam::quaternion::quaternion rotationTensor.yy() > rotationTensor.zz() ) { - scalar s = 2.0*Foam::sqrt + const scalar s = 2.0*Foam::sqrt ( 1.0 + rotationTensor.yy() @@ -234,7 +266,7 @@ inline Foam::quaternion::quaternion } else { - scalar s = 2.0*Foam::sqrt + const scalar s = 2.0*Foam::sqrt ( 1.0 + rotationTensor.zz() @@ -373,7 +405,7 @@ inline Foam::vector Foam::quaternion::threeAxes inline Foam::vector Foam::quaternion::eulerAngles ( - const rotationSequence rs + const eulerOrder order ) const { const scalar w2 = sqr(w()); @@ -381,9 +413,10 @@ inline Foam::vector Foam::quaternion::eulerAngles const scalar y2 = sqr(v().y()); const scalar z2 = sqr(v().z()); - switch(rs) + switch (order) { case ZYX: + { return threeAxes ( 2*(v().x()*v().y() + w()*v().z()), @@ -393,8 +426,10 @@ inline Foam::vector Foam::quaternion::eulerAngles w2 - x2 - y2 + z2 ); break; + } case ZYZ: + { return twoAxes ( 2*(v().y()*v().z() - w()*v().x()), @@ -404,8 +439,10 @@ inline Foam::vector Foam::quaternion::eulerAngles 2*(w()*v().y() - v().x()*v().z()) ); break; + } case ZXY: + { return threeAxes ( 2*(w()*v().z() - v().x()*v().y()), @@ -415,8 +452,10 @@ inline Foam::vector Foam::quaternion::eulerAngles w2 - x2 - y2 + z2 ); break; + } case ZXZ: + { return twoAxes ( 2*(v().x()*v().z() + w()*v().y()), @@ -426,8 +465,10 @@ inline Foam::vector Foam::quaternion::eulerAngles 2*(v().y()*v().z() + w()*v().x()) ); break; + } case YXZ: + { return threeAxes ( 2*(v().x()*v().z() + w()*v().y()), @@ -437,8 +478,10 @@ inline Foam::vector Foam::quaternion::eulerAngles w2 - x2 + y2 - z2 ); break; + } case YXY: + { return twoAxes ( 2*(v().x()*v().y() - w()*v().z()), @@ -448,8 +491,10 @@ inline Foam::vector Foam::quaternion::eulerAngles 2*(w()*v().x() - v().y()*v().z()) ); break; + } case YZX: + { return threeAxes ( 2*(w()*v().y() - v().x()*v().z()), @@ -459,8 +504,10 @@ inline Foam::vector Foam::quaternion::eulerAngles w2 - x2 + y2 - z2 ); break; + } case YZY: + { return twoAxes ( 2*(v().y()*v().z() + w()*v().x()), @@ -470,8 +517,10 @@ inline Foam::vector Foam::quaternion::eulerAngles 2*(v().x()*v().y() + w()*v().z()) ); break; + } case XYZ: + { return threeAxes ( 2*(w()*v().x() - v().y()*v().z()), @@ -481,8 +530,10 @@ inline Foam::vector Foam::quaternion::eulerAngles w2 + x2 - y2 - z2 ); break; + } case XYX: + { return twoAxes ( 2*(v().x()*v().y() + w()*v().z()), @@ -492,8 +543,10 @@ inline Foam::vector Foam::quaternion::eulerAngles 2*(v().x()*v().z() + w()*v().y()) ); break; + } case XZY: + { return threeAxes ( 2*(v().y()*v().z() + w()*v().x()), @@ -503,8 +556,10 @@ inline Foam::vector Foam::quaternion::eulerAngles w2 + x2 - y2 - z2 ); break; + } case XZX: + { return twoAxes ( 2*(v().x()*v().z() - w()*v().y()), @@ -514,12 +569,16 @@ inline Foam::vector Foam::quaternion::eulerAngles 2*(w()*v().z() - v().x()*v().y()) ); break; + } + default: FatalErrorInFunction - << "Unknown rotation sequence " << rs << abort(FatalError); - return Zero; + << "Unknown euler rotation order " + << int(order) << abort(FatalError); break; } + + return Zero; } diff --git a/src/meshTools/coordinate/rotation/EulerCoordinateRotation.C b/src/meshTools/coordinate/rotation/EulerCoordinateRotation.C index 98104f9c03045ea95211850ecc91219a401183c3..0de692db9ccbdb2b53e9f15323333844474546c2 100644 --- a/src/meshTools/coordinate/rotation/EulerCoordinateRotation.C +++ b/src/meshTools/coordinate/rotation/EulerCoordinateRotation.C @@ -62,6 +62,7 @@ namespace Foam Foam::tensor Foam::coordinateRotations::euler::rotation ( + const eulerOrder order, const vector& angles, bool degrees ) @@ -83,14 +84,163 @@ Foam::tensor Foam::coordinateRotations::euler::rotation // https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix - // Z1-X2-Z3 rotation - return - tensor - ( - c1*c3 - c2*s1*s3, -c1*s3 - c2*c3*s1, s1*s2, - c3*s1 + c1*c2*s3, c1*c2*c3 - s1*s3, -c1*s2, - s2*s3, c3*s2, c2 - ); + switch (order) + { + // Proper Euler angles + + case eulerOrder::XZX: // X1-Z2-X3 rotation + { + return tensor + ( + ( c2 ), ( -c3*s2 ), ( s2*s3 ), + ( c1*s2 ), ( c1*c2*c3 - s1*s3 ), ( -c3*s1 - c1*c2*s3 ), + ( s1*s2 ), ( c1*s3 + c2*c3*s1 ), ( c1*c3 - c2*s1*s3 ) + ); + break; + } + + case eulerOrder::XYX: // X1-Y2-X3 rotation + { + return tensor + ( + ( c2 ), ( s2*s3 ), ( c3*s2 ), + ( s1*s2 ), ( c1*c3 - c2*s1*s3 ), ( -c1*s3 - c2*c3*s1 ), + ( -c1*s2 ), ( c3*s1 + c1*c2*s3 ), ( c1*c2*c3 - s1*s3 ) + ); + break; + } + + case eulerOrder::YXY: // Y1-X2-Y3 rotation + { + return tensor + ( + ( c1*c3 - c2*s1*s3 ), ( s1*s2 ), ( c1*s3 + c2*c3*s1 ), + ( s2*s3 ), ( c2 ), ( -c3*s2 ), + ( -c3*s1 -c1*c2*s3 ), ( c1*s2 ), ( c1*c2*c3 - s1*s3 ) + ); + break; + } + + case eulerOrder::YZY: // Y1-Z2-Y3 rotation + { + return tensor + ( + ( c1*c2*c3 - s1*s3 ), ( -c1*s2 ), ( c3*s1 + c1*c2*s3 ), + ( c3*s2 ), ( c2 ), ( s2*s3 ), + (-c1*s3 - c2*c3*s1 ), ( s1*s2 ), ( c1*c3 - c2*s1*s3 ) + ); + break; + } + + case eulerOrder::ZYZ: // Z1-Y2-Z3 rotation + { + return tensor + ( + ( c1*c2*c3 - s1*s3 ), ( -c3*s1 - c1*c2*s3 ), ( c1*s2 ), + ( c1*s3 + c2*c3*s1 ), ( c1*c3 - c2*s1*s3 ), ( s1*s2 ), + ( -c3*s2 ), ( s2*s3 ), ( c2 ) + ); + break; + } + + case eulerOrder::ZXZ: // Z1-X2-Z3 rotation + { + return tensor + ( + ( c1*c3 - c2*s1*s3 ), ( -c1*s3 - c2*c3*s1 ), ( s1*s2 ), + ( c3*s1 + c1*c2*s3 ), ( c1*c2*c3 - s1*s3 ), ( -c1*s2 ), + ( s2*s3 ), ( c3*s2 ), ( c2 ) + ); + break; + } + + + // Tait-Bryan angles + + case eulerOrder::XZY: // X1-Z2-Y3 rotation + { + return tensor + ( + ( c2*c3 ), ( -s2 ), ( c2*s3 ), + ( s1*s3 + c1*c3*s2 ), ( c1*c2 ), ( c1*s2*s3 - c3*s1 ), + ( c3*s1*s2 - c1*s3 ), ( c2*s1 ), ( c1*c3 + s1*s2*s3 ) + ); + break; + } + + case eulerOrder::XYZ: // X1-Y2-Z3 rotation + { + return tensor + ( + ( c2*c3 ), ( -c2*s3 ), ( s2 ), + ( c1*s3 + c3*s1*s2 ), ( c1*c3 - s1*s2*s3 ), ( -c2*s1 ), + ( s1*s3 - c1*c3*s2 ), ( c3*s1 + c1*s2*s3 ), ( c1*c2 ) + ); + break; + } + + case eulerOrder::YXZ: // Y1-X2-Z3 rotation + { + return tensor + ( + ( c1*c3 + s1*s2*s3 ), ( c3*s1*s2 - c1*s3 ), ( c2*s1 ), + ( c2*s3 ), ( c2*c3 ), ( -s2 ), + ( c1*s2*s3 - c3*s1 ), ( c1*c3*s2 + s1*s3 ), ( c1*c2 ) + ); + break; + } + + case eulerOrder::YZX: // Y1-Z2-X3 rotation + { + return tensor + ( + ( c1*c2 ), ( s1*s3 - c1*c3*s2 ), ( c3*s1 + c1*s2*s3 ), + ( s2 ), ( c2*c3 ), ( -c2*s3 ), + ( -c2*s1 ), ( c1*s3 + c3*s1*s2 ), ( c1*c3 - s1*s2*s3 ) + ); + break; + } + + case eulerOrder::ZYX: // Z1-Y2-X3 rotation + { + return tensor + ( + ( c1*c2 ), ( c1*s2*s3 - c3*s1 ), ( s1*s3 + c1*c3*s2 ), + ( c2*s1 ), ( c1*c3 + s1*s2*s3 ), ( c3*s1*s2 - c1*s3 ), + ( -s2 ), ( c2*s3 ), ( c2*c3 ) + ); + break; + } + + case eulerOrder::ZXY: // Z1-X2-Y3 rotation + { + return tensor + ( + ( c1*c3 - s1*s2*s3 ), ( -c2*s1 ), ( c1*s3 + c3*s1*s2 ), + ( c3*s1 + c1*s2*s3 ), ( c1*c2 ), ( s1*s3 - c1*c3*s2 ), + ( -c2*s3 ), ( s2 ), ( c2*c3 ) + ); + break; + } + + default: + FatalErrorInFunction + << "Unknown euler rotation order " + << int(order) << abort(FatalError); + break; + } + + return tensor::I; +} + + +Foam::tensor Foam::coordinateRotations::euler::rotation +( + const vector& angles, + bool degrees +) +{ + return rotation(eulerOrder::ZXZ, angles, degrees); } @@ -100,7 +250,8 @@ Foam::coordinateRotations::euler::euler() : coordinateRotation(), angles_(Zero), - degrees_(true) + degrees_(true), + order_(eulerOrder::ZXZ) {} @@ -108,7 +259,8 @@ Foam::coordinateRotations::euler::euler(const euler& crot) : coordinateRotation(crot), angles_(crot.angles_), - degrees_(crot.degrees_) + degrees_(crot.degrees_), + order_(crot.order_) {} @@ -120,7 +272,8 @@ Foam::coordinateRotations::euler::euler : coordinateRotation(), angles_(angles), - degrees_(degrees) + degrees_(degrees), + order_(eulerOrder::ZXZ) {} @@ -134,7 +287,8 @@ Foam::coordinateRotations::euler::euler : coordinateRotation(), angles_(angle1, angle2, angle3), - degrees_(degrees) + degrees_(degrees), + order_(eulerOrder::ZXZ) {} @@ -142,7 +296,16 @@ Foam::coordinateRotations::euler::euler(const dictionary& dict) : coordinateRotation(), angles_(dict.getCompat<vector>("angles", {{"rotation", 1806}})), - degrees_(dict.lookupOrDefault("degrees", true)) + degrees_(dict.lookupOrDefault("degrees", true)), + order_ + ( + quaternion::eulerOrderNames.lookupOrDefault + ( + "order", + dict, + quaternion::eulerOrder::ZXZ + ) + ) {} @@ -182,6 +345,12 @@ void Foam::coordinateRotations::euler::writeEntry os.writeEntry("degrees", "false"); } + // writeEntryIfDifferent, but with enumerated name + if (order_ != eulerOrder::ZXZ) + { + os.writeEntry("order", quaternion::eulerOrderNames[order_]); + } + os.endBlock(); } diff --git a/src/meshTools/coordinate/rotation/EulerCoordinateRotation.H b/src/meshTools/coordinate/rotation/EulerCoordinateRotation.H index 05ff2d82ac2ed599070ae0d917d8df284d21640b..7d15ee65fce103a222f3d08ab984cc713a62e0ae 100644 --- a/src/meshTools/coordinate/rotation/EulerCoordinateRotation.H +++ b/src/meshTools/coordinate/rotation/EulerCoordinateRotation.H @@ -61,6 +61,7 @@ SourceFiles #define coordinateRotations_euler_H #include "coordinateRotation.H" +#include "quaternion.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -77,6 +78,16 @@ class euler : public coordinateRotation { +public: + + // Public Types + + //- Euler-angle rotation order + using eulerOrder = quaternion::eulerOrder; + + +private: + // Private Data //- The rotation angles @@ -85,6 +96,9 @@ class euler //- Angles measured in degrees bool degrees_; + //- The Euler-angle rotation order (default: zxz) + eulerOrder order_; + public: @@ -126,7 +140,15 @@ public: //- The rotation tensor calculated for the intrinsic Euler //- angles in z-x-z order - static tensor rotation(const vector& angles, bool degrees); + static tensor rotation(const vector& angles, bool degrees=false); + + //- The rotation tensor calculated for given angles and order + static tensor rotation + ( + const eulerOrder order, + const vector& angles, + bool degrees=false + ); // Member Functions diff --git a/src/meshTools/coordinate/rotation/axisAngleRotation.C b/src/meshTools/coordinate/rotation/axisAngleRotation.C index 1e88f383f61a6260794f250b5c0bfe41864daf15..430efd5bad25d7161788a01e258515b574a4cd8f 100644 --- a/src/meshTools/coordinate/rotation/axisAngleRotation.C +++ b/src/meshTools/coordinate/rotation/axisAngleRotation.C @@ -57,6 +57,24 @@ void Foam::coordinateRotations::axisAngle::checkSpec() } +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +Foam::tensor Foam::coordinateRotations::axisAngle::rotation +( + const vector& axis, + const scalar angle, + bool degrees +) +{ + if (mag(angle) < VSMALL || mag(axis) < SMALL) + { + return sphericalTensor::I; // identity rotation + } + + return quaternion(axis, (degrees ? degToRad(angle) : angle)).R(); +} + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::coordinateRotations::axisAngle::axisAngle() @@ -144,12 +162,7 @@ void Foam::coordinateRotations::axisAngle::clear() Foam::tensor Foam::coordinateRotations::axisAngle::R() const { - if (mag(angle_) < VSMALL || mag(axis_) < SMALL) - { - return sphericalTensor::I; // identity rotation - } - - return quaternion(axis_, (degrees_ ? degToRad(angle_) : angle_)).R(); + return rotation(axis_, angle_, degrees_); } diff --git a/src/meshTools/coordinate/rotation/axisAngleRotation.H b/src/meshTools/coordinate/rotation/axisAngleRotation.H index 0b5533331e5cb860bdd334fae95059d45496124b..a4ff281b123d5ef35498aa120ddf39b519cdb724 100644 --- a/src/meshTools/coordinate/rotation/axisAngleRotation.H +++ b/src/meshTools/coordinate/rotation/axisAngleRotation.H @@ -130,6 +130,17 @@ public: virtual ~axisAngle() = default; + // Static Member Functions + + //- The rotation tensor for given axis/angle + static tensor rotation + ( + const vector& axis, + const scalar angle, + bool degrees=false + ); + + // Member Functions //- Reset specification diff --git a/tutorials/incompressible/lumpedPointMotion/building/steady/system/controlDict b/tutorials/incompressible/lumpedPointMotion/building/steady/system/controlDict index 0f76b2efedb42d0abb02e578695b965ea71ff703..cfa0acb757cb87184426e3c46479d6647ac639fe 100644 --- a/tutorials/incompressible/lumpedPointMotion/building/steady/system/controlDict +++ b/tutorials/incompressible/lumpedPointMotion/building/steady/system/controlDict @@ -14,7 +14,12 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -libs ("libmeshTools.so" "liblumpedPointMotion.so"); +libs +( + "libmeshTools.so" + "liblumpedPointMotion.so" + "libfvMotionSolvers.so" +); application simpleFoam; // Change to pimpleFoam for transient