/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation Copyright (C) 2019 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 "interpolationTable.H" #include "openFoamTableReader.H" // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * // template void Foam::interpolationTable::readTable() { // preserve the original (unexpanded) fileName to avoid absolute paths // appearing subsequently in the write() method fileName fName(fileName_); fName.expand(); // Read data from file reader_()(fName, *this); if (this->empty()) { FatalErrorInFunction << "table read from " << fName << " is empty" << nl << exit(FatalError); } // Check that the data are okay check(); } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // template Foam::interpolationTable::interpolationTable() : List(), bounding_(bounds::repeatableBounding::WARN), fileName_("fileNameIsUndefined"), reader_(nullptr) {} template Foam::interpolationTable::interpolationTable ( const List>& values, const bounds::repeatableBounding bounding, const fileName& fName ) : List(values), bounding_(bounding), fileName_(fName), reader_(nullptr) {} template Foam::interpolationTable::interpolationTable(const fileName& fName) : List(), bounding_(bounds::repeatableBounding::WARN), fileName_(fName), reader_(new openFoamTableReader(dictionary())) { readTable(); } template Foam::interpolationTable::interpolationTable(const dictionary& dict) : List(), bounding_ ( bounds::repeatableBoundingNames.lookupOrDefault ( "outOfBounds", dict, bounds::repeatableBounding::WARN, true // Failsafe behaviour ) ), fileName_(dict.get("file")), reader_(tableReader::New(dict)) { readTable(); } template Foam::interpolationTable::interpolationTable ( const interpolationTable& tbl ) : List(tbl), bounding_(tbl.bounding_), fileName_(tbl.fileName_), reader_(tbl.reader_.clone()) {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // template void Foam::interpolationTable::check() const { const List& list = *this; scalar prevValue(0); label i = 0; for (const auto& item : list) { const scalar& currValue = item.first(); // Avoid duplicate values (divide-by-zero error) if (i && currValue <= prevValue) { FatalErrorInFunction << "out-of-order value: " << currValue << " at index " << i << nl << exit(FatalError); } prevValue = currValue; ++i; } } template void Foam::interpolationTable::write(Ostream& os) const { os.writeEntry("file", fileName_); os.writeEntry("outOfBounds", bounds::repeatableBoundingNames[bounding_]); if (reader_.valid()) { reader_->write(os); } } template Type Foam::interpolationTable::rateOfChange(scalar lookupValue) const { const List& list = *this; const label n = list.size(); if (n <= 1) { // Not enough entries for a rate of change return Zero; } const scalar minLimit = list.first().first(); const scalar maxLimit = list.last().first(); if (lookupValue < minLimit) { switch (bounding_) { case bounds::repeatableBounding::ERROR: { FatalErrorInFunction << "value (" << lookupValue << ") less than lower " << "bound (" << minLimit << ")\n" << exit(FatalError); break; } case bounds::repeatableBounding::WARN: { WarningInFunction << "value (" << lookupValue << ") less than lower " << "bound (" << minLimit << ")\n" << " Zero rate of change." << endl; // Behaviour as per CLAMP return Zero; break; } case bounds::repeatableBounding::CLAMP: { return Zero; break; } case bounds::repeatableBounding::REPEAT: { // Adjust lookupValue to >= minLimit scalar span = maxLimit-minLimit; lookupValue = fmod(lookupValue - minLimit, span) + minLimit; break; } } } else if (lookupValue >= maxLimit) { switch (bounding_) { case bounds::repeatableBounding::ERROR: { FatalErrorInFunction << "value (" << lookupValue << ") greater than upper " << "bound (" << maxLimit << ")\n" << exit(FatalError); break; } case bounds::repeatableBounding::WARN: { WarningInFunction << "value (" << lookupValue << ") greater than upper " << "bound (" << maxLimit << ")\n" << " Zero rate of change." << endl; // Behaviour as per CLAMP return Zero; break; } case bounds::repeatableBounding::CLAMP: { return Zero; break; } case bounds::repeatableBounding::REPEAT: { // Adjust lookupValue <= maxLimit scalar span = maxLimit-minLimit; lookupValue = fmod(lookupValue - minLimit, span) + minLimit; break; } } } label lo = 0; label hi = 0; // Look for the correct range for (label i = 0; i < n; ++i) { if (lookupValue >= list[i].first()) { lo = hi = i; } else { hi = i; break; } } if (lo == hi) { return Zero; } else if (hi == 0) { // This treatment should only occur under these conditions: // -> the 'REPEAT' treatment // -> (0 <= value <= minLimit) // -> minLimit > 0 // Use the value at maxLimit as the value for value=0 lo = n - 1; return ( (list[hi].second() - list[lo].second()) / (list[hi].first() + minLimit - list[lo].first()) ); } // Normal rate of change return ( (list[hi].second() - list[lo].second()) / (list[hi].first() - list[lo].first()) ); } template Type Foam::interpolationTable::interpolateValue ( const List>& list, scalar lookupValue, bounds::repeatableBounding bounding ) { const label n = list.size(); if (n <= 1) { #ifdef FULLDEBUG if (!n) { FatalErrorInFunction << "Cannot interpolate from zero-sized table" << nl << exit(FatalError); } #endif return list.first().second(); } const scalar minLimit = list.first().first(); const scalar maxLimit = list.last().first(); if (lookupValue < minLimit) { switch (bounding) { case bounds::repeatableBounding::ERROR: { FatalErrorInFunction << "value (" << lookupValue << ") less than lower " << "bound (" << minLimit << ")\n" << exit(FatalError); break; } case bounds::repeatableBounding::WARN: { WarningInFunction << "value (" << lookupValue << ") less than lower " << "bound (" << minLimit << ")\n" << " Continuing with the first entry" << endl; // Behaviour as per CLAMP return list.first().second(); break; } case bounds::repeatableBounding::CLAMP: { return list.first().second(); break; } case bounds::repeatableBounding::REPEAT: { // adjust lookupValue to >= minLimit const scalar span = maxLimit-minLimit; lookupValue = fmod(lookupValue - minLimit, span) + minLimit; break; } } } else if (lookupValue >= maxLimit) { switch (bounding) { case bounds::repeatableBounding::ERROR: { FatalErrorInFunction << "value (" << lookupValue << ") greater than upper " << "bound (" << maxLimit << ")\n" << exit(FatalError); break; } case bounds::repeatableBounding::WARN: { WarningInFunction << "value (" << lookupValue << ") greater than upper " << "bound (" << maxLimit << ")\n" << " Continuing with the last entry" << endl; // Behaviour as per 'CLAMP' return list.last().second(); break; } case bounds::repeatableBounding::CLAMP: { return list.last().second(); break; } case bounds::repeatableBounding::REPEAT: { // Adjust lookupValue <= maxLimit const scalar span = maxLimit-minLimit; lookupValue = fmod(lookupValue - minLimit, span) + minLimit; break; } } } label lo = 0; label hi = 0; // Look for the correct range for (label i = 0; i < n; ++i) { if (lookupValue >= list[i].first()) { lo = hi = i; } else { hi = i; break; } } if (lo == hi) { return list[hi].second(); } else if (hi == 0) { // This treatment should only occur under these conditions: // -> the 'REPEAT' treatment // -> (0 <= value <= minLimit) // -> minLimit > 0 // Use the value at maxLimit as the value for value=0 lo = n - 1; return ( list[lo].second() + (list[hi].second() - list[lo].second()) * (lookupValue / minLimit) ); } // Normal interpolation return ( list[lo].second() + (list[hi].second() - list[lo].second()) * (lookupValue - list[lo].first()) / (list[hi].first() - list[lo].first()) ); } template Type Foam::interpolationTable::interpolateValue ( scalar lookupValue ) const { return interpolateValue(*this, lookupValue, bounding_); } template Foam::tmp> Foam::interpolationTable::interpolateValues ( const UList& vals ) const { auto tfld = tmp>::New(vals.size()); auto& fld = tfld.ref(); forAll(fld, i) { fld[i] = interpolateValue(vals[i]); } return tfld; } // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * // template void Foam::interpolationTable::operator= ( const interpolationTable& rhs ) { if (this == &rhs) { return; } static_cast&>(*this) = rhs; bounding_ = rhs.bounding_; fileName_ = rhs.fileName_; reader_.reset(rhs.reader_.clone()); } template const Foam::Tuple2& Foam::interpolationTable::operator[](label idx) const { const List& list = *this; const label n = list.size(); if (n <= 1) { idx = 0; #ifdef FULLDEBUG if (!n) { FatalErrorInFunction << "Cannot interpolate from zero-sized table" << nl << exit(FatalError); } #endif } else if (idx < 0) { switch (bounding_) { case bounds::repeatableBounding::ERROR: { FatalErrorInFunction << "index (" << idx << ") underflow" << nl << exit(FatalError); break; } case bounds::repeatableBounding::WARN: { WarningInFunction << "index (" << idx << ") underflow" << nl << " Continuing with the first entry" << nl; // Behaviour as per 'CLAMP' idx = 0; break; } case bounds::repeatableBounding::CLAMP: { idx = 0; break; } case bounds::repeatableBounding::REPEAT: { while (idx < 0) { idx += n; } break; } } } else if (idx >= n) { switch (bounding_) { case bounds::repeatableBounding::ERROR: { FatalErrorInFunction << "index (" << idx << ") overflow" << nl << exit(FatalError); break; } case bounds::repeatableBounding::WARN: { WarningInFunction << "index (" << idx << ") overflow" << nl << " Continuing with the last entry" << nl; // Behaviour as per 'CLAMP' idx = n - 1; break; } case bounds::repeatableBounding::CLAMP: { idx = n - 1; break; } case bounds::repeatableBounding::REPEAT: { while (idx >= n) { idx -= n; } break; } } } return list[idx]; } template Type Foam::interpolationTable::operator()(scalar lookupValue) const { return interpolateValue(*this, lookupValue, bounding_); } // ************************************************************************* //