diff --git a/src/functionObjects/field/Make/files b/src/functionObjects/field/Make/files index fe9eec3283..bd6b60b373 100644 --- a/src/functionObjects/field/Make/files +++ b/src/functionObjects/field/Make/files @@ -154,4 +154,6 @@ resolutionIndex/resolutionIndexModels/CelikEtaIndex/CelikEtaIndex.C age/age.C comfort/comfort.C +fieldStatistics/fieldStatistics.cxx + LIB = $(FOAM_LIBBIN)/libfieldFunctionObjects diff --git a/src/functionObjects/field/fieldStatistics/fieldStatistics.H b/src/functionObjects/field/fieldStatistics/fieldStatistics.H new file mode 100644 index 0000000000..b00cd57fd5 --- /dev/null +++ b/src/functionObjects/field/fieldStatistics/fieldStatistics.H @@ -0,0 +1,367 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2025 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +Class + Foam::functionObjects::fieldStatistics + +Group + grpFieldFunctionObjects + +Description + Calculates various statistics of the specified fields. + + Operands: + \table + Operand | Type | Location + input | vol\Field(s) | Registry + output file | dat | \/postProcessing/\/\/\ + output field | - | - + \endtable + + where \c \=Scalar/Vector/SphericalTensor/SymmTensor/Tensor. + +Usage + Minimal example by using \c system/controlDict.functions: + \verbatim + fieldStatistics1 + { + // Mandatory entries + type fieldStatistics; + libs (fieldFunctionObjects); + fields (); + statistics (); + + // Optional entries + mode ; + mean ; + extrema ; + internal ; + + // Inherited entries + ... + } + \endverbatim + + where the entries mean: + \table + Property | Description | Type | Reqd | Deflt + type | Type name: fieldStatistics | word | yes | - + libs | Library name: fieldFunctionObjects | word | yes | - + fields | List of operand fields | wordList | yes | - + statistics | List of operand statistics | wordList | yes | - + mode | Output format of the statistical results | word | no | magnitude + mean | Type of the mean operation | word | no | arithmetic + internal | Flag to use internal fields only in computing statistics | bool | no | false + extrema | Flag to enable extrema data calculations | bool | no | false + \endtable + + Available statistics of the operand field through the \c statistics entry: + \verbatim + min | Minimum value + max | Maximum value + mean | Arithmetic mean value (optionally volume-weighted) + variance | Sample variance value (unbiased) + \endverbatim + + Options for the \c mode entry: + \verbatim + magnitude | Output statistics magnitude-wise + component | Output statistics separately for each component + \endverbatim + + Options for the \c mean entry: + \verbatim + arithmetic | Arithmetic mean (average) + volumetric | Volume-weighted arithmetic mean + \endverbatim + + The inherited entries are elaborated in: + - \link functionObject.H \endlink + - \link writeFile.H \endlink + +SourceFiles + fieldStatistics.cxx + fieldStatisticsImpl.cxx + +\*---------------------------------------------------------------------------*/ + +#ifndef functionObjects_fieldStatistics_H +#define functionObjects_fieldStatistics_H + +#include "fvMeshFunctionObject.H" +#include "writeFile.H" +#include "volFieldSelection.H" +#include +#include + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace functionObjects +{ + +/*---------------------------------------------------------------------------*\ + Class fieldStatistics Declaration +\*---------------------------------------------------------------------------*/ + +class fieldStatistics +: + public fvMeshFunctionObject, + public writeFile +{ + // Private Enumerations + + //- Options for the output format of the statistical results + enum class modeType : char + { + MAG = 0, //!< "Output statistics magnitude-wise" + CMPT //!< "Output statistics separately for each component" + }; + + //- Names for modeType + static const Enum modeTypeNames_; + + //- Options for the mean type of the specified fields + enum class meanType : char + { + ARITHMETIC = 0, //!< "Arithmetic mean (average)" + VOLUMETRIC //!< "Volume-weighted arithmetic mean" + }; + + //- Names for meanType + static const Enum meanTypeNames_; + + //- Options for the type of statistics calculation + enum class calcType : char + { + UNKNOWN = 0, //!< placeholder + MIN, //!< min value + MAX, //!< max value + MEAN, //!< mean value + VARIANCE //!< variance + }; + + //- Names for calcType + static const Enum calcTypeNames_; + + + // Private Classes + + //- Type-safe union for input field types + using variantInput = std::variant + < + scalarField, + vectorField, + sphericalTensorField, + symmTensorField, + tensorField + >; + + //- Type-safe union for output data types + using variantOutput = std::variant + < + scalar, + vector, + sphericalTensor, + symmTensor, + tensor + >; + + //- Class to encapsulate information about specified statistic + struct statistic + { + //- Name of the statistic + word name_; + + //- Returns the value of the specified statistic + std::function calc; + }; + + //- Class to encapsulate the data about minima and maxima + struct extremaData + { + //- Value of the extremum + variantOutput value_; + + //- Processor index of the extremum + label procID_; + + //- Cell index of the extremum + label cellID_; + + //- Position (cell or face centre) of the extremum + point position_; + }; + + + // Private Data + + //- Flag to use internal fields only in computing statistics + bool internal_; + + //- Flag to enable extrema data calculations + bool extrema_; + + //- Output-format mode - only applicable for tensor ranks > 0 + modeType mode_; + + //- Type of the mean of the specified fields + meanType mean_; + + //- Operand fields on which statistics are computed + volFieldSelection fieldSet_; + + //- List of file pointers; one file per field + HashPtrTable filePtrs_; + + //- List of file pointers for extrema data; one file per field + HashPtrTable extremaFilePtrs_; + + //- Hash table containing all specified statistics + HashTable statistics_; + + //- Hash table containing all statistical results per field + HashTable> results_; + + //- Hash table containing the results of the extrema per field + HashTable> extremaResults_; + + + // Private Member Functions + + //- Return the statistic container + statistic createStatistic(const word& statName, const modeType mode); + + //- Compute the specified statistics of a given field + template + bool calcStat(const word& fieldName); + + + // Central tendency statistics + + //- Return the arithmetic mean of the given input field + template + T calcMean(const Field& field) const; + + + // Dispersion statistics + + //- Return the minimum value of the given input field + // Store the processor index, cell index and location of the minimum + template + T calcMin(const Field& field) const; + + //- Return the maximum value of the given input field + // Store the processor index, cell index and location of the maximum + template + T calcMax(const Field& field) const; + + //- Return the sample variance of the given input field + template + T calcVariance(const Field& field) const; + + //- Return a combined field: internal + flattened boundary + template + tmp> + flatten(const GeoField& field) const; + + //- Return the extrema data of the specified field + template + Pair calcExtremaData(const GeoField& field) const; + + //- Output the file header information + void writeFileHeader(Ostream& os, const word& fieldName); + + //- Write the statistical data to the specified file + void writeStatData(); + + //- Write the statistical data to the standard stream + void logStatData(); + + //- Output the extrema-data file header information + void writeExtremaFileHeader(Ostream& os, const word& fieldName); + + //- Write extrema data to the specified file + void writeExtremaData(); + + //- Write extrema data to the standard stream + void logExtremaData(); + + +public: + + //- Runtime type information + TypeName("fieldStatistics"); + + + // Generated Methods + + //- No copy construct + fieldStatistics(const fieldStatistics&) = delete; + + //- No copy assignment + void operator=(const fieldStatistics&) = delete; + + + // Constructors + + //- Construct from name, Time and dictionary + fieldStatistics + ( + const word& name, + const Time& runTime, + const dictionary& dict + ); + + + //- Destructor + virtual ~fieldStatistics() = default; + + + // Member Functions + + //- Read function object settings + virtual bool read(const dictionary&); + + //- Execute function object + virtual bool execute(); + + //- Write function object results + virtual bool write(); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace functionObjects +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/functionObjects/field/fieldStatistics/fieldStatistics.cxx b/src/functionObjects/field/fieldStatistics/fieldStatistics.cxx new file mode 100644 index 0000000000..259a63d3fb --- /dev/null +++ b/src/functionObjects/field/fieldStatistics/fieldStatistics.cxx @@ -0,0 +1,549 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2025 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +\*---------------------------------------------------------------------------*/ + +#include "fieldStatistics.H" +#include "fieldTypes.H" +#include "addToRunTimeSelectionTable.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ +namespace functionObjects +{ + defineTypeNameAndDebug(fieldStatistics, 0); + addToRunTimeSelectionTable(functionObject, fieldStatistics, dictionary); +} +} + +const Foam::Enum +< + Foam::functionObjects::fieldStatistics::modeType +> +Foam::functionObjects::fieldStatistics::modeTypeNames_ +({ + { modeType::MAG, "magnitude" }, + { modeType::CMPT, "component" }, +}); + +const Foam::Enum +< + Foam::functionObjects::fieldStatistics::meanType +> +Foam::functionObjects::fieldStatistics::meanTypeNames_ +({ + { meanType::ARITHMETIC, "arithmetic" }, + { meanType::VOLUMETRIC, "volumetric" }, +}); + +const Foam::Enum +< + Foam::functionObjects::fieldStatistics::calcType +> +Foam::functionObjects::fieldStatistics::calcTypeNames_ +({ + // UNKNOWN is not enumerated + { calcType::MIN, "min" }, + { calcType::MAX, "max" }, + { calcType::MEAN, "mean" }, + { calcType::VARIANCE, "variance" }, +}); + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Implementation +#include "fieldStatisticsImpl.cxx" + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::functionObjects::fieldStatistics::statistic +Foam::functionObjects::fieldStatistics::createStatistic +( + const word& statName, + const modeType mode +) +{ + statistic stat; + stat.name_ = statName; + + const auto statType(calcTypeNames_.lookup(statName, calcType::UNKNOWN)); + + stat.calc = [this, statType, mode](variantInput input) -> variantOutput + { + return std::visit + ( + [this, statType, mode](auto&& arg) -> variantOutput + { + using T = std::decay_t; + using value_type = typename T::value_type; + + switch (statType) + { + case calcType::MIN : + { + if (mode == modeType::MAG) + return calcMin(mag(arg)); + else + return calcMin(arg); + } + case calcType::MAX : + { + if (mode == modeType::MAG) + return calcMax(mag(arg)); + else + return calcMax(arg); + } + case calcType::MEAN : + { + if (mode == modeType::MAG) + return calcMean(mag(arg)); + else + return calcMean(arg); + } + case calcType::VARIANCE : + { + if (mode == modeType::MAG) + return calcVariance(mag(arg)); + else + return calcVariance(arg); + } + default : + { + // Default case (for compiler) + return scalar(0); + } + } + }, + input + ); + }; + + return stat; +} + + +void Foam::functionObjects::fieldStatistics::writeFileHeader +( + Ostream& os, + const word& fieldName +) +{ + writeHeader(os, word("Field Statistics: " + fieldName)); + writeCommented(os, "Time"); + + // Number of input statistics (i.e., statistics_) should be the same with + // that of output statistics (i.e., results_). However, for consistency, + // the output file columns are based on output statistics. + const auto& result = results_(fieldName); + + for (const auto& iter : result.csorted()) + { + const word& name = iter.key(); + writeTabbed(os, name); + } + os << endl; +} + + +void Foam::functionObjects::fieldStatistics::writeStatData() +{ + for (const word& fieldName : fieldSet_.selectionNames()) + { + const auto& results = results_(fieldName); + + if (!results.size()) break; + + OFstream& file = *filePtrs_(fieldName); + + writeCurrentTime(file); + + for (const auto& iter : results.csorted()) + { + const variantOutput& value = iter.val(); + + std::visit + ( + [&file](const auto& v) + { + if constexpr + ( + is_vectorspace_v> + ) + { + for (const auto& val : v) file<< token::TAB << val; + } + else + { + file<< token::TAB << v; + } + }, + value + ); + } + file<< nl; + } +} + + +void Foam::functionObjects::fieldStatistics::logStatData() +{ + for (const word& fieldName : fieldSet_.selectionNames()) + { + const auto& results = results_(fieldName); + + if (!results.size()) break; + + const word outputName + ( + (mode_ == modeType::MAG) + ? word("mag(" + fieldName + ")") + : fieldName + ); + + Info<< nl << " Field " << outputName << nl; + + for (const auto& iter : results.csorted()) + { + const word& name = iter.key(); + const variantOutput& value = iter.val(); + + Info<< " " << name; + std::visit + ( + [](const auto& v) + { + if constexpr + ( + is_vectorspace_v> + ) + { + for (const auto& val : v) Info<< ' ' << val; + } + else + { + Info<< ' ' << v; + } + }, + value + ); + Info<< nl; + } + } + Info<< endl; +} + + +void Foam::functionObjects::fieldStatistics::writeExtremaFileHeader +( + Ostream& os, + const word& fieldName +) +{ + writeHeader(os, word("Field Extrema Data: " + fieldName)); + writeCommented(os, "Time"); + writeTabbed(os, "min"); + writeTabbed(os, "min_procID"); + writeTabbed(os, "min_cellID"); + writeTabbed(os, "min_position"); + writeTabbed(os, "max"); + writeTabbed(os, "max_procID"); + writeTabbed(os, "max_cellID"); + writeTabbed(os, "max_position"); + os << endl; +} + + +void Foam::functionObjects::fieldStatistics::writeExtremaData() +{ + for (const word& fieldName : fieldSet_.selectionNames()) + { + const auto& min = extremaResults_(fieldName).first(); + const auto& max = extremaResults_(fieldName).second(); + + OFstream& file = *extremaFilePtrs_(fieldName); + + writeCurrentTime(file); + + file<< token::TAB; + + std::visit([&file](const auto& v){ file<< v; }, min.value_); + + file<< token::TAB + << min.procID_ << token::TAB + << min.cellID_ << token::TAB + << min.position_ << token::TAB; + + std::visit([&file](const auto& v){ file<< v; }, max.value_); + + file<< token::TAB + << max.procID_ << token::TAB + << max.cellID_ << token::TAB + << max.position_ << nl; + } +} + + +void Foam::functionObjects::fieldStatistics::logExtremaData() +{ + for (const word& fieldName : fieldSet_.selectionNames()) + { + const auto& min = extremaResults_(fieldName).first(); + const auto& max = extremaResults_(fieldName).second(); + + const word outputName + ( + (mode_ == modeType::MAG) + ? word("mag(" + fieldName + ")") + : fieldName + ); + + std::visit + ( + [outputName](const auto& v) + { + Info<< " min(" << outputName << ") = " << v; + }, + min.value_ + ); + + Info<< " in cell " << min.cellID_ + << " at location " << min.position_ + << " on processor " << min.procID_; + + std::visit + ( + [outputName](const auto& v) + { + Info<< nl << " max(" << outputName << ") = " << v; + }, + max.value_ + ); + + Info<< " in cell " << max.cellID_ + << " at location " << max.position_ + << " on processor " << max.procID_ << endl; + } +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::functionObjects::fieldStatistics::fieldStatistics +( + const word& name, + const Time& runTime, + const dictionary& dict +) +: + fvMeshFunctionObject(name, runTime, dict), + writeFile(mesh_, name, typeName, dict), + internal_(false), + extrema_(false), + mode_(modeType::MAG), + mean_(meanType::ARITHMETIC), + fieldSet_(mesh_) +{ + read(dict); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +bool Foam::functionObjects::fieldStatistics::read(const dictionary& dict) +{ + if (!(fvMeshFunctionObject::read(dict) && writeFile::read(dict))) + { + return false; + } + + internal_ = dict.getOrDefault("internal", false); + extrema_ = dict.getOrDefault("extrema", false); + + mode_ = modeTypeNames_.getOrDefault("mode", dict, modeType::MAG); + mean_ = meanTypeNames_.getOrDefault("mean", dict, meanType::ARITHMETIC); + + // Reset and reprepare the input field names + fieldSet_.clear(); + fieldSet_.read(dict); + + // Reset and reprepare the input statistics + statistics_.clear(); + + const wordHashSet statNames(dict.get("statistics")); + // NOTE: could also filter out bad/unknown types here + + for (const word& statName : statNames.sortedToc()) + { + statistics_.insert(statName, createStatistic(statName, mode_)); + } + + // Reset the output-statistics container + results_.clear(); + + // Reset the extrema-data container + extremaResults_.clear(); + + return true; +} + + +bool Foam::functionObjects::fieldStatistics::execute() +{ + fieldSet_.updateSelection(); + + // Calculate the specified statistics for the specified fields + for (const word& fieldName : fieldSet_.selectionNames()) + { + const bool ok = + ( + calcStat(fieldName) + || calcStat(fieldName) + || calcStat(fieldName) + || calcStat(fieldName) + || calcStat(fieldName) + ); + + if (!ok) + { + WarningInFunction + << "Unable to find field " << fieldName << endl; + } + } + + // Store the statistical results into the state containers + for (const word& fieldName : fieldSet_.selectionNames()) + { + const auto& results = results_(fieldName); + + for (const auto& iter : results.csorted()) + { + const word& name = iter.key(); + const variantOutput& value = iter.val(); + + const word variableName(fieldName + "_" + name); + + std::visit + ( + [this, variableName](const auto& v) + { + this->setResult(variableName, v); + }, + value + ); + } + + if (extrema_) + { + const auto& min = extremaResults_(fieldName).first(); + std::visit + ( + [this, fieldName](const auto& v) + { + this->setResult(word(fieldName + "_min"), v); + }, + min.value_ + ); + this->setResult(word(fieldName + "_min_procID"), min.procID_); + this->setResult(word(fieldName + "_min_cellID"), min.cellID_); + this->setResult(word(fieldName + "_min_position"), min.position_); + + const auto& max = extremaResults_(fieldName).second(); + std::visit + ( + [this, fieldName](const auto& v) + { + this->setResult(word(fieldName + "_max"), v); + }, + max.value_ + ); + this->setResult(word(fieldName + "_max_procID"), max.procID_); + this->setResult(word(fieldName + "_max_cellID"), max.cellID_); + this->setResult(word(fieldName + "_max_position"), max.position_); + } + } + + return true; +} + + +bool Foam::functionObjects::fieldStatistics::write() +{ + Log << type() << ' ' << name() << " write:" << endl; + + // Create an output file per field + if (writeToFile() && !writtenHeader_) + { + for (const word& fieldName : fieldSet_.selectionNames()) + { + filePtrs_.set(fieldName, newFileAtStartTime(fieldName)); + + OFstream& file = *filePtrs_(fieldName); + + writeFileHeader(file, fieldName); + } + + if (extrema_) + { + for (const word& fieldName : fieldSet_.selectionNames()) + { + extremaFilePtrs_.set + ( + fieldName, + newFileAtStartTime(word("fieldMinMax_" + fieldName)) + ); + + OFstream& file = *extremaFilePtrs_(fieldName); + writeExtremaFileHeader(file, fieldName); + } + } + + writtenHeader_ = true; + } + + // Write the statistical results to the output file if requested + if (writeToFile()) + { + if (extrema_) writeExtremaData(); + + writeStatData(); + } + + // Print the statistical results to the standard stream if requested + if (log) + { + if (extrema_) logExtremaData(); + + logStatData(); + } + + return true; +} + + +// ************************************************************************* // diff --git a/src/functionObjects/field/fieldStatistics/fieldStatisticsImpl.cxx b/src/functionObjects/field/fieldStatistics/fieldStatisticsImpl.cxx new file mode 100644 index 0000000000..21163704ed --- /dev/null +++ b/src/functionObjects/field/fieldStatistics/fieldStatisticsImpl.cxx @@ -0,0 +1,297 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2025 OpenCFD Ltd. +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +\*---------------------------------------------------------------------------*/ + +#include "fieldStatistics.H" +#include "volFields.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +template +Foam::tmp> +Foam::functionObjects::fieldStatistics::flatten(const GeoField& fld) const +{ + typedef typename GeoField::value_type value_type; + typedef Field FieldType; + + label n(0); + + if (!internal_) + { + // Count boundary values + for (const auto& pfld : fld.boundaryField()) + { + if (!pfld.coupled()) + { + n += pfld.size(); + } + } + } + + if (!n) + { + // No boundary values - quick return + return tmp(fld.primitiveField()); + } + + + // Combined internal + flattened boundary fields + // - this adds extra storage, but necessary since the visitor pattern + // requires a single input + + auto tflatFld = tmp::New(fld.size() + n); + auto& flatFld = tflatFld.ref(); + + // Copy internal values + flatFld.slice(0, fld.size()) = fld.primitiveField(); + + // Copy boundary values + n = fld.size(); + for (const auto& pfld : fld.boundaryField()) + { + if (!pfld.coupled()) + { + flatFld.slice(n, pfld.size()) = pfld; + n += pfld.size(); + } + } + + return tflatFld; +} + + +template +bool Foam::functionObjects::fieldStatistics::calcStat(const word& fieldName) +{ + typedef GeometricField VolFieldType; + + const auto* fieldp = obr_.cfindObject(fieldName); + if (!fieldp) + { + return false; + } + const auto& field = *fieldp; + + tmp> tfullfield = flatten(field); + const auto& fullfield = tfullfield.cref(); + + HashTable result; + for (const auto& iter : statistics_.csorted()) + { + const statistic& stat = iter.val(); + + // Assign a new entry, overwriting existing entries + result.set(stat.name_, stat.calc(fullfield)); + } + + results_.set(fieldName, result); + + if (extrema_) + { + extremaResults_.set + ( + fieldName, + (mode_ == modeType::MAG) + ? calcExtremaData(mag(field)()) + : calcExtremaData(field) + ); + } + + return true; +} + + +template +T Foam::functionObjects::fieldStatistics::calcMean(const Field& field) const +{ + if (internal_ && (mean_ == meanType::VOLUMETRIC)) + { + return gWeightedAverage(mesh_.V(), field); + } + + return gAverage(field); +} + + +template +T Foam::functionObjects::fieldStatistics::calcMin(const Field& field) const +{ + return gMin(field); +} + + +template +T Foam::functionObjects::fieldStatistics::calcMax(const Field& field) const +{ + return gMax(field); +} + + +template +T Foam::functionObjects::fieldStatistics::calcVariance +( + const Field& field +) const +{ + const T avg(calcMean(field)); + + T var = Zero; + for (const auto& elem : field) + { + var += (elem - avg); + } + + label count = field.size(); + Foam::sumReduce(var, count); + + if (count <= 1) + { + return Zero; + } + + return 1.0/(count - 1.0)*var; +} + + +template +Foam::Pair +Foam::functionObjects::fieldStatistics::calcExtremaData +( + const GeoField& field +) const +{ + typedef typename GeoField::value_type value_type; + + const label proci = UPstream::myProcNo(); + + List minVs(UPstream::nProcs(), pTraits::max); + List