diff --git a/applications/test/field1/Test-field1.C b/applications/test/field1/Test-field1.C index c2b739e398676ebcb43dcd497366c4e44d514eec..3fdfcb6a7ff429d21435a5615f378e53071f8d92 100644 --- a/applications/test/field1/Test-field1.C +++ b/applications/test/field1/Test-field1.C @@ -24,6 +24,10 @@ License Description Simple field tests + Test use of Kahan/Neumaier to extend precision for when running SPDP + mode. Conclusion is that it is easier/quicker to run these summation + loops as double precision (i.e. solveScalar). + \*---------------------------------------------------------------------------*/ #include "primitiveFields.H" @@ -32,6 +36,116 @@ Description using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +template<class Type, class CombineOp, class ResultType> +void sumNeumaier +( + const UList<Type>& vals, + const CombineOp& cop, + ResultType& result +) +{ + // Neumaier version of Kahan + ResultType sum = Zero; + ResultType c = Zero; + for (const Type& vali : vals) + { + ResultType val; + cop(val, vali); + + const ResultType t = sum + val; + + for + ( + direction cmpt = 0; + cmpt < pTraits<ResultType>::nComponents; + cmpt++ + ) + { + if (mag(sum[cmpt]) >= mag(val[cmpt])) + { + // If sum is bigger, low-order digits of input[i] are lost. + c[cmpt] += (sum[cmpt] - t[cmpt]) + val[cmpt]; + } + else + { + // Else low-order digits of sum are lost. + c[cmpt] += (val[cmpt] - t[cmpt]) + sum[cmpt]; + } + } + sum = t; + } + result = sum + c; +} + + +template<class CombineOp, class ResultType> +void sumNeumaier +( + const UList<scalar>& vals, + const CombineOp& cop, + ResultType& result +) +{ + // Neumaier version of Kahan + ResultType sum = Zero; + ResultType c = Zero; + for (const scalar vali : vals) + { + ResultType val; + cop(val, vali); + + const ResultType t = sum + val; + if (mag(sum) >= mag(val)) + { + // If sum is bigger, low-order digits of input[i] are lost. + c += (sum - t) + val; + } + else + { + // Else low-order digits of sum are lost. + c += (val - t) + sum; + } + sum = t; + } + result = sum + c; +} + + +template<class Type> +Type mySum(const UList<Type>& f) +{ + typedef typename Foam::typeOfSolve<Type>::type solveType; + + solveType Sum = Zero; + + if (f.size()) + { + sumNeumaier(f, eqOp<solveType>(), Sum); + } + + return Type(Sum); +} + + +//- The sumSqr always adds only positive numbers. Here there can never be any +// cancellation of truncation errors. +template<class Type> +typename outerProduct1<Type>::type mySumSqr(const UList<Type>& f) +{ + typedef typename outerProduct1<solveScalar>::type prodType; + + prodType result = Zero; + + if (f.size()) + { + sumNeumaier(f, eqSqrOp<prodType>(), result); + } + + return result; +} + + // Main program: int main(int argc, char *argv[]) @@ -63,6 +177,71 @@ int main(int argc, char *argv[]) Info<< "negated: " << lfield << nl; + + // Summation (compile in SPDP) + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Pout.precision(16); + Sout.precision(16); + + const scalar SMALLS(1e-6); + const vector SMALLV(SMALLS, SMALLS, SMALLS); + const scalar GREATS(1e6); + const vector GREATV(GREATS, GREATS, GREATS); + + // scalarField summation + { + scalarField sfield(10, SMALLS); + + sfield[8] = GREATS; + sfield[9] = -sfield[8]; + Info<< "scalarField:" << sfield.size() << nl + << " sum :" << sum(sfield) << nl + << " corrected:" << mySum(sfield) << endl; + } + // vectorField summation + { + vectorField vfield(10, SMALLV); + + vfield[8] = GREATV; + vfield[9] = -vfield[8]; + Info<< "vectorField:" << vfield.size() << nl + << " sum :" << sum(vfield) << nl + << " corrected:" << mySum(vfield) << endl; + } + // sphericalTensorField summation + { + sphericalTensorField tfield(10, SMALLS); + + tfield[8] = GREATS; + tfield[9] = -tfield[8]; + Info<< "sphericalTensorField:" << tfield.size() << nl + << " sum :" << sum(tfield) << nl + << " corrected:" << mySum(tfield) << endl; + } + // symmTensorField summation + { + symmTensorField tfield(10, SMALLS*symmTensor::I); + + tfield[8] = GREATS*symmTensor::I; + tfield[9] = -tfield[8]; + Info<< "symmTensorField:" << tfield.size() << nl + << " sum :" << sum(tfield) << nl + << " corrected:" << mySum(tfield) << endl; + } + // tensorField summation + { + tensorField tfield(10, SMALLS*tensor::I); + + tfield[8] = GREATS*tensor::I; + tfield[9] = -tfield[8]; + Info<< "tensorField:" << tfield.size() << nl + << " sum :" << sum(tfield) << nl + << " corrected:" << mySum(tfield) << endl; + } + + + return 0; } diff --git a/src/OpenFOAM/fields/Fields/Field/FieldFunctions.C b/src/OpenFOAM/fields/Fields/Field/FieldFunctions.C index 21badff3c815d883ef13ea5c528f4f5742bff5bc..0c5091fab6eb8e04a7671b56868cecb95660b019 100644 --- a/src/OpenFOAM/fields/Fields/Field/FieldFunctions.C +++ b/src/OpenFOAM/fields/Fields/Field/FieldFunctions.C @@ -390,14 +390,16 @@ TMP_UNARY_FUNCTION(Type, min) template<class Type> Type sum(const UList<Type>& f) { - Type Sum = Zero; + typedef typename Foam::typeOfSolve<Type>::type solveType; + + solveType Sum = Zero; if (f.size()) { - TFOR_ALL_S_OP_F(Type, Sum, +=, Type, f) + TFOR_ALL_S_OP_FUNC_F(solveType, Sum, +=, solveType, Type, f) } - return Sum; + return Type(Sum); } TMP_UNARY_FUNCTION(Type, sum) diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCellCentresAndVols.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCellCentresAndVols.C index d71ae13909507ed55167b2ba5edf72891bcae738..c7d4b1bd05547a89d9a88fd20c65d78d811de989 100644 --- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCellCentresAndVols.C +++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCellCentresAndVols.C @@ -30,6 +30,7 @@ Description \*---------------------------------------------------------------------------*/ #include "primitiveMesh.H" +#include "PrecisionAdaptor.H" // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // @@ -75,10 +76,17 @@ void Foam::primitiveMesh::makeCellCentresAndVols ( const vectorField& fCtrs, const vectorField& fAreas, - vectorField& cellCtrs, - scalarField& cellVols + vectorField& cellCtrs_s, + scalarField& cellVols_s ) const { + typedef Vector<solveScalar> solveVector; + + PrecisionAdaptor<solveVector, vector> tcellCtrs(cellCtrs_s); + Field<solveVector>& cellCtrs = tcellCtrs.ref(); + PrecisionAdaptor<solveScalar, scalar> tcellVols(cellVols_s); + Field<solveScalar>& cellVols = tcellVols.ref(); + // Clear the fields for accumulation cellCtrs = Zero; cellVols = 0.0; @@ -89,18 +97,18 @@ void Foam::primitiveMesh::makeCellCentresAndVols // first estimate the approximate cell centre as the average of // face centres - vectorField cEst(nCells(), Zero); + Field<solveVector> cEst(nCells(), Zero); labelField nCellFaces(nCells(), Zero); forAll(own, facei) { - cEst[own[facei]] += fCtrs[facei]; + cEst[own[facei]] += solveVector(fCtrs[facei]); ++nCellFaces[own[facei]]; } forAll(nei, facei) { - cEst[nei[facei]] += fCtrs[facei]; + cEst[nei[facei]] += solveVector(fCtrs[facei]); ++nCellFaces[nei[facei]]; } @@ -111,12 +119,15 @@ void Foam::primitiveMesh::makeCellCentresAndVols forAll(own, facei) { + const solveVector fc(fCtrs[facei]); + const solveVector fA(fAreas[facei]); + // Calculate 3*face-pyramid volume - scalar pyr3Vol = - fAreas[facei] & (fCtrs[facei] - cEst[own[facei]]); + solveScalar pyr3Vol = + fA & (fc - cEst[own[facei]]); // Calculate face-pyramid centre - vector pc = (3.0/4.0)*fCtrs[facei] + (1.0/4.0)*cEst[own[facei]]; + solveVector pc = (3.0/4.0)*fc + (1.0/4.0)*cEst[own[facei]]; // Accumulate volume-weighted face-pyramid centre cellCtrs[own[facei]] += pyr3Vol*pc; @@ -127,12 +138,15 @@ void Foam::primitiveMesh::makeCellCentresAndVols forAll(nei, facei) { + const solveVector fc(fCtrs[facei]); + const solveVector fA(fAreas[facei]); + // Calculate 3*face-pyramid volume - scalar pyr3Vol = - fAreas[facei] & (cEst[nei[facei]] - fCtrs[facei]); + solveScalar pyr3Vol = + fA & (cEst[nei[facei]] - fc); // Calculate face-pyramid centre - vector pc = (3.0/4.0)*fCtrs[facei] + (1.0/4.0)*cEst[nei[facei]]; + solveVector pc = (3.0/4.0)*fc + (1.0/4.0)*cEst[nei[facei]]; // Accumulate volume-weighted face-pyramid centre cellCtrs[nei[facei]] += pyr3Vol*pc; diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C index 05a56e62d4c8db12bb324a89aa5123a3adce9736..4f4a1445f37c719769a6bf878fd90283dfea6751 100644 --- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C +++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshCheck.C @@ -61,22 +61,22 @@ bool Foam::primitiveMesh::checkClosedBoundary // Loop through all boundary faces and sum up the face area vectors. // For a closed boundary, this should be zero in all vector components - vector sumClosed(Zero); - scalar sumMagClosedBoundary = 0; + Vector<solveScalar> sumClosed(Zero); + solveScalar sumMagClosedBoundary = 0; for (label facei = nInternalFaces(); facei < areas.size(); facei++) { if (!internalOrCoupledFaces.size() || !internalOrCoupledFaces[facei]) { - sumClosed += areas[facei]; + sumClosed += Vector<solveScalar>(areas[facei]); sumMagClosedBoundary += mag(areas[facei]); } } - reduce(sumClosed, sumOp<vector>()); - reduce(sumMagClosedBoundary, sumOp<scalar>()); + reduce(sumClosed, sumOp<Vector<solveScalar>>()); + reduce(sumMagClosedBoundary, sumOp<solveScalar>()); - vector openness = sumClosed/(sumMagClosedBoundary + VSMALL); + Vector<solveScalar> openness = sumClosed/(sumMagClosedBoundary + VSMALL); if (cmptMax(cmptMag(openness)) > closedThreshold_) { diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.C index 95db7415385f9d71cfef1f8742ad45c5f03847f5..98a8b032b7332216ce0bfd939ba8b8fa4bcf9157 100644 --- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.C +++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshCheck/primitiveMeshTools.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2012-2016 OpenFOAM Foundation - Copyright (C) 2017-2018 OpenCFD Ltd. + Copyright (C) 2017-2019 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -420,6 +420,7 @@ Foam::tmp<Foam::scalarField> Foam::primitiveMeshTools::faceFlatness tmp<scalarField> tfaceFlatness(new scalarField(mesh.nFaces(), 1.0)); scalarField& faceFlatness = tfaceFlatness.ref(); + typedef Vector<solveScalar> solveVector; forAll(fcs, facei) { @@ -427,20 +428,20 @@ Foam::tmp<Foam::scalarField> Foam::primitiveMeshTools::faceFlatness if (f.size() > 3 && magAreas[facei] > ROOTVSMALL) { - const point& fc = fCtrs[facei]; + const solveVector fc = fCtrs[facei]; // Calculate the sum of magnitude of areas and compare to magnitude // of sum of areas. - scalar sumA = 0.0; + solveScalar sumA = 0.0; forAll(f, fp) { - const point& thisPoint = p[f[fp]]; - const point& nextPoint = p[f.nextLabel(fp)]; + const solveVector thisPoint = p[f[fp]]; + const solveVector nextPoint = p[f.nextLabel(fp)]; // Triangle around fc. - vector n = 0.5*((nextPoint - thisPoint)^(fc - thisPoint)); + solveVector n = 0.5*((nextPoint - thisPoint)^(fc - thisPoint)); sumA += mag(n); } diff --git a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshFaceCentresAndAreas.C b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshFaceCentresAndAreas.C index dfd90c25ebe01b562d1db3116edc75150ad28f8d..ac906072acf557caf7e572221ffd844466b41904 100644 --- a/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshFaceCentresAndAreas.C +++ b/src/OpenFOAM/meshes/primitiveMesh/primitiveMeshFaceCentresAndAreas.C @@ -84,7 +84,7 @@ void Foam::primitiveMesh::makeFaceCentresAndAreas forAll(fs, facei) { const labelList& f = fs[facei]; - label nPoints = f.size(); + const label nPoints = f.size(); // If the face is a triangle, do a direct calculation for efficiency // and to avoid round-off error-related problems @@ -95,26 +95,29 @@ void Foam::primitiveMesh::makeFaceCentresAndAreas } else { - vector sumN = Zero; - scalar sumA = 0.0; - vector sumAc = Zero; + typedef Vector<solveScalar> solveVector; - point fCentre = p[f[0]]; + solveVector sumN = Zero; + solveScalar sumA = 0.0; + solveVector sumAc = Zero; + + solveVector fCentre = p[f[0]]; for (label pi = 1; pi < nPoints; pi++) { - fCentre += p[f[pi]]; + fCentre += solveVector(p[f[pi]]); } fCentre /= nPoints; for (label pi = 0; pi < nPoints; pi++) { - const point& nextPoint = p[f[(pi + 1) % nPoints]]; - - vector c = p[f[pi]] + nextPoint + fCentre; - vector n = (nextPoint - p[f[pi]])^(fCentre - p[f[pi]]); - scalar a = mag(n); + const label nextPi(pi == nPoints-1 ? 0 : pi+1); + const solveVector nextPoint(p[f[nextPi]]); + const solveVector thisPoint(p[f[pi]]); + solveVector c = thisPoint + nextPoint + fCentre; + solveVector n = (nextPoint - thisPoint)^(fCentre - thisPoint); + solveScalar a = mag(n); sumN += n; sumA += a; sumAc += a*c; diff --git a/src/OpenFOAM/primitives/Scalar/scalar/scalar.H b/src/OpenFOAM/primitives/Scalar/scalar/scalar.H index e6888e71eb78ccb835c3ed9f563ffe9164f2b00b..25359893165017312b98436491623de9959d1c98 100644 --- a/src/OpenFOAM/primitives/Scalar/scalar/scalar.H +++ b/src/OpenFOAM/primitives/Scalar/scalar/scalar.H @@ -154,7 +154,7 @@ namespace Foam #endif // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -// Additional transcendental functions +// Additional transcendental functions and specialisations namespace Foam { @@ -172,6 +172,15 @@ namespace Foam //- Lower incomplete gamma function scalar incGamma_P(const scalar a, const scalar x); + + //- Type to use for extended precision + template<> + class typeOfSolve<scalar> + { + public: + + typedef solveScalar type; + }; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/src/OpenFOAM/primitives/SphericalTensor/SphericalTensor.H b/src/OpenFOAM/primitives/SphericalTensor/SphericalTensor.H index 47d895d54b4e264a4c19a1606d9106ee4069bf8f..3ac5117a5d84711b58b8e74aa108eac5e63cb2e5 100644 --- a/src/OpenFOAM/primitives/SphericalTensor/SphericalTensor.H +++ b/src/OpenFOAM/primitives/SphericalTensor/SphericalTensor.H @@ -137,6 +137,15 @@ struct is_contiguous_scalar<SphericalTensor<Cmpt>> {}; +template<class Cmpt> +class typeOfSolve<SphericalTensor<Cmpt>> +{ +public: + + typedef SphericalTensor<solveScalar> type; +}; + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam diff --git a/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H b/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H index 37e8bf8c98b02a3ddd34f0c9605a9d5aa8a4a220..998c6b5b3394673975cfb44d31e27bbbb09daa54 100644 --- a/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H +++ b/src/OpenFOAM/primitives/SymmTensor/SymmTensor.H @@ -176,6 +176,15 @@ public: }; +template<class Cmpt> +class typeOfSolve<SymmTensor<Cmpt>> +{ +public: + + typedef SymmTensor<solveScalar> type; +}; + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam diff --git a/src/OpenFOAM/primitives/Tensor/Tensor.H b/src/OpenFOAM/primitives/Tensor/Tensor.H index 561232f630bb63f2b75401e7ad26d6cc604d0f77..253bcc00d587b8cb44d418ce3eac4dc7466daabb 100644 --- a/src/OpenFOAM/primitives/Tensor/Tensor.H +++ b/src/OpenFOAM/primitives/Tensor/Tensor.H @@ -344,6 +344,15 @@ public: }; +template<class Cmpt> +class typeOfSolve<Tensor<Cmpt>> +{ +public: + + typedef Tensor<solveScalar> type; +}; + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam diff --git a/src/OpenFOAM/primitives/Vector/Vector.H b/src/OpenFOAM/primitives/Vector/Vector.H index f4fc10316aa7bde5f1abc82ae949968231083dac..100a6f704da71733fb055bc5dc285db06d6ca397 100644 --- a/src/OpenFOAM/primitives/Vector/Vector.H +++ b/src/OpenFOAM/primitives/Vector/Vector.H @@ -167,6 +167,15 @@ public: }; +template<class Cmpt> +class typeOfSolve<Vector<Cmpt>> +{ +public: + + typedef Vector<solveScalar> type; +}; + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam diff --git a/src/OpenFOAM/primitives/VectorSpace/products.H b/src/OpenFOAM/primitives/VectorSpace/products.H index e0643885cce7586f038ba1409242c24c7e0d5214..fcf9f4a46494ee17fbb845918bf0493e2856b8b5 100644 --- a/src/OpenFOAM/primitives/VectorSpace/products.H +++ b/src/OpenFOAM/primitives/VectorSpace/products.H @@ -35,6 +35,7 @@ Description #ifndef products_H #define products_H +#include "direction.H" #include "pTraits.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -72,6 +73,16 @@ class symmTypeOfRank {}; +//- The extended precision type (solveScalar for float) +template<class Type> +class typeOfSolve +{ +public: + + typedef Type type; +}; + + //- The magnitude type for given argument. template<class arg1> class typeOfMag