foamUnits.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2026 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 Application
25  foamUnits
26 
27 Description
28  Print information about dimensions and unit conversions
29 
30 Usage
31  \b foamUnits
32  \b foamUnits <unit>
33  \b foamUnits <unit1> <unit2>
34 
35  Options:
36  - \par -all \n
37  Print information for all available dimensions and units
38 
39  - \par -value \n
40  Print the conversion factor value only (e.g., for use in a script)
41 
42 Note
43  This utility can be run with no arguments, one argument or two arguments.
44  If no arguments are given this utility will print the names of all
45  dimensions and units. If one argument is given then this is taken to be the
46  name of a dimension or a unit and information will be printed regarding its
47  relationship to the corresponding fundamental dimension or unit. If two
48  arguments are given then these must be units (not dimensions) and
49  conversions between them will be printed.
50 
51 Example usage:
52  foamUnits
53  foamUnits -all
54  foamUnits specificHeatCapacity
55  foamUnits "(mol/cm^3)^-0.5/s" "(kmol/m^3)^-0.5/s"
56 
57 \*---------------------------------------------------------------------------*/
58 
59 #include "argList.H"
60 #include "units.H"
61 #include "stringOps.H"
62 #include "IOobject.H"
63 
64 using namespace Foam;
65 
66 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
67 
68 string standardUnitName
69 (
70  const wordList& dimensionUnitNames,
71  const wordList& dimlessUnitNames,
72  const unitSet& unit
73 )
74 {
75  string result;
76 
77  for (label i = 0; i < dimensionSet::nDimensions; ++ i)
78  {
79  const scalar e = unit[static_cast<dimensionSet::dimensionType>(i)];
80  if (e == 0) continue;
81  result.append(dimensionUnitNames[i]);
82  if (e != 1) result.append("^" + name(e));
83  result.append(" ");
84  }
85  for (label i = 0; i < unitSet::nDimlessUnits; ++ i)
86  {
87  const scalar e = unit[static_cast<unitSet::dimlessUnitType>(i)];
88  if (e == 0) continue;
89  result.append(dimlessUnitNames[i]);
90  if (e != 1) result.append("^" + name(e));
91  result.append(" ");
92  }
93 
94  return result(result.size() - 1);
95 }
96 
97 
98 template<class Type>
99 bool stringIs(const string& str, const HashTable<Type>& types)
100 {
101  forAllConstIter(typename HashTable<Type>, types, iter)
102  {
103  const size_t i0 = str.find(iter.key());
104  const size_t i1 = i0 + iter.key().size();
105 
106  if (i0 == std::string::npos) continue;
107 
108  if (i0 > 0 && isalnum(str[i0 - 1])) continue;
109 
110  if (i1 < str.size() && isalnum(str[i1])) continue;
111 
112  return true;
113  }
114 
115  return false;
116 }
117 
118 
119 bool isFundamental(const dimensionSet& dimension)
120 {
121  label result = 0;
122 
123  for (label i = 0; i < dimensionSet::nDimensions; ++ i)
124  {
126  static_cast<dimensionSet::dimensionType>(i);
127 
128  result =
129  result == 0 && dimension[t] == 1 ? 1
130  : dimension[t] == 0 ? result
131  : -1;
132  }
133 
134  return result == 1;
135 }
136 
137 
138 bool isFundamental(const unitSet& unit)
139 {
140  label result = 0;
141 
142  for (label i = 0; i < dimensionSet::nDimensions; ++ i)
143  {
145  static_cast<dimensionSet::dimensionType>(i);
146 
147  result =
148  result == 0 && unit[t] == 1 ? 1
149  : unit[t] == 0 ? result
150  : -1;
151  }
152 
153  for (label i = 0; i < unitSet::nDimlessUnits; ++ i)
154  {
155  const unitSet::dimlessUnitType t =
156  static_cast<unitSet::dimlessUnitType>(i);
157 
158  result =
159  result == 0 && unit[t] == 1 ? 1
160  : unit[t] == 0 ? result
161  : -1;
162  }
163 
164  return result == 1;
165 }
166 
167 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
168 
169 int main(int argc, char *argv[])
170 {
171  #include "removeCaseOptions.H"
172  writeInfoHeader = false;
173 
175  (
176  "all",
177  "list only the names of the dimensions and units"
178  );
180  (
181  "value",
182  "print only the conversion factor value"
183  );
184 
185  const label nArgs = argList::nArgs(argc, argv);
186  switch (nArgs)
187  {
188  case 1:
189  argList::validArgs.append("unit");
190  break;
191  case 2:
192  argList::validArgs.append("unit1");
193  argList::validArgs.append("unit2");
194  break;
195  default:
196  break;
197  }
198 
200  (
201  "This utility can be run with no arguments, one argument or two\n"
202  "arguments. If no arguments are given this utility will print the\n"
203  "names of all dimensions and units. If one argument is given then \n"
204  "this is taken to be the name of a dimension or a unit and\n"
205  "information will be printed regarding its relationship to the\n"
206  "corresponding fundamental dimension or unit. If two arguments are\n"
207  "given then these must be units (not dimensions) and conversions\n"
208  "between them will be printed.\n"
209  "\n"
210  "Example usage:\n"
211  " foamUnits\n"
212  " foamUnits -all\n"
213  " foamUnits specificHeatCapacity\n"
214  " foamUnits \"(mol/cm^3)^-0.5/s\" \"(kmol/m^3)^-0.5/s\""
215  );
216 
217  argList args(argc, argv);
218 
219  const bool all = args.optionFound("all");
220  const bool value = args.optionFound("value");
221 
222  if (all && value)
223  {
225  << "Options -all and -value can not be used together"
226  << exit(FatalError);
227  }
228  if (nArgs == 0 && value)
229  {
231  << "Option -value requires a unit argument"
232  << exit(FatalError);
233  }
234 
235  // Build lists of fundamental unit names
236  wordList dimensionUnitNames(dimensionSet::nDimensions);
237  wordList dimlessUnitNames(unitSet::nDimlessUnits);
239  {
240  const unitSet& unit = iter();
241 
242  label dimensioni = -1, dimlessUniti = -1;
243  for (label i = 0; i < dimensionSet::nDimensions; ++ i)
244  {
246  static_cast<dimensionSet::dimensionType>(i);
247  if (dimensioni >= 0 && unit[t] != 0)
248  {
249  dimensioni = -2;
250  }
251  if (dimensioni == -1 && unit[t] == 1 && unit.standard())
252  {
253  dimensioni = i;
254  }
255  }
256  for (label i = 0; i < unitSet::nDimlessUnits; ++ i)
257  {
258  const unitSet::dimlessUnitType t =
259  static_cast<unitSet::dimlessUnitType>(i);
260  if (dimlessUniti >= 0 && unit[t] != 0)
261  {
262  dimlessUniti = -1;
263  }
264  if (dimlessUniti == -1 && unit[t] == 1 && unit.standard())
265  {
266  dimlessUniti = i;
267  }
268  }
269 
270  if (dimensioni >= 0 && dimlessUniti == -1)
271  {
272  dimensionUnitNames[dimensioni] = iter.key();
273  }
274  if (dimensioni == -1 && dimlessUniti >= 0)
275  {
276  dimlessUnitNames[dimlessUniti] = iter.key();
277  }
278  }
279 
280  auto printDimension = [&](const word& name, const dimensionSet& dimension)
281  {
282  if (!value)
283  {
284  Info<< "Dimension [" << name << "]" << nl;
285  if (!isFundamental(dimension))
286  Info<< "+ Dimensions = " << dimension.info() << nl;
287  Info << "+ Exponents = " << dimension << nl
288  << endl;
289  }
290  };
291 
292  auto printUnit = [&](const word& name, const unitSet& unit)
293  {
294  if (value)
295  {
296  Info<< unit.toStandard(scalar(1));
297  }
298  else
299  {
300  const string standardName =
301  standardUnitName(dimensionUnitNames, dimlessUnitNames, unit);
302  Info<< "Unit [" << name << "]" << nl
303  << "+ Dimensions = " << unit.dimensions().info() << nl;
304  if (!isFundamental(unit))
305  {
306  Info<< "+ Standard Unit = [" << standardName.c_str() << "]"
307  << nl;
308  }
309  Info<< "+ Conversion Factor = " << unit.toStandard(scalar(1))
310  << nl << endl;
311  }
312  };
313 
314  if (!value) Info<< endl;
315 
316  // Print all dimensions and units
317  if (all)
318  {
320  {
321  printDimension(iter.key(), iter());
322  }
323 
325  {
326  printUnit(iter.key(), iter());
327  }
328 
329  if (nArgs)
330  {
332  Info<< nl;
333  }
334  }
335 
336  // Print a single dimension or unit
337  if (nArgs == 1)
338  {
339  const string name(args[1]);
340 
341  const bool isDimension = stringIs(name, dimensions::table);
342  const bool isUnit = stringIs(name, units::table());
343 
344  if (isDimension && !isUnit)
345  {
346  printDimension(name, IStringStream(("[" + name + "]").c_str())());
347 
348  return 0;
349  }
350 
351  if (!isDimension && isUnit)
352  {
353  printUnit(name, IStringStream(("[" + name + "]").c_str())());
354 
355  return 0;
356  }
357 
359  << "'" << args[1].c_str()
360  << "' is not a valid dimension or unit"
361  << exit(FatalError);
362  }
363 
364  // Print the conversion between two units
365  if (nArgs == 2)
366  {
367  const string name1(args[1]);
368  const string name2(args[2]);
369 
370  auto assertStringIsUnit = [](const string& str)
371  {
372  if (stringIs(str, dimensions::table))
373  {
375  << "'" << str.c_str() << "' is a dimension. "
376  << "Comparison is only supported for units."
377  << exit(FatalError);
378  }
379  };
380  assertStringIsUnit(name1);
381  assertStringIsUnit(name2);
382 
383  const unitSet unit1
384  (
385  IStringStream(("[" + name1 + "]").c_str())()
386  );
387  const unitSet unit2
388  (
389  IStringStream(("[" + name2 + "]").c_str())()
390  );
391 
392  // Check the units are the same, except for the multiplier
393  unitSet
394  (
395  unit1.dimensions(),
396  unit1[unitSet::FRACTION],
397  unit1[unitSet::ANGLE],
398  1
399  )
400  + unitSet
401  (
402  unit2.dimensions(),
403  unit2[unitSet::FRACTION],
404  unit2[unitSet::ANGLE],
405  1
406  );
407 
408  const string standardName =
409  standardUnitName(dimensionUnitNames, dimlessUnitNames, unit1);
410 
411  if (value)
412  {
413  Info<< unit2.toUser(unit1.toStandard(scalar(1)));
414  }
415  else
416  {
417  Info<< "Units [" << name1.c_str() << "] [" << name2.c_str() << ']'
418  << nl << "+ Dimensions = " << unit1.dimensions().info() << nl
419  << "+ Standard Unit = [" << standardName.c_str() << "]" << nl
420  << "+ Conversion: " << 1 << " [" << name1.c_str() << "] = "
421  << unit2.toUser(unit1.toStandard(scalar(1))) << " ["
422  << name2.c_str() << ']' << nl
423  << "+ Conversion: " << 1 << " [" << name2.c_str() << "] = "
424  << unit1.toUser(unit2.toStandard(scalar(1))) << " ["
425  << name1.c_str() << ']' << nl
426  << endl;
427  }
428 
429  return 0;
430  }
431 
432  // Print just the names of the dimensions and units
433  if (!all)
434  {
435  auto print = [](const char* group, const DynamicList<word>& names)
436  {
437  Info<< group << ":" << nl;
438 
439  OStringStream str;
440  forAll(names, i)
441  {
442  str << (i ? " " : "") << "[" << names[i] << ']';
443  }
444 
445  Info<< stringOps::breakIntoIndentedLines(str.str(), 80, 4).c_str()
446  << nl << endl;
447  };
448 
449  DynamicList<word> fundamentalDimensions;
450  DynamicList<word> derivedDimensions;
452  {
453  (
454  isFundamental(iter())
455  ? fundamentalDimensions
456  : derivedDimensions
457  ).append(iter.key());
458  }
459 
460  print("Fundamental Dimensions", fundamentalDimensions);
461  print("Derived Dimensions", derivedDimensions);
462 
463  DynamicList<word> fundamentalUnits;
464  DynamicList<word> derivedUnits;
465  DynamicList<word> scaledUnits;
466  DynamicList<word> derivedScaledUnits;
468  {
469  (
470  isFundamental(iter()) && iter().standard() ? fundamentalUnits
471  : !isFundamental(iter()) && iter().standard() ? derivedUnits
472  : isFundamental(iter()) && !iter().standard() ? scaledUnits
473  : derivedScaledUnits
474  ).append(iter.key());
475  }
476 
477  print("Fundamental Units", fundamentalUnits);
478  print("Derived Units", derivedUnits);
479  print("Scaled Units", scaledUnits);
480  print("Derived-Scaled Units", derivedScaledUnits);
481  }
482 
483  return 0;
484 }
485 
486 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:492
An STL-conforming hash table.
Definition: HashTable.H:127
static Stream & writeDivider(Stream &os)
Write the standard file section divider.
Definition: IOobjectI.H:93
Input from memory buffer stream.
Definition: IStringStream.H:52
Output to memory buffer stream.
Definition: OStringStream.H:52
string str() const
Return the string.
Extract command arguments and options from the supplied argc and argv parameters.
Definition: argList.H:103
static label nArgs(int argc, char *argv[])
Return the number of arguments (not options)
Definition: argList.C:291
static void addNote(const string &)
Add extra notes for the usage information.
Definition: argList.C:152
static void addBoolOption(const word &opt, const string &usage="")
Add to a bool option to validOptions with usage information.
Definition: argList.C:111
bool optionFound(const word &opt) const
Return true if the named option is found.
Definition: argListI.H:114
static SLList< string > validArgs
A list of valid (mandatory) arguments.
Definition: argList.H:153
Dimension set for the base types.
Definition: dimensionSet.H:125
InfoProxy< dimensionSet > info() const
Return info proxy.
Definition: dimensionSet.H:245
dimensionType
Define an enumeration for the names of the dimension exponents.
Definition: dimensionSet.H:139
Unit conversion structure. Contains the associated dimensions and the multiplier with which to conver...
Definition: unitSet.H:68
const dimensionSet & dimensions() const
Access the dimensions.
Definition: unitSetI.H:50
bool standard() const
Return whether this unit is standard. I.e., is its multiplier one?
Definition: unitSetI.H:74
T toStandard(const T &) const
Convert a value to standard units.
@ nDimlessUnits
Definition: unitSet.H:76
dimlessUnitType
Define an enumeration for the names of the dimensionless unit.
Definition: unitSet.H:82
A class for handling words, derived from string.
Definition: word.H:63
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
int main(int argc, char *argv[])
Definition: financialFoam.C:44
const char *const group
Group name for atomic constants.
const HashTable< dimensionSet > table
Table of dimensions.
Definition: dimensions.C:74
string breakIntoIndentedLines(const string &str, const string::size_type nLength=80, const string::size_type nIndent=0)
Break a string up into indented lines.
Definition: stringOps.C:963
const HashTable< unitSet > & table()
Get the table of unit conversions.
Definition: units.C:142
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
const doubleScalar e
Definition: doubleScalar.H:106
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
bool writeInfoHeader
messageStream Info
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
error FatalError
static const char nl
Definition: Ostream.H:297
Foam::argList args(argc, argv)
Useful unit conversions.