foamDictionary.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) 2016-2023 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  foamDictionary
26 
27 Description
28  Interrogates and manipulates dictionaries.
29 
30  Supports parallel operation for decomposed dictionary files associated with
31  a case. These may be mesh or field files or any other decomposed
32  dictionaries.
33 
34 Usage
35  \b foamDictionary [OPTION] dictionary
36 
37  - \par -case <dir>
38  Select a case directory instead of the current working directory
39 
40  - \par -parallel
41  Specify case as a parallel job
42 
43  - \par -doc
44  Display the documentation in browser
45 
46  - \par -srcDoc
47  Display the source documentation in browser
48 
49  - \par -help
50  Print the usage
51 
52  - \par -entry <name>
53  Selects an entry
54 
55  - \par -keywords <name>
56  Prints the keywords (of the selected entry or of the top level if
57  no entry was selected
58 
59  - \par -add <value>
60  Adds the entry (should not exist yet)
61 
62  - \par -set <value>
63  Adds or replaces the entry selected by \c -entry
64 
65  - \par -set <substitutions>
66  Applies the list of substitutions
67 
68  - \par -merge <value>
69  Merges the entry
70 
71  - \par -dict
72  Set, add or merge entry from a dictionary
73 
74  - \par -remove
75  Remove the selected entry
76 
77  - \par -diff <dictionary>
78  Write differences with respect to the specified dictionary
79  (or sub entry if -entry specified)
80 
81  - \par -expand
82  Read the specified dictionary file, expand the macros etc. and write
83  the resulting dictionary to standard output.
84 
85  - \par -includes
86  List the \c #include and \c #includeIfPresent files to standard output
87 
88  Example usage:
89  - Change simulation to run for one timestep only:
90  \verbatim
91  foamDictionary system/controlDict -entry stopAt -set writeNow
92  \endverbatim
93 
94  - Change solver:
95  \verbatim
96  foamDictionary system/fvSolution -entry solvers/p/solver -set PCG
97  \endverbatim
98 
99  - Print bc type:
100  \verbatim
101  foamDictionary 0/U -entry boundaryField/movingWall/type
102  \endverbatim
103 
104  - Change bc parameter:
105  \verbatim
106  foamDictionary 0/U -entry boundaryField/movingWall/value \
107  -set "uniform (2 0 0)"
108  \endverbatim
109 
110  - Change bc parameter in parallel:
111  \verbatim
112  mpirun -np 4 foamDictionary 0.5/U \
113  -entry boundaryField/movingWall/value \
114  -set "uniform (2 0 0)" -parallel
115  \endverbatim
116 
117  - Change whole bc type:
118  \verbatim
119  foamDictionary 0/U -entry boundaryField/movingWall \
120  -set "{type uniformFixedValue; uniformValue (2 0 0);}"
121  \endverbatim
122 
123  - Write the differences with respect to a template dictionary:
124  \verbatim
125  foamDictionary 0/U -diff $FOAM_ETC/templates/closedVolume/0/U
126  \endverbatim
127 
128  - Write the differences in boundaryField with respect to a
129  template dictionary:
130  \verbatim
131  foamDictionary 0/U -diff $FOAM_ETC/templates/closedVolume/0/U \
132  -entry boundaryField
133  \endverbatim
134 
135  - Change patch type:
136  \verbatim
137  foamDictionary constant/polyMesh/boundary \
138  -entry entry0/fixedWalls/type -set patch
139  \endverbatim
140  This uses special parsing of Lists which stores these in the
141  dictionary with keyword 'entryDDD' where DDD is the position
142  in the dictionary (after ignoring the FoamFile entry).
143 
144  - Substitute multiple entries:
145  \verbatim
146  foamDictionary system/controlDict -set "startTime=2000, endTime=3000"
147  \endverbatim
148 
149 \*---------------------------------------------------------------------------*/
150 
151 #include "argList.H"
152 #include "Time.H"
153 #include "localIOdictionary.H"
154 #include "Pair.H"
155 #include "IFstream.H"
156 #include "OFstream.H"
157 #include "includeEntry.H"
158 #include "inputSyntaxEntry.H"
159 
160 using namespace Foam;
161 
162 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
163 
164 //- Read dictionary from file and return
165 // Sets stream to binary mode if specified in the optional header
166 IOstream::streamFormat readDict(dictionary& dict, const fileName& dictFileName)
167 {
169 
170  // Read the first entry and if it is FoamFile set the file format
171  {
172  IFstream dictFile(dictFileName);
173  if (!dictFile().good())
174  {
176  << "Cannot open file " << dictFileName
177  << exit(FatalError, 1);
178  }
179 
180  // Check if the first token in the file is "FoamFile"
181  // to avoid problems if the first entry is a variable or function
182  token firstToken;
183  dictFile.read(firstToken);
184  if (firstToken.isWord() && firstToken.wordToken() == IOobject::foamFile)
185  {
186  dictFile.putBack(firstToken);
187 
188  // Read the first entry from the dictionary
189  autoPtr<entry> firstEntry(entry::New(dictFile()));
190 
191  // If the first entry is the "FoamFile" header
192  // read and set the stream format
193  if
194  (
195  firstEntry->isDict()
196  && firstEntry->keyword() == IOobject::foamFile
197  )
198  {
199  dictFormat = IOstream::formatEnum
200  (
201  firstEntry->dict().lookup("format")
202  );
203  }
204  }
205  }
206 
207  IFstream dictFile(dictFileName, dictFormat);
208 
209  // Read and add the rest of the dictionary entries
210  // preserving the IOobject::foamFile header dictionary if present
211  dict.read(dictFile(), true);
212 
213  return dictFormat;
214 }
215 
216 
217 //- Convert keyword syntax to "dot" if the dictionary is "dot" syntax
218 word dotToSlash(const fileName& entryName)
219 {
220  if
221  (
223  && entryName.find('/') != string::npos
224  )
225  {
226  wordList entryNames(entryName.components('/'));
227 
228  word entry(entryNames[0]);
229  for (label i = 1; i < entryNames.size(); i++)
230  {
231  entry += word('.') + entryNames[i];
232  }
233  return entry;
234  }
235  else
236  {
237  return entryName;
238  }
239 }
240 
241 
242 void remove(dictionary& dict, const dictionary& removeDict)
243 {
244  forAllConstIter(dictionary, removeDict, iter)
245  {
246  entry* entPtr = dict.lookupEntryPtr
247  (
248  iter().keyword(),
249  false,
250  false
251  );
252 
253  if (entPtr)
254  {
255  if (entPtr->isDict())
256  {
257  if (iter().isDict())
258  {
259  remove(entPtr->dict(), iter().dict());
260 
261  // Check if dictionary is empty
262  if (!entPtr->dict().size())
263  {
264  dict.remove(iter().keyword());
265  }
266  }
267  }
268  else if (!iter().isDict())
269  {
270  if (*entPtr == iter())
271  {
272  dict.remove(iter().keyword());
273  }
274  }
275  }
276  }
277 }
278 
279 
280 void substitute(dictionary& dict, string substitutions)
281 {
283  List<Tuple2<word, string>> namedArgs;
284  dictArgList(substitutions, args, namedArgs);
285 
286  forAll(namedArgs, i)
287  {
288  const Pair<word> dAk(dictAndKeyword(dotToSlash(namedArgs[i].first())));
289  dictionary& subDict(dict.scopedDict(dAk.first()));
290  IStringStream entryStream
291  (
292  dAk.second() + ' ' + namedArgs[i].second() + ';'
293  );
294  subDict.set(entry::New(entryStream).ptr());
295  }
296 }
297 
298 
299 int main(int argc, char *argv[])
300 {
301  writeInfoHeader = false;
302 
303  argList::addNote("manipulates dictionaries");
304 
305  argList::validArgs.append("dictionary file");
306 
307  argList::addBoolOption("keywords", "list keywords");
308  argList::addOption("entry", "name", "report/select the named entry");
310  (
311  "value",
312  "Print entry value"
313  );
315  (
316  "set",
317  "value",
318  "Set entry value, add new entry or apply list of substitutions"
319  );
321  (
322  "add",
323  "value",
324  "Add a new entry"
325  );
327  (
328  "merge",
329  "value",
330  "Merge entry"
331  );
333  (
334  "dict",
335  "Set, add or merge entry from a dictionary."
336  );
338  (
339  "remove",
340  "Remove the entry."
341  );
343  (
344  "diff",
345  "dict",
346  "Write differences with respect to the specified dictionary"
347  );
349  (
350  "includes",
351  "List the #include/#includeIfPresent files to standard output"
352  );
354  (
355  "expand",
356  "Read the specified dictionary file, expand the macros etc. and write "
357  "the resulting dictionary to standard output"
358  );
360  (
361  "writePrecision",
362  "label",
363  "Write with the specified precision"
364  );
365 
366  argList args(argc, argv);
367 
368  const bool listIncludes = args.optionFound("includes");
369 
370  if (listIncludes)
371  {
373  }
374 
375  // Do not expand functionEntries except during dictionary expansion
376  // with the -expand option
377  if (!args.optionFound("expand"))
378  {
380  }
381 
382  // Set write precision
383  if (args.optionFound("writePrecision"))
384  {
385  const label writePrecision = args.optionRead<label>("writePrecision");
386  IOstream::defaultPrecision(writePrecision);
387  Sout.precision(writePrecision);
389  }
390 
391  const fileName dictPath(args[1]);
392 
393  Time* runTimePtr = nullptr;
394  localIOdictionary* localDictPtr = nullptr;
395 
396  dictionary* dictPtr = nullptr;
398 
399  // When running in parallel read the dictionary as a case localIOdictionary
400  // supporting file handlers
401  if (Pstream::parRun())
402  {
403  if (!args.checkRootCase())
404  {
405  FatalError.exit();
406  }
407 
409 
410  const wordList dictPathComponents(dictPath.components());
411 
412  if (dictPathComponents.size() == 1)
413  {
415  << "File name " << dictPath
416  << " does not contain an instance path needed in parallel"
417  << exit(FatalError, 1);
418  }
419 
420  const word instance = dictPathComponents[0];
421  const fileName dictFileName
422  (
423  SubList<word>(dictPathComponents, dictPathComponents.size() - 1, 1)
424  );
425 
426  scalar time;
427  if (readScalar(instance.c_str(), time))
428  {
429  runTimePtr->setTime(time, 0);
430  }
431 
432  localDictPtr = new localIOdictionary
433  (
434  IOobject
435  (
436  dictFileName,
437  instance,
438  *runTimePtr,
441  false
442  )
443  );
444  }
445  else
446  {
447  dictPtr = new dictionary(dictPath);
448  dictFormat = readDict(*dictPtr, dictPath);
449  }
450 
451  dictionary& dict = localDictPtr ? *localDictPtr : *dictPtr;
452 
453  bool changed = false;
454 
455  if (listIncludes)
456  {
457  return 0;
458  }
459  else if (args.optionFound("expand") && !args.optionFound("entry"))
460  {
462  <<"//\n// " << dictPath << "\n//\n";
463 
464  // Change the format to ASCII
466  {
468  (
469  "format",
471  true
472  );
473  }
474 
475  dict.dictionary::write(Info, false);
477 
478  return 0;
479  }
480 
481 
482  // Second dictionary for -diff
483  fileName diffFileName;
484  dictionary diffDict;
485 
486  if (args.optionReadIfPresent("diff", diffFileName))
487  {
488  readDict(diffDict, diffFileName);
489  }
490 
491  word entryName;
492  if (args.optionReadIfPresent("entry", entryName))
493  {
494  const word scopedName(dotToSlash(entryName));
495 
496  string newValue;
497  if
498  (
499  args.optionReadIfPresent("set", newValue)
500  || args.optionReadIfPresent("add", newValue)
501  || args.optionReadIfPresent("merge", newValue)
502  )
503  {
504  const bool overwrite = args.optionFound("set");
505  const bool merge = args.optionFound("merge");
506 
507  const Pair<word> dAk(dictAndKeyword(scopedName));
508  dictionary& subDict(dict.scopedDict(dAk.first()));
509 
510  entry* ePtr = nullptr;
511 
512  if (args.optionFound("dict"))
513  {
514  const fileName fromDictFileName(newValue);
515  dictionary fromDict;
516  readDict(fromDict, fromDictFileName);
517 
518  const entry* fePtr
519  (
520  fromDict.lookupScopedEntryPtr
521  (
522  scopedName,
523  false,
524  true // Support wildcards
525  )
526  );
527 
528  if (!fePtr)
529  {
531  << "Cannot find entry " << entryName
532  << " in file " << fromDictFileName
533  << exit(FatalError, 1);
534  }
535 
536  ePtr = fePtr->clone().ptr();
537  }
538  else
539  {
540  IStringStream str(string(dAk.second()) + ' ' + newValue + ';');
541  ePtr = entry::New(str).ptr();
542  }
543 
544  if (overwrite)
545  {
546  Info << "New entry " << *ePtr << endl;
547  subDict.set(ePtr);
548  }
549  else
550  {
551  subDict.add(ePtr, merge);
552  }
553  changed = true;
554  }
555  else if (args.optionFound("remove"))
556  {
557  // Extract dictionary name and keyword
558  const Pair<word> dAk(dictAndKeyword(scopedName));
559 
560  dictionary& subDict(dict.scopedDict(dAk.first()));
561  subDict.remove(dAk.second());
562  changed = true;
563  }
564  else
565  {
566  // Optionally remove a second dictionary
567  if (args.optionFound("diff"))
568  {
569  const Pair<word> dAk(dictAndKeyword(scopedName));
570 
571  dictionary& subDict(dict.scopedDict(dAk.first()));
572  const dictionary& subDict2(diffDict.scopedDict(dAk.first()));
573 
574  entry* ePtr =
575  subDict.lookupEntryPtr(dAk.second(), false, true);
576  const entry* e2Ptr =
577  subDict2.lookupEntryPtr(dAk.second(), false, true);
578 
579  if (ePtr && e2Ptr)
580  {
581  if (*ePtr == *e2Ptr)
582  {
583  subDict.remove(dAk.second());
584  }
585  else if (ePtr->isDict() && e2Ptr->isDict())
586  {
587  remove(ePtr->dict(), e2Ptr->dict());
588  }
589  }
590  }
591 
592 
593  const entry* entPtr = dict.lookupScopedEntryPtr
594  (
595  scopedName,
596  false,
597  true // Support wildcards
598  );
599 
600  if (entPtr)
601  {
602  if (args.optionFound("keywords"))
603  {
604  const dictionary& dict = entPtr->dict();
606  {
607  Info<< iter().keyword() << endl;
608  }
609  }
610  else
611  {
612  if (args.optionFound("value"))
613  {
614  if (entPtr->isStream())
615  {
616  const tokenList& tokens = entPtr->stream();
617  forAll(tokens, i)
618  {
619  Info<< tokens[i];
620  if (i < tokens.size() - 1)
621  {
622  Info<< token::SPACE;
623  }
624  }
625  Info<< endl;
626  }
627  else if (entPtr->isDict())
628  {
629  Info<< entPtr->dict();
630  }
631  }
632  else
633  {
634  Info<< *entPtr;
635  }
636  }
637  }
638  else
639  {
641  << "Cannot find entry " << entryName
642  << exit(FatalIOError, 2);
643  }
644  }
645  }
646  else if (args.optionFound("set"))
647  {
648  const string substitutions(args.optionRead<string>("set"));
649  substitute(dict, substitutions);
650  changed = true;
651  }
652  else if (args.optionFound("keywords"))
653  {
655  {
656  Info<< iter().keyword() << endl;
657  }
658  }
659  else if (args.optionFound("diff"))
660  {
661  remove(dict, diffDict);
662  dict.dictionary::write(Info, false);
663  }
664  else
665  {
666  dict.dictionary::write(Info, false);
667  }
668 
669  if (changed)
670  {
671  if (localDictPtr)
672  {
673  localDictPtr->regIOobject::write();
674  }
675  else if (dictPtr)
676  {
677  OFstream os(dictPath, dictFormat);
679  if (dictPtr->found(IOobject::foamFile))
680  {
681  os << IOobject::foamFile;
682  dictPtr->subDict(IOobject::foamFile).write(os);
683  dictPtr->remove(IOobject::foamFile);
684  IOobject::writeDivider(os) << nl;
685  }
686  dictPtr->write(os, false);
688  }
689  }
690 
691  delete dictPtr;
692  delete localDictPtr;
693  delete runTimePtr;
694 
695  return 0;
696 }
697 
698 
699 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
Input from file stream.
Definition: IFstream.H:85
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:99
static Stream & writeBanner(Stream &os, bool noHint=false)
Write the standard OpenFOAM file/dictionary banner.
Definition: IOobjectI.H:45
static Stream & writeEndDivider(Stream &os)
Write the standard end file divider.
Definition: IOobjectI.H:103
static constexpr const char * foamFile
Keyword for the FoamFile header sub-dictionary.
Definition: IOobject.H:104
static Stream & writeDivider(Stream &os)
Write the standard file section divider.
Definition: IOobjectI.H:93
streamFormat
Enumeration for the format of data in the stream.
Definition: IOstream.H:87
static unsigned int defaultPrecision()
Return the default precision.
Definition: IOstream.H:458
static streamFormat formatEnum(const word &)
Return stream format of given format name.
Definition: IOstream.C:39
Input from memory buffer stream.
Definition: IStringStream.H:52
Output to file stream.
Definition: OFstream.H:86
virtual int precision() const
Get precision of output field.
Definition: OSstream.C:246
A List obtained as a section of another List.
Definition: SubList.H:56
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:76
virtual void setTime(const Time &)
Reset the time and time-index to those of the given time.
Definition: Time.C:939
static word controlDictName
The default control dictionary name (normally "controlDict")
Definition: Time.H:204
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:399
Extract command arguments and options from the supplied argc and argv parameters.
Definition: argList.H:103
static void addOption(const word &opt, const string &param="", const string &usage="")
Add to an option to validOptions with usage information.
Definition: argList.C:128
static void addNote(const string &)
Add extra notes for the usage information.
Definition: argList.C:159
T optionRead(const word &opt) const
Read a value from the named option.
Definition: argListI.H:193
static void addBoolOption(const word &opt, const string &usage="")
Add to a bool option to validOptions with usage information.
Definition: argList.C:118
bool optionFound(const word &opt) const
Return true if the named option is found.
Definition: argListI.H:114
bool optionReadIfPresent(const word &opt, T &) const
Read a value from the named option if present.
Definition: argListI.H:204
bool checkRootCase() const
Check root path and case path.
Definition: argList.C:1441
static SLList< string > validArgs
A list of valid (mandatory) arguments.
Definition: argList.H:153
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: autoPtr.H:51
A list of keyword definitions, which are a keyword followed by any number of values (e....
Definition: dictionary.H:160
bool read(Istream &, const bool keepHeader=false)
Read dictionary from Istream, optionally keeping the header.
Definition: dictionaryIO.C:112
const entry * lookupEntryPtr(const word &, bool recursive, bool patternMatch) const
Find and return an entry data stream pointer if present.
Definition: dictionary.C:698
void write(Ostream &, const bool subDict=true) const
Write dictionary, normally with sub-dictionary formatting.
Definition: dictionaryIO.C:211
const entry * lookupScopedEntryPtr(const word &, bool recursive, bool patternMatch) const
Find and return an entry data stream pointer if present,.
Definition: dictionary.C:887
bool remove(const word &)
Remove an entry specified by keyword.
Definition: dictionary.C:1332
const dictionary & subDict(const word &) const
Find and return a sub-dictionary.
Definition: dictionary.C:998
bool add(entry *, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:1169
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:659
const dictionary & scopedDict(const word &) const
Find and return a sub-dictionary by scoped lookup.
Definition: dictionary.C:1093
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:68
virtual bool isDict() const
Return true if this entry is a dictionary.
Definition: entry.H:156
virtual ITstream & stream() const =0
Return token stream if this entry is a primitive entry.
static bool New(dictionary &parentDict, Istream &)
Construct from Istream and insert into dictionary.
Definition: entryIO.C:92
virtual const dictionary & dict() const =0
Return dictionary if this entry is a dictionary.
virtual autoPtr< entry > clone(const dictionary &parentDict) const =0
Construct on freestore as copy with reference to the.
virtual bool isStream() const
Return true if this entry is a stream.
Definition: entry.H:147
static int disableFunctionEntries
Definition: entry.H:86
void exit(const int errNo=1)
Exit : can be called for any error to exit program.
Definition: error.C:125
A class for handling file names.
Definition: fileName.H:82
wordList components(const char delimiter='/') const
Return path components as wordList.
Definition: fileName.C:314
static bool log
Report which file is included to stdout.
Definition: includeEntry.H:88
static bool dot()
Return true if the inputSyntax is dot.
localIOdictionary derived from IOdictionary with global set false to disable parallel master reading.
A token holds items read from Istream.
Definition: token.H:73
bool isWord() const
Definition: tokenI.H:261
const word & wordToken() const
Definition: tokenI.H:266
A class for handling words, derived from string.
Definition: word.H:62
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:318
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
int main(int argc, char *argv[])
Definition: financialFoam.C:44
static Time * runTimePtr
Definition: globalFoam.H:51
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
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:251
bool writeInfoHeader
void dictArgList(const string &argString, word &configName, wordReList &args, List< Tuple2< word, string >> &namedArgs)
Parse dictionary substitution argument list.
Definition: dictionary.C:1653
messageStream Info
labelList first(const UList< labelPair > &p)
Definition: patchToPatch.C:39
Pair< word > dictAndKeyword(const word &scopedName)
Extracts dict name and keyword.
Definition: dictionary.C:1812
bool readScalar(const char *buf, doubleScalar &s)
Read whole of buf as a scalar. Return true if successful.
Definition: doubleScalar.H:75
IOerror FatalIOError
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
error FatalError
prefixOSstream Sout(cout, "Sout")
Definition: IOstreams.H:51
static const char nl
Definition: Ostream.H:260
dictionary dict
Foam::argList args(argc, argv)