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  - \par -case <dir>
37  Select a case directory instead of the current working directory
38 
39  - \par -parallel
40  Specify case as a parallel job
41 
42  - \par -doc
43  Display the documentation in browser
44 
45  - \par -srcDoc
46  Display the source documentation in browser
47 
48  - \par -help
49  Print the usage
50 
51  - \par -entry <name>
52  Selects an entry
53 
54  - \par -keywords <name>
55  Prints the keywords (of the selected entry or of the top level if
56  no entry was selected
57 
58  - \par -add <value>
59  Adds the entry (should not exist yet)
60 
61  - \par -set <value>
62  Adds or replaces the entry selected by \c -entry
63 
64  - \par -set <substitutions>
65  Applies the list of substitutions
66 
67  - \par -merge <value>
68  Merges the entry
69 
70  - \par -dict
71  Set, add or merge entry from a dictionary
72 
73  - \par -remove
74  Remove the selected entry
75 
76  - \par -diff <dictionary>
77  Write differences with respect to the specified dictionary
78  (or sub entry if -entry specified)
79 
80  - \par -expand
81  Read the specified dictionary file, expand the macros etc. and write
82  the resulting dictionary to standard output.
83 
84  - \par -includes
85  List the \c #include and \c #includeIfPresent files to standard output
86 
87  Example usage:
88  - Change simulation to run for one timestep only:
89  \verbatim
90  foamDictionary system/controlDict -entry stopAt -set writeNow
91  \endverbatim
92 
93  - Change solver:
94  \verbatim
95  foamDictionary system/fvSolution -entry solvers/p/solver -set PCG
96  \endverbatim
97 
98  - Print bc type:
99  \verbatim
100  foamDictionary 0/U -entry boundaryField/movingWall/type
101  \endverbatim
102 
103  - Change bc parameter:
104  \verbatim
105  foamDictionary 0/U -entry boundaryField/movingWall/value \
106  -set "uniform (2 0 0)"
107  \endverbatim
108 
109  - Change bc parameter in parallel:
110  \verbatim
111  mpirun -np 4 foamDictionary 0.5/U \
112  -entry boundaryField/movingWall/value \
113  -set "uniform (2 0 0)" -parallel
114  \endverbatim
115 
116  - Change whole bc type:
117  \verbatim
118  foamDictionary 0/U -entry boundaryField/movingWall \
119  -set "{type uniformFixedValue; uniformValue (2 0 0);}"
120  \endverbatim
121 
122  - Write the differences with respect to a template dictionary:
123  \verbatim
124  foamDictionary 0/U -diff $FOAM_ETC/templates/closedVolume/0/U
125  \endverbatim
126 
127  - Write the differences in boundaryField with respect to a
128  template dictionary:
129  \verbatim
130  foamDictionary 0/U -diff $FOAM_ETC/templates/closedVolume/0/U \
131  -entry boundaryField
132  \endverbatim
133 
134  - Change patch type:
135  \verbatim
136  foamDictionary constant/polyMesh/boundary \
137  -entry entry0/fixedWalls/type -set patch
138  \endverbatim
139  This uses special parsing of Lists which stores these in the
140  dictionary with keyword 'entryDDD' where DDD is the position
141  in the dictionary (after ignoring the FoamFile entry).
142 
143  - Substitute multiple entries:
144  \verbatim
145  foamDictionary system/controlDict -set "startTime=2000, endTime=3000"
146  \endverbatim
147 
148 \*---------------------------------------------------------------------------*/
149 
150 #include "argList.H"
151 #include "Time.H"
152 #include "localIOdictionary.H"
153 #include "Pair.H"
154 #include "IFstream.H"
155 #include "OFstream.H"
156 #include "includeEntry.H"
157 #include "inputSyntaxEntry.H"
158 
159 using namespace Foam;
160 
161 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
162 
163 //- Read dictionary from file and return
164 // Sets stream to binary mode if specified in the optional header
165 IOstream::streamFormat readDict(dictionary& dict, const fileName& dictFileName)
166 {
168 
169  // Read the first entry and if it is FoamFile set the file format
170  {
171  IFstream dictFile(dictFileName);
172  if (!dictFile().good())
173  {
175  << "Cannot open file " << dictFileName
176  << exit(FatalError, 1);
177  }
178 
179  // Check if the first token in the file is "FoamFile"
180  // to avoid problems if the first entry is a variable or function
181  token firstToken;
182  dictFile.read(firstToken);
183  if (firstToken.isWord() && firstToken.wordToken() == IOobject::foamFile)
184  {
185  dictFile.putBack(firstToken);
186 
187  // Read the first entry from the dictionary
188  autoPtr<entry> firstEntry(entry::New(dictFile()));
189 
190  // If the first entry is the "FoamFile" header
191  // read and set the stream format
192  if
193  (
194  firstEntry->isDict()
195  && firstEntry->keyword() == IOobject::foamFile
196  )
197  {
198  dictFormat = IOstream::formatEnum
199  (
200  firstEntry->dict().lookup("format")
201  );
202  }
203  }
204  }
205 
206  IFstream dictFile(dictFileName, dictFormat);
207 
208  // Read and add the rest of the dictionary entries
209  // preserving the IOobject::foamFile header dictionary if present
210  dict.read(dictFile(), true);
211 
212  return dictFormat;
213 }
214 
215 
216 //- Convert keyword syntax to "dot" if the dictionary is "dot" syntax
217 word dotToSlash(const fileName& entryName)
218 {
219  if
220  (
222  && entryName.find('/') != string::npos
223  )
224  {
225  wordList entryNames(entryName.components('/'));
226 
227  word entry(entryNames[0]);
228  for (label i = 1; i < entryNames.size(); i++)
229  {
230  entry += word('.') + entryNames[i];
231  }
232  return entry;
233  }
234  else
235  {
236  return entryName;
237  }
238 }
239 
240 
241 void remove(dictionary& dict, const dictionary& removeDict)
242 {
243  forAllConstIter(dictionary, removeDict, iter)
244  {
245  entry* entPtr = dict.lookupEntryPtr
246  (
247  iter().keyword(),
248  false,
249  false
250  );
251 
252  if (entPtr)
253  {
254  if (entPtr->isDict())
255  {
256  if (iter().isDict())
257  {
258  remove(entPtr->dict(), iter().dict());
259 
260  // Check if dictionary is empty
261  if (!entPtr->dict().size())
262  {
263  dict.remove(iter().keyword());
264  }
265  }
266  }
267  else if (!iter().isDict())
268  {
269  if (*entPtr == iter())
270  {
271  dict.remove(iter().keyword());
272  }
273  }
274  }
275  }
276 }
277 
278 
279 void substitute(dictionary& dict, string substitutions)
280 {
282  List<Tuple2<word, string>> namedArgs;
283  dictArgList(substitutions, args, namedArgs);
284 
285  forAll(namedArgs, i)
286  {
287  const Pair<word> dAk(dictAndKeyword(dotToSlash(namedArgs[i].first())));
288  dictionary& subDict(dict.scopedDict(dAk.first()));
289  IStringStream entryStream
290  (
291  dAk.second() + ' ' + namedArgs[i].second() + ';'
292  );
293  subDict.set(entry::New(entryStream).ptr());
294  }
295 }
296 
297 
298 int main(int argc, char *argv[])
299 {
300  writeInfoHeader = false;
301 
302  argList::addNote("manipulates dictionaries");
303 
304  argList::validArgs.append("dictionary file");
305 
306  argList::addBoolOption("keywords", "list keywords");
307  argList::addOption("entry", "name", "report/select the named entry");
309  (
310  "value",
311  "Print entry value"
312  );
314  (
315  "set",
316  "value",
317  "Set entry value, add new entry or apply list of substitutions"
318  );
320  (
321  "add",
322  "value",
323  "Add a new entry"
324  );
326  (
327  "merge",
328  "value",
329  "Merge entry"
330  );
332  (
333  "dict",
334  "Set, add or merge entry from a dictionary."
335  );
337  (
338  "remove",
339  "Remove the entry."
340  );
342  (
343  "diff",
344  "dict",
345  "Write differences with respect to the specified dictionary"
346  );
348  (
349  "includes",
350  "List the #include/#includeIfPresent files to standard output"
351  );
353  (
354  "expand",
355  "Read the specified dictionary file, expand the macros etc. and write "
356  "the resulting dictionary to standard output"
357  );
359  (
360  "writePrecision",
361  "label",
362  "Write with the specified precision"
363  );
364 
365  argList args(argc, argv);
366 
367  const bool listIncludes = args.optionFound("includes");
368 
369  if (listIncludes)
370  {
372  }
373 
374  // Do not expand functionEntries except during dictionary expansion
375  // with the -expand option
376  if (!args.optionFound("expand"))
377  {
379  }
380 
381  // Set write precision
382  if (args.optionFound("writePrecision"))
383  {
384  const label writePrecision = args.optionRead<label>("writePrecision");
385  IOstream::defaultPrecision(writePrecision);
386  Sout.precision(writePrecision);
388  }
389 
390  const fileName dictPath(args[1]);
391 
392  Time* runTimePtr = nullptr;
393  localIOdictionary* localDictPtr = nullptr;
394 
395  dictionary* dictPtr = nullptr;
397 
398  // When running in parallel read the dictionary as a case localIOdictionary
399  // supporting file handlers
400  if (Pstream::parRun())
401  {
402  if (!args.checkRootCase())
403  {
404  FatalError.exit();
405  }
406 
408 
409  const wordList dictPathComponents(dictPath.components());
410 
411  if (dictPathComponents.size() == 1)
412  {
414  << "File name " << dictPath
415  << " does not contain an instance path needed in parallel"
416  << exit(FatalError, 1);
417  }
418 
419  const word instance = dictPathComponents[0];
420  const fileName dictFileName
421  (
422  SubList<word>(dictPathComponents, dictPathComponents.size() - 1, 1)
423  );
424 
425  scalar time;
426  if (readScalar(instance.c_str(), time))
427  {
428  runTimePtr->setTime(time, 0);
429  }
430 
431  localDictPtr = new localIOdictionary
432  (
433  IOobject
434  (
435  dictFileName,
436  instance,
437  *runTimePtr,
440  false
441  )
442  );
443  }
444  else
445  {
446  dictPtr = new dictionary(dictPath);
447  dictFormat = readDict(*dictPtr, dictPath);
448  }
449 
450  dictionary& dict = localDictPtr ? *localDictPtr : *dictPtr;
451 
452  bool changed = false;
453 
454  if (listIncludes)
455  {
456  return 0;
457  }
458  else if (args.optionFound("expand") && !args.optionFound("entry"))
459  {
461  <<"//\n// " << dictPath << "\n//\n";
462 
463  // Change the format to ASCII
465  {
467  (
468  "format",
470  true
471  );
472  }
473 
474  dict.dictionary::write(Info, false);
476 
477  return 0;
478  }
479 
480 
481  // Second dictionary for -diff
482  fileName diffFileName;
483  dictionary diffDict;
484 
485  if (args.optionReadIfPresent("diff", diffFileName))
486  {
487  readDict(diffDict, diffFileName);
488  }
489 
490  word entryName;
491  if (args.optionReadIfPresent("entry", entryName))
492  {
493  const word scopedName(dotToSlash(entryName));
494 
495  string newValue;
496  if
497  (
498  args.optionReadIfPresent("set", newValue)
499  || args.optionReadIfPresent("add", newValue)
500  || args.optionReadIfPresent("merge", newValue)
501  )
502  {
503  const bool overwrite = args.optionFound("set");
504  const bool merge = args.optionFound("merge");
505 
506  const Pair<word> dAk(dictAndKeyword(scopedName));
507  dictionary& subDict(dict.scopedDict(dAk.first()));
508 
509  entry* ePtr = nullptr;
510 
511  if (args.optionFound("dict"))
512  {
513  const fileName fromDictFileName(newValue);
514  dictionary fromDict;
515  readDict(fromDict, fromDictFileName);
516 
517  const entry* fePtr
518  (
519  fromDict.lookupScopedEntryPtr
520  (
521  scopedName,
522  false,
523  true // Support wildcards
524  )
525  );
526 
527  if (!fePtr)
528  {
530  << "Cannot find entry " << entryName
531  << " in file " << fromDictFileName
532  << exit(FatalError, 1);
533  }
534 
535  ePtr = fePtr->clone().ptr();
536  }
537  else
538  {
539  IStringStream str(string(dAk.second()) + ' ' + newValue + ';');
540  ePtr = entry::New(str).ptr();
541  }
542 
543  if (overwrite)
544  {
545  Info << "New entry " << *ePtr << endl;
546  subDict.set(ePtr);
547  }
548  else
549  {
550  subDict.add(ePtr, merge);
551  }
552  changed = true;
553  }
554  else if (args.optionFound("remove"))
555  {
556  // Extract dictionary name and keyword
557  const Pair<word> dAk(dictAndKeyword(scopedName));
558 
559  dictionary& subDict(dict.scopedDict(dAk.first()));
560  subDict.remove(dAk.second());
561  changed = true;
562  }
563  else
564  {
565  // Optionally remove a second dictionary
566  if (args.optionFound("diff"))
567  {
568  const Pair<word> dAk(dictAndKeyword(scopedName));
569 
570  dictionary& subDict(dict.scopedDict(dAk.first()));
571  const dictionary& subDict2(diffDict.scopedDict(dAk.first()));
572 
573  entry* ePtr =
574  subDict.lookupEntryPtr(dAk.second(), false, true);
575  const entry* e2Ptr =
576  subDict2.lookupEntryPtr(dAk.second(), false, true);
577 
578  if (ePtr && e2Ptr)
579  {
580  if (*ePtr == *e2Ptr)
581  {
582  subDict.remove(dAk.second());
583  }
584  else if (ePtr->isDict() && e2Ptr->isDict())
585  {
586  remove(ePtr->dict(), e2Ptr->dict());
587  }
588  }
589  }
590 
591 
592  const entry* entPtr = dict.lookupScopedEntryPtr
593  (
594  scopedName,
595  false,
596  true // Support wildcards
597  );
598 
599  if (entPtr)
600  {
601  if (args.optionFound("keywords"))
602  {
603  const dictionary& dict = entPtr->dict();
605  {
606  Info<< iter().keyword() << endl;
607  }
608  }
609  else
610  {
611  if (args.optionFound("value"))
612  {
613  if (entPtr->isStream())
614  {
615  const tokenList& tokens = entPtr->stream();
616  forAll(tokens, i)
617  {
618  Info<< tokens[i];
619  if (i < tokens.size() - 1)
620  {
621  Info<< token::SPACE;
622  }
623  }
624  Info<< endl;
625  }
626  else if (entPtr->isDict())
627  {
628  Info<< entPtr->dict();
629  }
630  }
631  else
632  {
633  Info<< *entPtr;
634  }
635  }
636  }
637  else
638  {
640  << "Cannot find entry " << entryName
641  << exit(FatalIOError, 2);
642  }
643  }
644  }
645  else if (args.optionFound("set"))
646  {
647  const string substitutions(args.optionRead<string>("set"));
648  substitute(dict, substitutions);
649  changed = true;
650  }
651  else if (args.optionFound("keywords"))
652  {
654  {
655  Info<< iter().keyword() << endl;
656  }
657  }
658  else if (args.optionFound("diff"))
659  {
660  remove(dict, diffDict);
661  dict.dictionary::write(Info, false);
662  }
663  else
664  {
665  dict.dictionary::write(Info, false);
666  }
667 
668  if (changed)
669  {
670  if (localDictPtr)
671  {
672  localDictPtr->regIOobject::write();
673  }
674  else if (dictPtr)
675  {
676  OFstream os(dictPath, dictFormat);
678  if (dictPtr->found(IOobject::foamFile))
679  {
680  os << IOobject::foamFile;
681  dictPtr->subDict(IOobject::foamFile).write(os);
682  dictPtr->remove(IOobject::foamFile);
683  IOobject::writeDivider(os) << nl;
684  }
685  dictPtr->write(os, false);
687  }
688  }
689 
690  delete dictPtr;
691  delete localDictPtr;
692  delete runTimePtr;
693 
694  return 0;
695 }
696 
697 
698 // ************************************************************************* //
#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:108
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:204
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)