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