Files
OpenFOAM-12/src/OpenFOAM/unitConversion/unitConversionIO.C
Will Bainbridge a00b947c1d unitConversion: Fixed format of non-named units
Non-named unit conversions are now written in a single set of square
brackets, with colons used to separate the sets of exponents and
multipliers. So, for example, a named unit conversion of [rpm] gets
converted to a non-named [0 0 -1 0 0 0 0 : 0 1 : 0.10472] (i.e.,
[time^-1 : angle : 0.10472]).

Previously, multiple sets of square brackets were used to separate the
sections, so the above example would have been written as
[0 0 -1 0 0 0 0][0 1][0.10472]. The problem with this is that '][' is
not distinguished in the token parser from '] ['. Combined with the fact
that the later sections are optional, this makes reading pairs or lists
of these objects impossible. The new format is, by comparison,
unambiguously delimited in all contexts.

This change may break the restart of a case in which unit conversion
entries have been written out. It is unlikely to require modification to
an initial configuration as named unit conversion entries (such as [rpm]
and [MPa] and [l/min]) are unaffected.
2024-10-03 11:54:56 +01:00

484 lines
13 KiB
C++

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2024 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "unitConversion.H"
#include "dictionary.H"
#include "symbols.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::unitConversion::unitConversion(Istream& is)
:
dimensions_(dimless),
multiplier_(NaN)
{
is >> *this;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::unitConversion::read(const word& keyword, const dictionary& dict)
{
const unitConversion units(dict.lookup(keyword));
if (!compare(*this, units, false))
{
FatalIOErrorInFunction(dict)
<< "The units " << units.info() << " of " << keyword
<< " in dictionary " << dict.name() << " do not match "
<< "the required units " << info()
<< abort(FatalIOError);
}
reset(units);
}
void Foam::unitConversion::read(Istream& is)
{
const unitConversion units(is);
if (!compare(*this, units, false))
{
FatalIOErrorInFunction(is)
<< "The units " << units.info() << " provided do not match "
<< "the required units " << info()
<< abort(FatalIOError);
}
reset(units);
}
void Foam::unitConversion::read
(
const word& keyword,
const dictionary& dict,
Istream& is
)
{
const unitConversion units(is);
if (!compare(*this, units, false))
{
FatalIOErrorInFunction(dict)
<< "The units " << units.info() << " of " << keyword
<< " in dictionary " << dict.name() << " do not match "
<< "the required units " << info()
<< abort(FatalIOError);
}
reset(units);
}
bool Foam::unitConversion::readIfPresent
(
const word& keyword,
const dictionary& dict
)
{
const entry* entryPtr = dict.lookupEntryPtr(keyword, false, true);
if (entryPtr)
{
const unitConversion units(entryPtr->stream());
if (!compare(*this, units, false))
{
FatalIOErrorInFunction(dict)
<< "The units " << units.info() << " of " << keyword
<< " in dictionary " << dict.name() << " do not match "
<< "the required units " << info()
<< abort(FatalIOError);
}
reset(units);
return true;
}
else
{
if (dictionary::writeOptionalEntries)
{
IOInfoInFunction(dict)
<< "Optional entry '" << keyword << "' is not present,"
<< " the default value '" << info() << "' will be used."
<< endl;
}
return false;
}
}
bool Foam::unitConversion::readIfPresent(Istream& is)
{
token nextToken(is);
is.putBack(nextToken);
if (nextToken != token::BEGIN_SQR) return false;
const unitConversion units(is);
if (!unitConversion::compare(units, *this, false))
{
FatalIOErrorInFunction(is)
<< "The units " << units.info() << " provided do not match "
<< "the required units " << info()
<< abort(FatalIOError);
}
if (debug && (any() || !unitConversion::compare(units, *this, true)))
{
Info<< "Unit conversion at line " << is.lineNumber()
<< " of file " << is.name()
<< " with factor " << units.multiplier_ << endl;
}
reset(units);
return true;
}
bool Foam::unitConversion::readIfPresent
(
const word& keyword,
const dictionary& dict,
Istream& is
)
{
token nextToken(is);
is.putBack(nextToken);
if (nextToken != token::BEGIN_SQR) return false;
const unitConversion units(is);
if (!unitConversion::compare(units, *this, false))
{
FatalIOErrorInFunction(dict)
<< "The units " << units.info() << " of " << keyword
<< " in dictionary " << dict.name() << " do not match "
<< "the required units " << info()
<< abort(FatalIOError);
}
if (debug && (any() || !unitConversion::compare(units, *this, true)))
{
Info<< "Unit conversion of " << keyword
<< " in dictionary " << dict.name()
<< " with factor " << units.multiplier_ << endl;
}
reset(units);
return true;
}
// * * * * * * * * * * * * * * Friend Operators * * * * * * * * * * * * * * //
Foam::Istream& Foam::operator>>(Istream& is, unitConversion& units)
{
token nextToken;
// Read the next delimiting token. This must be the start bracket.
is >> nextToken;
if (nextToken != token::BEGIN_SQR)
{
FatalIOErrorInFunction(is)
<< "expected a " << token::BEGIN_SQR << " in unitConversion"
<< endl << "in stream " << is.info() << ", got a "
<< nextToken << exit(FatalIOError);
}
// Peek at the next token
is >> nextToken;
is.putBack(nextToken);
// If not a number or separator, then these are named units. Parse.
if (!nextToken.isNumber() && nextToken != token::COLON)
{
// Named units. Parse.
units.reset(symbols::parseNoBeginOrEnd(is, unitless, Foam::units()));
// Read the next delimiting token. This must be the end bracket.
is >> nextToken;
if (nextToken != token::END_SQR)
{
FatalIOErrorInFunction(is)
<< "expected a " << token::END_SQR << " in unitConversion "
<< endl << "in stream " << is.info() << ", got a "
<< nextToken << exit(FatalIOError);
}
// Check state of Istream
is.check("Istream& operator>>(Istream&, unitConversion&)");
return is;
}
// Otherwise these are numbered units. Read directly...
// Read the dimensions
units.dimensions_.readNoBeginOrEnd(is);
// Read the next delimiting token. If a separator, then there are
// dimensionless units and a multiplier to read. If it is an end bracket,
// then the dimensionless units are zero and the multiplier is one and the
// parsing is finished. Otherwise the parsing has failed.
is >> nextToken;
if (nextToken == token::COLON)
{
// Peek at the next token
is >> nextToken;
is.putBack(nextToken);
// Read the dimensionless units if present, or set to zero
if (!nextToken.isNumber())
{
for (int i = 0; i < unitConversion::nDimlessUnits; ++ i)
{
units.exponents_[i] = 0;
}
}
else
{
for (int i = 0; i < unitConversion::nDimlessUnits; ++ i)
{
is >> units.exponents_[i];
}
}
// Read the next delimiting token. If a separator then there is a
// multiplier to read. If it is an end bracket then the multiplier is
// one and the parsing is finished. Otherwise the parsing has failed.
is >> nextToken;
if (nextToken == token::COLON)
{
// Peek at the next token
is >> nextToken;
is.putBack(nextToken);
// Read the multiplier if present, or set to unity
if (!nextToken.isNumber())
{
units.multiplier_ = 1;
}
else
{
is >> units.multiplier_;
}
// Read the next delimiting token. This must be the end bracket.
is >> nextToken;
if (nextToken != token::END_SQR)
{
FatalIOErrorInFunction(is)
<< "expected a " << token::END_SQR << " in unitConversion "
<< endl << "in stream " << is.info() << ", got a "
<< nextToken << exit(FatalIOError);
}
}
else if (nextToken == token::END_SQR)
{
units.multiplier_ = 1;
}
else
{
FatalIOErrorInFunction(is)
<< "expected a " << token::END_SQR << " or a " << token::COLON
<< " in unitConversion " << endl << "in stream " << is.info()
<< ", got a " << nextToken << exit(FatalIOError);
}
}
else if (nextToken == token::END_SQR)
{
for (int i = 0; i < unitConversion::nDimlessUnits; ++ i)
{
units.exponents_[i] = 0;
}
units.multiplier_ = 1;
}
else
{
FatalIOErrorInFunction(is)
<< "expected a " << token::END_SQR << " or a " << token::COLON
<< " in unitConversion " << endl << "in stream " << is.info()
<< ", got a " << nextToken << exit(FatalIOError);
}
// Check state of Istream
is.check("Istream& operator>>(Istream&, unitConversion&)");
return is;
}
Foam::Ostream& Foam::operator<<(Ostream& os, const unitConversion& units)
{
// Write the start
os << token::BEGIN_SQR;
// Write the dimensions
units.dimensions_.writeNoBeginOrEnd(os);
// Determine if any dimensionless units are non-zero
bool nonZeroDimlessUnits = false;
for (int i = 0; i < unitConversion::nDimlessUnits; ++ i)
{
nonZeroDimlessUnits =
nonZeroDimlessUnits
|| mag(units.exponents_[i]) > unitConversion::smallExponent;
}
// Determine if the multiplier is non-unity
bool nonUnityMultiplier = units.multiplier_ != 1;
// Write a separator if there is anything to follow
if (nonZeroDimlessUnits || nonUnityMultiplier)
{
os << token::SPACE << token::COLON;
}
// Write the dimensionless units if any are non-zero
if (nonZeroDimlessUnits)
{
for (int i = 0; i < unitConversion::nDimlessUnits; ++ i)
{
os << token::SPACE << units.exponents_[i];
}
}
// Write a separator if there is anything to follow
if (nonUnityMultiplier)
{
os << token::SPACE << token::COLON;
}
// Write the multiplier if it is non-unity
if (nonUnityMultiplier)
{
os << token::SPACE << units.multiplier_;
}
// Write the end
os << token::END_SQR;
// Check state of Ostream
os.check("Ostream& operator<<(Ostream&, const unitConversion&)");
return os;
}
Foam::Ostream& Foam::operator<<
(
Ostream& os,
const InfoProxy<unitConversion>& ip
)
{
const unitConversion& units = ip.t_;
// Filter out special cases
if (units.any())
{
return os << token::BEGIN_SQR << "<any>" << token::END_SQR;
}
if (units.none())
{
return os << token::BEGIN_SQR << "<none>" << token::END_SQR;
}
// Write the start
os << token::BEGIN_SQR;
// Write the dimensions
units.dimensions_.writeInfoNoBeginOrEnd(os);
// Determine if any dimensionless units are non-zero
bool nonZeroDimlessUnits = false;
for (int i = 0; i < unitConversion::nDimlessUnits; ++ i)
{
nonZeroDimlessUnits =
nonZeroDimlessUnits
|| mag(units.exponents_[i]) > unitConversion::smallExponent;
}
// Determine if the multiplier is non-unity
bool nonUnityMultiplier = units.multiplier_ != 1;
// Write a separator if there is anything to follow
if (nonZeroDimlessUnits || nonUnityMultiplier)
{
os << token::SPACE << token::COLON;
}
// Write the dimensionless units if any are non-zero
if (nonZeroDimlessUnits)
{
for (int i=0; i<unitConversion::nDimlessUnits; i++)
{
if (mag(units.exponents_[i]) > unitConversion::smallExponent)
{
os << token::SPACE << unitConversion::dimlessUnitTypeNames_
[static_cast<unitConversion::dimlessUnitType>(i)];
if (units.exponents_[i] != 1)
{
os << '^' << units.exponents_[i];
}
}
}
}
// Write a separator if there is anything to follow
if (nonUnityMultiplier)
{
os << token::SPACE << token::COLON;
}
// Write the multiplier if it is non-unity
if (nonUnityMultiplier)
{
os << token::SPACE << units.multiplier_;
}
// Write the end
os << token::END_SQR;
// Check state of Ostream
os.check("Ostream& operator<<(Ostream&, const InfoProxy<unitConversion>&)");
return os;
}
// ************************************************************************* //