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  {
287  Info<< "+ Dimensions = " << dimension.info() << nl;
288  }
289  Info << "+ Exponents = " << dimension << nl;
290  const string standardName =
291  standardUnitName
292  (
293  dimensionUnitNames,
294  dimlessUnitNames,
295  dimension
296  );
297  DynamicList<word> standardNames(1, standardName.c_str());
298  DynamicList<word> otherNames;
300  {
301  if (iter.key() != standardName && (iter()/dimension).unitless())
302  {
303  (iter().standard() ? standardNames : otherNames).append
304  (
305  iter.key()
306  );
307  }
308  }
309  Info<< "+ Standard Unit"
310  << (standardNames.size() > 1 ? "s" : "") << " =";
311  forAll(standardNames, i) Info<< " [" << standardNames[i] << "]";
312  Info<< nl;
313  if (otherNames.size())
314  {
315  Info<< "+ Other Units =";
316  forAll(otherNames, i) Info<< " [" << otherNames[i] << "]";
317  Info<< nl;
318  }
319  Info<< endl;
320  }
321  };
322 
323  auto printUnit = [&](const word& name, const unitSet& unit)
324  {
325  if (value)
326  {
327  Info<< unit.toStandard(scalar(1));
328  }
329  else
330  {
331  const string standardName =
332  standardUnitName(dimensionUnitNames, dimlessUnitNames, unit);
333  Info<< "Unit [" << name << "]" << nl
334  << "+ Dimensions = " << unit.dimensions().info() << nl;
335  if (!isFundamental(unit))
336  {
337  Info<< "+ Standard Unit = [" << standardName.c_str() << "]"
338  << nl;
339  }
340  Info<< "+ Conversion Factor = " << unit.toStandard(scalar(1))
341  << nl << endl;
342  }
343  };
344 
345  if (!value) Info<< endl;
346 
347  // Print all dimensions and units
348  if (all)
349  {
351  {
352  printDimension(iter.key(), iter());
353  }
354 
356  {
357  printUnit(iter.key(), iter());
358  }
359 
360  if (nArgs)
361  {
363  Info<< nl;
364  }
365  }
366 
367  // Print a single dimension or unit
368  if (nArgs == 1)
369  {
370  const string name(args[1]);
371 
372  const bool isDimension = stringIs(name, dimensions::table);
373  const bool isUnit = stringIs(name, units::table());
374 
375  if (isDimension && !isUnit)
376  {
377  printDimension(name, IStringStream(("[" + name + "]").c_str())());
378 
379  return 0;
380  }
381 
382  if (!isDimension && isUnit)
383  {
384  printUnit(name, IStringStream(("[" + name + "]").c_str())());
385 
386  return 0;
387  }
388 
390  << "'" << args[1].c_str()
391  << "' is not a valid dimension or unit"
392  << exit(FatalError);
393  }
394 
395  // Print the conversion between two units
396  if (nArgs == 2)
397  {
398  const string name1(args[1]);
399  const string name2(args[2]);
400 
401  auto assertStringIsUnit = [](const string& str)
402  {
403  if (stringIs(str, dimensions::table))
404  {
406  << "'" << str.c_str() << "' is a dimension. "
407  << "Comparison is only supported for units."
408  << exit(FatalError);
409  }
410  };
411  assertStringIsUnit(name1);
412  assertStringIsUnit(name2);
413 
414  const unitSet unit1
415  (
416  IStringStream(("[" + name1 + "]").c_str())()
417  );
418  const unitSet unit2
419  (
420  IStringStream(("[" + name2 + "]").c_str())()
421  );
422 
423  // Check the units are the same, except for the multiplier
424  unitSet
425  (
426  unit1.dimensions(),
427  unit1[unitSet::FRACTION],
428  unit1[unitSet::ANGLE],
429  1
430  )
431  + unitSet
432  (
433  unit2.dimensions(),
434  unit2[unitSet::FRACTION],
435  unit2[unitSet::ANGLE],
436  1
437  );
438 
439  const string standardName =
440  standardUnitName(dimensionUnitNames, dimlessUnitNames, unit1);
441 
442  if (value)
443  {
444  Info<< unit2.toUser(unit1.toStandard(scalar(1)));
445  }
446  else
447  {
448  Info<< "Units [" << name1.c_str() << "] [" << name2.c_str() << ']'
449  << nl << "+ Dimensions = " << unit1.dimensions().info() << nl
450  << "+ Standard Unit = [" << standardName.c_str() << "]" << nl
451  << "+ Conversion: " << 1 << " [" << name1.c_str() << "] = "
452  << unit2.toUser(unit1.toStandard(scalar(1))) << " ["
453  << name2.c_str() << ']' << nl
454  << "+ Conversion: " << 1 << " [" << name2.c_str() << "] = "
455  << unit1.toUser(unit2.toStandard(scalar(1))) << " ["
456  << name1.c_str() << ']' << nl
457  << endl;
458  }
459 
460  return 0;
461  }
462 
463  // Print just the names of the dimensions and units
464  if (!all)
465  {
466  auto print = [](const char* group, const DynamicList<word>& names)
467  {
468  Info<< group << ":" << nl;
469 
470  OStringStream str;
471  forAll(names, i)
472  {
473  str << (i ? " " : "") << "[" << names[i] << ']';
474  }
475 
476  Info<< stringOps::breakIntoIndentedLines(str.str(), 80, 4).c_str()
477  << nl << endl;
478  };
479 
480  DynamicList<word> fundamentalDimensions;
481  DynamicList<word> derivedDimensions;
483  {
484  (
485  isFundamental(iter())
486  ? fundamentalDimensions
487  : derivedDimensions
488  ).append(iter.key());
489  }
490 
491  print("Fundamental Dimensions", fundamentalDimensions);
492  print("Derived Dimensions", derivedDimensions);
493 
494  DynamicList<word> fundamentalUnits;
495  DynamicList<word> derivedUnits;
496  DynamicList<word> scaledUnits;
497  DynamicList<word> derivedScaledUnits;
499  {
500  (
501  isFundamental(iter()) && iter().standard() ? fundamentalUnits
502  : !isFundamental(iter()) && iter().standard() ? derivedUnits
503  : isFundamental(iter()) && !iter().standard() ? scaledUnits
504  : derivedScaledUnits
505  ).append(iter.key());
506  }
507 
508  print("Fundamental Units", fundamentalUnits);
509  print("Derived Units", derivedUnits);
510  print("Scaled Units", scaledUnits);
511  print("Derived-Scaled Units", derivedScaledUnits);
512  }
513 
514  return 0;
515 }
516 
517 // ************************************************************************* //
#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
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
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:263
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:80
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.
HashTable< dimensionSet > table
Table of dimensions.
Definition: dimensions.C:38
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 unitSet unitless
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.