From a00b947c1db6f229f21b5f2a5801cf09a652f2f2 Mon Sep 17 00:00:00 2001 From: Will Bainbridge Date: Thu, 3 Oct 2024 10:25:20 +0100 Subject: [PATCH] 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. --- src/OpenFOAM/dimensionSet/dimensionSet.H | 10 +- src/OpenFOAM/dimensionSet/dimensionSetIO.C | 173 +++++----- .../Function1/Function1UnitConversions.C | 4 +- src/OpenFOAM/symbols/symbols.C | 18 +- src/OpenFOAM/symbols/symbols.H | 7 +- src/OpenFOAM/symbols/symbolsTemplates.C | 24 +- src/OpenFOAM/unitConversion/unitConversion.H | 6 + .../unitConversion/unitConversionIO.C | 299 ++++++++++++------ 8 files changed, 346 insertions(+), 195 deletions(-) diff --git a/src/OpenFOAM/dimensionSet/dimensionSet.H b/src/OpenFOAM/dimensionSet/dimensionSet.H index 777099f8dd..05d4d67ca1 100644 --- a/src/OpenFOAM/dimensionSet/dimensionSet.H +++ b/src/OpenFOAM/dimensionSet/dimensionSet.H @@ -227,15 +227,21 @@ public: // I/O - //- Read, assuming the '[' has already been read - Istream& readNoBegin(Istream& is); + //- Read without the square brackets + Istream& readNoBeginOrEnd(Istream& is); //- Read Istream& read(Istream& is); + //- Write without the square brackets + Ostream& writeNoBeginOrEnd(Ostream& os) const; + //- Write Ostream& write(Ostream& os) const; + //- Write info without the square brackets + Ostream& writeInfoNoBeginOrEnd(Ostream& os) const; + //- Return info proxy inline InfoProxy info() const { diff --git a/src/OpenFOAM/dimensionSet/dimensionSetIO.C b/src/OpenFOAM/dimensionSet/dimensionSetIO.C index ecba0c49a5..ed744b8147 100644 --- a/src/OpenFOAM/dimensionSet/dimensionSetIO.C +++ b/src/OpenFOAM/dimensionSet/dimensionSetIO.C @@ -30,7 +30,7 @@ License void Foam::dimensionSet::round(const scalar tol) { - for (int i=0; i < dimensionSet::nDimensions; ++i) + for (int i=0; i> nextToken; + is.putBack(nextToken); + + // If not a number, then these are named dimensions. Parse. if (!nextToken.isNumber()) { - // Named dimensions. Parse. - is.putBack(nextToken); - reset(symbols::parseNoBegin(is, dimless, dimensions())); + reset(symbols::parseNoBeginOrEnd(is, dimless, dimensions())); + + return is; + } + + // Otherwise these are numbered dimensions. Read directly... + + // Read first five dimensions + for (int i=0; i> exponents_[i]; + } + + // Peek at the next token + is >> nextToken; + is.putBack(nextToken); + + // If next token is another number then read the last two dimensions + // and then read another token for the end of the dimensionSet + if (nextToken.isNumber()) + { + is >> exponents_[dimensionSet::CURRENT]; + is >> exponents_[dimensionSet::LUMINOUS_INTENSITY]; } else { - // Numbered dimensions. Read directly. - - // Read first five dimensions - exponents_[dimensionSet::MASS] = nextToken.number(); - for (int Dimension=1; Dimension> exponents_[Dimension]; - } - - // Read next token - token nextToken(is); - - // If next token is another number then read the last two dimensions - // and then read another token for the end of the dimensionSet - if (nextToken.isNumber()) - { - exponents_[dimensionSet::CURRENT] = nextToken.number(); - is >> nextToken; - exponents_[dimensionSet::LUMINOUS_INTENSITY] = nextToken.number(); - is >> nextToken; - } - else - { - exponents_[dimensionSet::CURRENT] = 0; - exponents_[dimensionSet::LUMINOUS_INTENSITY] = 0; - } - - // Check end of dimensionSet - if (nextToken != token::END_SQR) - { - FatalIOErrorInFunction(is) - << "expected a " << token::END_SQR << " in dimensionSet " - << endl << "in stream " << is.info() - << exit(FatalIOError); - } + exponents_[dimensionSet::CURRENT] = 0; + exponents_[dimensionSet::LUMINOUS_INTENSITY] = 0; } - // Check state of Istream - is.check("Istream& operator>>(Istream&, dimensionSet&)"); - return is; } @@ -122,7 +110,6 @@ Foam::Istream& Foam::dimensionSet::read(Istream& is) { // Read beginning of dimensionSet token startToken(is); - if (startToken != token::BEGIN_SQR) { FatalIOErrorInFunction(is) @@ -131,7 +118,42 @@ Foam::Istream& Foam::dimensionSet::read(Istream& is) << exit(FatalIOError); } - return readNoBegin(is); + readNoBeginOrEnd(is); + + // Read end of dimensionSet + token endToken(is); + if (endToken != token::END_SQR) + { + FatalIOErrorInFunction(is) + << "expected a " << token::END_SQR << " in dimensionSet " + << endl << "in stream " << is.info() + << exit(FatalIOError); + } + + // Check state of Istream + is.check("Istream& operator>>(Istream&, dimensionSet&)"); + + return is; +} + + +Foam::Ostream& Foam::dimensionSet::writeNoBeginOrEnd(Ostream& os) const +{ + if (dimensionless()) + { + // Do nothing + } + else + { + for (int i=0; i dimensionSet::smallExponent) + { + if (!first) + { + os << token::SPACE; + } + + os << dimensionSet::dimensionTypeNames_ + [static_cast(i)]; + + if (exponents_[i] != 1) + { + os << '^' << exponents_[i]; + } + + first = false; + } + } + + return os; +} + + void Foam::writeEntry(Ostream& os, const dimensionSet& value) { os << value; @@ -185,30 +231,9 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const dimensionSet& dims) Foam::Ostream& Foam::operator<<(Ostream& os, const InfoProxy& ip) { - const dimensionSet& dims = ip.t_; - os << token::BEGIN_SQR; - for (int first=true, i=0; i dimensionSet::smallExponent) - { - if (!first) - { - os << token::SPACE; - } - - os << dimensionSet::dimensionTypeNames_ - [static_cast(i)]; - - if (dims.exponents_[i] != 1) - { - os << '^' << dims.exponents_[i]; - } - - first = false; - } - } + ip.t_.writeInfoNoBeginOrEnd(os); os << token::END_SQR; diff --git a/src/OpenFOAM/primitives/functions/Function1/Function1/Function1UnitConversions.C b/src/OpenFOAM/primitives/functions/Function1/Function1/Function1UnitConversions.C index d4dac7a034..90a52e5624 100644 --- a/src/OpenFOAM/primitives/functions/Function1/Function1/Function1UnitConversions.C +++ b/src/OpenFOAM/primitives/functions/Function1/Function1/Function1UnitConversions.C @@ -72,8 +72,8 @@ bool Foam::Function1s::unitConversions::readIfPresent ITstream& is = entryPtr->stream(); is.readBegin("unitConversions"); - x.readIfPresent(keyword, dict, is); - value.readIfPresent(keyword, dict, is); + x.read(keyword, dict, is); + value.read(keyword, dict, is); is.readEnd("unitConversions"); return true; diff --git a/src/OpenFOAM/symbols/symbols.C b/src/OpenFOAM/symbols/symbols.C index c2e01ba40d..71205402eb 100644 --- a/src/OpenFOAM/symbols/symbols.C +++ b/src/OpenFOAM/symbols/symbols.C @@ -90,6 +90,7 @@ Foam::token Foam::symbols::tokeniser::nextToken() if (size_ == 0) { token t(is_); + if (t.isWord()) { splitWord(t.wordToken()); @@ -175,16 +176,17 @@ bool Foam::symbols::tokeniser::valid(char c) !isspace(c) && c != '"' // string quote && c != '\'' // string quote - && c != '/' // div + && c != '/' // divide && c != ';' // end statement - && c != '{' // beg subdict - && c != '}' // end subdict - && c != '(' // beg expr - && c != ')' // end expr - && c != '[' // beg dim - && c != ']' // end dim + && c != '{' // begin sub-dictionary + && c != '}' // end sub-dictionary + && c != '(' // begin expression + && c != ')' // end expression + && c != '[' // begin dimensions/units + && c != ']' // end dimensions/units + && c != ':' // separate dimensions/units && c != '^' // power - && c != '*' // mult + && c != '*' // multiply ); } diff --git a/src/OpenFOAM/symbols/symbols.H b/src/OpenFOAM/symbols/symbols.H index c6cea6ee71..ad814a25c7 100644 --- a/src/OpenFOAM/symbols/symbols.H +++ b/src/OpenFOAM/symbols/symbols.H @@ -124,10 +124,11 @@ Type parseNoBegin const HashTable& table ); -//- Parse a dimension set or unit conversion, assuming that the '[' has already -// been read +//- Parse tokens into a dimension set or unit conversion, assuming that the '[' +// has already been read, and ending before the '[' or ':'. Note that this +// will leave the stream with a pushed back token. template -Type parseNoBegin +Type parseNoBeginOrEnd ( Istream& is, const Type& identity, diff --git a/src/OpenFOAM/symbols/symbolsTemplates.C b/src/OpenFOAM/symbols/symbolsTemplates.C index d84c991307..37b075c557 100644 --- a/src/OpenFOAM/symbols/symbolsTemplates.C +++ b/src/OpenFOAM/symbols/symbolsTemplates.C @@ -76,12 +76,19 @@ Type Foam::symbols::parseNoBegin tis.putBack(nextToken); return result; } + else if (nextToken.pToken() == token::COLON) + { + // End the units + tis.putBack(nextToken); + return result; + } else if (nextToken.pToken() == token::BEGIN_LIST) { // Parenthesis. Evaluate the sub-units and multiply. result.reset ( - result*parseNoBegin(nextPrior, tis, identity, table) + result + *parseNoBegin(nextPrior, tis, identity, table) ); // Check that the parentheses end @@ -108,7 +115,8 @@ Type Foam::symbols::parseNoBegin // This has priority. Evaluate the next units and multiply. result.reset ( - result*parseNoBegin(nextPrior, tis, identity, table) + result + *parseNoBegin(nextPrior, tis, identity, table) ); } else @@ -127,7 +135,8 @@ Type Foam::symbols::parseNoBegin { result.reset ( - result/parseNoBegin(nextPrior, tis, identity, table) + result + /parseNoBegin(nextPrior, tis, identity, table) ); } else @@ -177,6 +186,7 @@ Type Foam::symbols::parseNoBegin } nextToken = tis.nextToken(); + if (nextToken.error()) { break; @@ -195,7 +205,7 @@ Type Foam::symbols::parseNoBegin template -Type Foam::symbols::parseNoBegin +Type Foam::symbols::parseNoBeginOrEnd ( Istream& is, const Type& identity, @@ -204,7 +214,11 @@ Type Foam::symbols::parseNoBegin { symbols::tokeniser tis(is); - return parseNoBegin(0, tis, identity, table); + Type result = parseNoBegin(0, tis, identity, table); + + is.putBack(tis.nextToken()); + + return result; } diff --git a/src/OpenFOAM/unitConversion/unitConversion.H b/src/OpenFOAM/unitConversion/unitConversion.H index c5010fa7e5..9e0a240c4d 100644 --- a/src/OpenFOAM/unitConversion/unitConversion.H +++ b/src/OpenFOAM/unitConversion/unitConversion.H @@ -219,6 +219,12 @@ public: //- Update void read(const word& keyword, const dictionary&); + //- Update + void read(Istream& is); + + //- Update + void read(const word& keyword, const dictionary&, Istream& is); + //- Update if found in the dictionary bool readIfPresent(const word& keyword, const dictionary&); diff --git a/src/OpenFOAM/unitConversion/unitConversionIO.C b/src/OpenFOAM/unitConversion/unitConversionIO.C index a5f28eb790..84772d94b2 100644 --- a/src/OpenFOAM/unitConversion/unitConversionIO.C +++ b/src/OpenFOAM/unitConversion/unitConversionIO.C @@ -47,6 +47,23 @@ void Foam::unitConversion::read(const word& keyword, const dictionary& dict) 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); @@ -56,6 +73,28 @@ void Foam::unitConversion::read(const word& keyword, const dictionary& dict) } +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, @@ -71,7 +110,8 @@ bool Foam::unitConversion::readIfPresent if (!compare(*this, units, false)) { FatalIOErrorInFunction(dict) - << "The units " << units.info() << " provided do not match " + << "The units " << units.info() << " of " << keyword + << " in dictionary " << dict.name() << " do not match " << "the required units " << info() << abort(FatalIOError); } @@ -102,24 +142,24 @@ bool Foam::unitConversion::readIfPresent(Istream& is) if (nextToken != token::BEGIN_SQR) return false; - unitConversion u(is); + const unitConversion units(is); - if (!unitConversion::compare(u, *this, false)) + if (!unitConversion::compare(units, *this, false)) { FatalIOErrorInFunction(is) - << "The units " << u.info() << " provided do not match " + << "The units " << units.info() << " provided do not match " << "the required units " << info() << abort(FatalIOError); } - if (debug && (any() || !unitConversion::compare(u, *this, true))) + if (debug && (any() || !unitConversion::compare(units, *this, true))) { Info<< "Unit conversion at line " << is.lineNumber() << " of file " << is.name() - << " with factor " << u.multiplier_ << endl; + << " with factor " << units.multiplier_ << endl; } - reset(u); + reset(units); return true; } @@ -137,25 +177,25 @@ bool Foam::unitConversion::readIfPresent if (nextToken != token::BEGIN_SQR) return false; - unitConversion u(is); + const unitConversion units(is); - if (!unitConversion::compare(u, *this, false)) + if (!unitConversion::compare(units, *this, false)) { - FatalIOErrorInFunction(is) - << "The units " << u.info() << " of " << keyword + 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(u, *this, true))) + if (debug && (any() || !unitConversion::compare(units, *this, true))) { Info<< "Unit conversion of " << keyword << " in dictionary " << dict.name() - << " with factor " << u.multiplier_ << endl; + << " with factor " << units.multiplier_ << endl; } - reset(u); + reset(units); return true; } @@ -165,95 +205,133 @@ bool Foam::unitConversion::readIfPresent Foam::Istream& Foam::operator>>(Istream& is, unitConversion& units) { - // Read beginning of unitConversion - token startToken(is); + token nextToken; - if (startToken != token::BEGIN_SQR) + // 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() - << exit(FatalIOError); + << endl << "in stream " << is.info() << ", got a " + << nextToken << exit(FatalIOError); } // Peek at the next token - token nextToken(is); + is >> nextToken; is.putBack(nextToken); - if (!nextToken.isNumber()) + // If not a number or separator, then these are named units. Parse. + if (!nextToken.isNumber() && nextToken != token::COLON) { // Named units. Parse. - units.reset(symbols::parseNoBegin(is, unitless, Foam::units())); + 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; } - else + + // 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) { - // Read the dimensions - units.dimensions_.readNoBegin(is); + // Peek at the next token + is >> nextToken; + is.putBack(nextToken); // Read the dimensionless units if present, or set to zero - token nextToken; - if (!is.eof()) - { - is >> nextToken; - } - if (nextToken == token::BEGIN_SQR) + if (!nextToken.isNumber()) { for (int i = 0; i < unitConversion::nDimlessUnits; ++ i) { - is >> units.exponents_[i]; - } - - // Check end of dimensionless units - token endToken(is); - if (endToken != token::END_SQR) - { - FatalIOErrorInFunction(is) - << "expected a " << token::END_SQR - << " in unitConversion " << endl << "in stream " - << is.info() << exit(FatalIOError); - } - - // Read the multiplier if present, or set to unity - token nextToken; - if (!is.eof()) - { - is >> nextToken; - } - if (nextToken == token::BEGIN_SQR) - { - is >> units.multiplier_; - - // Check end of multiplier - token endToken(is); - if (endToken != token::END_SQR) - { - FatalIOErrorInFunction(is) - << "expected a " << token::END_SQR - << " in unitConversion " << endl << "in stream " - << is.info() << exit(FatalIOError); - } - } - else - { - units.multiplier_ = 1; - if (!nextToken.undefined()) - { - is.putBack(nextToken); - } + units.exponents_[i] = 0; } } else { for (int i = 0; i < unitConversion::nDimlessUnits; ++ i) { - units.exponents_[i] = 0; - } - units.multiplier_ = 1; - if (!nextToken.undefined()) - { - is.putBack(nextToken); + 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 @@ -265,8 +343,11 @@ Foam::Istream& Foam::operator>>(Istream& is, unitConversion& units) Foam::Ostream& Foam::operator<<(Ostream& os, const unitConversion& units) { + // Write the start + os << token::BEGIN_SQR; + // Write the dimensions - os << units.dimensions_; + units.dimensions_.writeNoBeginOrEnd(os); // Determine if any dimensionless units are non-zero bool nonZeroDimlessUnits = false; @@ -280,25 +361,36 @@ Foam::Ostream& Foam::operator<<(Ostream& os, const unitConversion& units) // Determine if the multiplier is non-unity bool nonUnityMultiplier = units.multiplier_ != 1; - // Write the dimensionless units if any are non-zero or we have a - // multiplier to write out afterwards + // Write a separator if there is anything to follow if (nonZeroDimlessUnits || nonUnityMultiplier) { - os << token::BEGIN_SQR; + 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 (i) os << token::SPACE; - os << units.exponents_[i]; + os << token::SPACE << units.exponents_[i]; } - os << token::END_SQR; + } + + // 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::BEGIN_SQR << units.multiplier_ << token::END_SQR; + os << token::SPACE << units.multiplier_; } + // Write the end + os << token::END_SQR; + // Check state of Ostream os.check("Ostream& operator<<(Ostream&, const unitConversion&)"); @@ -324,8 +416,11 @@ Foam::Ostream& Foam::operator<< return os << token::BEGIN_SQR << "" << token::END_SQR; } + // Write the start + os << token::BEGIN_SQR; + // Write the dimensions - os << units.dimensions_.info(); + units.dimensions_.writeInfoNoBeginOrEnd(os); // Determine if any dimensionless units are non-zero bool nonZeroDimlessUnits = false; @@ -339,43 +434,45 @@ Foam::Ostream& Foam::operator<< // Determine if the multiplier is non-unity bool nonUnityMultiplier = units.multiplier_ != 1; - // Write the dimensionless units if any are non-zero or we have a - // multiplier to write out afterwards + // Write a separator if there is anything to follow if (nonZeroDimlessUnits || nonUnityMultiplier) { - // Write the dimensionless units - os << token::BEGIN_SQR; + os << token::SPACE << token::COLON; + } - for (int first=true, i=0; i unitConversion::smallExponent) { - if (!first) - { - os << token::SPACE; - } - - os << unitConversion::dimlessUnitTypeNames_ + os << token::SPACE << unitConversion::dimlessUnitTypeNames_ [static_cast(i)]; if (units.exponents_[i] != 1) { os << '^' << units.exponents_[i]; } - - first = false; } } + } - os << token::END_SQR; + // 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::BEGIN_SQR << units.multiplier_ << token::END_SQR; + os << token::SPACE << units.multiplier_; } + // Write the end + os << token::END_SQR; + // Check state of Ostream os.check("Ostream& operator<<(Ostream&, const InfoProxy&)");