Loading...
Searching...
No Matches
foamDictionary.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2016-2017 OpenFOAM Foundation
9 Copyright (C) 2017-2025 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27Application
28 foamDictionary
29
30Description
31 Interrogate and manipulate dictionaries.
32
33Usage
34 \b foamDictionary [OPTION] dictionary
35
36 - \par -entry <name>
37 Selects an entry
38
39 - \par -keywords
40 Prints the keywords (of the selected entry or of the top level if
41 no entry was selected
42
43 - \par -add <value>
44 Adds the entry (should not exist yet)
45
46 - \par -set <value>
47 Adds or replaces the entry
48
49 - \par -remove
50 Remove the selected entry
51
52 - \par -diff <dictionary>
53 Write differences with respect to the specified dictionary
54 (or sub entry if -entry specified)
55
56 - \par -diff-etc <dictionary>
57 Write differences with respect to the specified dictionary
58 (or sub entry if -entry specified)
59
60 - \par -expand
61 Read the specified dictionary file, expand the macros etc. and write
62 the resulting dictionary to standard output.
63
64 - \par -includes
65 List the \c \#include and \c \#sinclude files to standard output
66
67 - \par -disableFunctionEntries
68 Do not expand macros or directives (\#include etc)
69
70 - \par -precision int
71 Set default write precision for IOstreams
72
73 Example usage:
74 - Change simulation to run for one timestep only:
75 \verbatim
76 foamDictionary system/controlDict -entry stopAt -set writeNow
77 \endverbatim
78
79 - Change solver:
80 \verbatim
81 foamDictionary system/fvSolution -entry solvers/p/solver -set PCG
82 \endverbatim
83
84 - Print bc type:
85 \verbatim
86 foamDictionary 0/U -entry boundaryField/movingWall/type
87 \endverbatim
88
89 - Change bc parameter:
90 \verbatim
91 foamDictionary 0/U -entry boundaryField/movingWall/value \
92 -set "uniform (2 0 0)"
93 \endverbatim
94
95 - Change whole bc type:
96 \verbatim
97 foamDictionary 0/U -entry boundaryField/movingWall \
98 -set "{type uniformFixedValue; uniformValue (2 0 0);}"
99 \endverbatim
100
101 - Write the differences with respect to a template dictionary:
102 \verbatim
103 foamDictionary 0/U -diff-etc templates/closedVolume/0/U
104 \endverbatim
105
106 - Write the differences in boundaryField with respect to a
107 template dictionary:
108 \verbatim
109 foamDictionary 0/U -diff-etc templates/closedVolume/0/U \
110 -entry boundaryField
111 \endverbatim
112
113 - Change patch type:
114 \verbatim
115 foamDictionary constant/polyMesh/boundary \
116 -entry entry0/fixedWalls/type -set patch
117 \endverbatim
118 This uses special parsing of Lists which stores these in the
119 dictionary with keyword 'entryDDD' where DDD is the position
120 in the dictionary (after ignoring the FoamFile entry).
121
122 Notes:
123 - the use of '.' as the scoping symbol might conflict with
124 e.g. file extensions ('.' is not really considered
125 to be a valid word character). Instead use the '/' as a scoping
126 character e.g.
127 foamDictionary system/snappyHexMeshDict \
128 -entry /geometry/motorBike.obj -remove
129
130\*---------------------------------------------------------------------------*/
131
132#include "argList.H"
133#include "profiling.H"
134#include "Time.H"
135#include "Fstream.H"
136#include "etcFiles.H"
137#include "includeEntry.H"
138
139using namespace Foam;
140
141// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
142
143//- Read dictionary from IFstream, setting the stream to ascii/binary mode
144//- depending on the 'FoamFile' header content
145dictionary readDictionary(Istream& is)
146{
147 auto format = is.format();
148
149 // If the file starts with 'FoamFile { ... }'
150 token tok;
151 if
152 (
153 (tok.read(is) && tok.isWord("FoamFile"))
154 && (tok.read(is) && tok.isPunctuation(token::BEGIN_BLOCK))
155 )
156 {
157 is.putBack(tok); // Put back '{'
158
159 // FoamFile sub-dictionary content
160 dictionary header(is);
161
162 // Get "format" if present
163 format = IOstreamOption::formatEnum("format", header, format);
164 }
165
166 // Start again. Probably does not work well with IPstream though
167 is.rewind();
168 is.format(format);
169
170 // Read, preserving headers
171 return dictionary(is, true);
172}
173
174
175//- Convert very old ':' scope syntax to less old '.' scope syntax,
176// but leave anything with '/' delimiters untouched
177bool upgradeScope(word& entryName)
178{
179 if (entryName.contains(':') && !entryName.contains('/'))
180 {
181 InfoErr
182 << "Warning: upgrading very old ':' scope syntax: \""
183 << entryName << '"' << endl;
184
185 // Make copy - cannot use stringOps::split
186 const wordList cmpts(fileName(entryName).components(':'));
187
188 entryName.clear();
189
190 bool addSep = false;
191
192 for (const word& cmpt : cmpts)
193 {
194 if (addSep) entryName += '.';
195 if (!cmpt.empty())
196 {
197 addSep = true;
198 entryName += cmpt;
199 }
200 }
201
202 return true;
203 }
204
205 // Nothing changed
206 return false;
207}
208
209
210//- Split into dictionary name and the entry name
211class dictAndKeyword
212{
213 word dict_;
214 word key_;
215
216public:
217
218 dictAndKeyword(const word& scopedName)
219 {
220 auto i = scopedName.rfind('/');
221 if (i == std::string::npos)
222 {
223 i = scopedName.rfind('.');
224 }
225
226 if (i != std::string::npos)
227 {
228 dict_ = scopedName.substr(0, i);
229 key_ = scopedName.substr(i+1);
230 }
231 else
232 {
233 key_ = scopedName;
234 }
235 }
236
237 const word& dict() const noexcept { return dict_; }
238
239 const word& key() const noexcept { return key_; }
240};
241
242
243const dictionary& lookupScopedDict
244(
245 const dictionary& dict,
246 const word& subDictName
247)
248{
249 if (subDictName.empty())
250 {
251 return dict;
252 }
253
254 const auto finder = dict.csearchScoped(subDictName, keyType::LITERAL);
255
256 if (!finder.good() || !finder.isDict())
257 {
258 // Not found or not a dictionary
260 << '"' << subDictName << '"' << nl;
261
262 if (!finder.good())
263 {
264 FatalIOError << "Not found in dictionary";
265 }
266 else
267 {
268 FatalIOError << "Not a dictionary entry";
269 }
270
272 << nl << nl
273 << "Known entries of " << finder.context().name() << " : " << nl
274 << finder.context().keys()
275 << exit(FatalIOError);
276 }
277
278 return finder.dict();
279}
280
281
282void removeDict(dictionary& dict, const dictionary& dictToRemove)
283{
284 for (const entry& refEntry : dictToRemove)
285 {
286 auto finder = dict.search(refEntry.keyword(), keyType::LITERAL);
287
288 bool purge = false;
289
290 if (finder.isDict())
291 {
292 if (refEntry.isDict())
293 {
294 removeDict(finder.dict(), refEntry.dict());
295
296 // Purge if dictionary is empty
297 purge = finder.dict().empty();
298 }
299 }
300 else if (finder.good() && !refEntry.isDict())
301 {
302 // Purge if entries match
303 purge = (finder.ref() == refEntry);
304 }
305
306 if (purge)
307 {
308 dict.remove(refEntry.keyword());
309 }
310 }
311}
312
313
314// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
315
316int main(int argc, char *argv[])
317{
319 (
320 "Interrogate and manipulate dictionaries"
321 );
322
323 argList::noBanner(); // Essential if redirecting stdout
325 argList::addArgument("dict", "The dictionary file to process");
326 argList::addBoolOption("keywords", "List keywords");
327 argList::addOption("entry", "name", "Report/select the named entry");
329 (
330 "value",
331 "Print entry value"
332 );
334 (
335 "set",
336 "value",
337 "Set entry value or add new entry"
338 );
340 (
341 "add",
342 "value",
343 "Add a new entry"
344 );
346 (
347 "remove",
348 "Remove the entry"
349 );
351 (
352 "diff",
353 "dict",
354 "Write differences with respect to the specified dictionary"
355 );
357 (
358 "diff-etc",
359 "dict",
360 "As per -diff, but locate the file as per foamEtcFile"
361 );
362 argList::addOptionCompat("diff-etc", {"diffEtc", 1712});
364 (
365 "precision",
366 "int",
367 "Set default write precision for IOstreams"
368 );
369
371 (
372 "includes",
373 "List the #include/#sinclude files to standard output"
374 );
376 (
377 "expand",
378 "Read the specified dictionary file, expand the macros etc. and write "
379 "the resulting dictionary to standard output"
380 );
382 (
383 "disableFunctionEntries",
384 "Disable expansion of dictionary directives - #include, #codeStream etc"
385 );
386 profiling::disable(); // Disable profiling (and its output)
387
388 argList args(argc, argv);
389
390 const bool listIncludes = args.found("includes");
391
392 if (listIncludes)
393 {
395 }
396
397 const bool disableEntries = args.found("disableFunctionEntries");
398 if (disableEntries)
399 {
400 // Report on stderr (once) to avoid polluting the output
401 InfoErr<< "Not expanding variables or dictionary directives" << endl;
403 }
404
405 // Set the default output precision
406 {
407 const unsigned prec = args.getOrDefault<unsigned>("precision", 0u);
408 if (prec)
409 {
410 // InfoErr<< "Output write precision set to " << prec << endl;
412 Sout.precision(prec);
413 }
414 }
415
416 const auto dictFileName = args.get<fileName>(1);
417
418 auto dictFile = autoPtr<IFstream>::New(dictFileName);
419 if (!dictFile || !dictFile().good())
420 {
422 << "Cannot open file " << dictFileName
423 << exit(FatalError, 1);
424 }
425
426
427 bool changed = false;
428
429 // Read, preserving headers
431 dictionary dict = readDictionary(dictFile());
432
433 // The extracted dictionary format
434 const auto dictFormat = dictFile().format();
435
436
437 if (listIncludes)
438 {
439 return 0;
440 }
441 else if (args.found("expand"))
442 {
444 <<"//\n// " << dictFileName << "\n//\n";
445 dict.write(Info, false);
447
448 return 0;
449 }
450
451
452 // Has "diff" or "diff-etc"
453 bool optDiff = false;
454
455 // Reference dictionary for -diff / -diff-etc
456 dictionary diffDict;
457 {
458 fileName diffFileName;
459 if (args.readIfPresent("diff", diffFileName))
460 {
461 IFstream diffFile(diffFileName);
462 if (!diffFile.good())
463 {
465 << "Cannot open file " << diffFileName
466 << exit(FatalError, 1);
467 }
468
469 // Read, preserving headers
471 diffDict = readDictionary(diffFile);
472
473 optDiff = true;
474 }
475 else if (args.readIfPresent("diff-etc", diffFileName))
476 {
477 fileName foundName = findEtcFile(diffFileName);
478 if (foundName.empty())
479 {
481 << "Cannot find etcFile " << diffFileName
482 << exit(FatalError, 1);
483 }
484
485 IFstream diffFile(foundName);
486 if (!diffFile.good())
487 {
489 << "Cannot open file " << foundName
490 << exit(FatalError, 1);
491 }
492
493 // Read, preserving headers
495 diffDict = readDictionary(diffFile);
496 optDiff = true;
497 }
498 }
499
500 word scopedName; // Actually fileName, since it can contain '/' scoping
501 if (args.readIfPresent("entry", scopedName))
502 {
503 upgradeScope(scopedName);
504
505 string newValue;
506 if
507 (
508 args.readIfPresent("set", newValue)
509 || args.readIfPresent("add", newValue)
510 )
511 {
512 const bool overwrite = args.found("set");
513
514 // Dictionary name and keyword
515 const dictAndKeyword dAk(scopedName);
516
517 // The context for the action
518 const dictionary& d(lookupScopedDict(dict, dAk.dict()));
519
520 // Create a new entry
521 IStringStream str(string(dAk.key()) + ' ' + newValue + ';');
522 entry* ePtr(entry::New(str).ptr());
523
524 if (overwrite)
525 {
526 const_cast<dictionary&>(d).set(ePtr);
527 }
528 else
529 {
530 const_cast<dictionary&>(d).add(ePtr, false);
531 }
532 changed = true;
533
534 const auto finder = dict.csearchScoped(scopedName, keyType::REGEX);
535
536 // Print the changed entry to stderr
537 if (finder.good())
538 {
539 InfoErr<< finder.ref();
540 }
541 }
542 else if (args.found("remove"))
543 {
544 // Dictionary name and keyword
545 const dictAndKeyword dAk(scopedName);
546
547 // The context for the action
548 const dictionary& d(lookupScopedDict(dict, dAk.dict()));
549
550 const_cast<dictionary&>(d).remove(dAk.key());
551 changed = true;
552 }
553 else
554 {
555 // Optionally remove a second dictionary
556 if (optDiff)
557 {
558 // Dictionary name and keyword
559 const dictAndKeyword dAk(scopedName);
560
561 const dictionary& d1(lookupScopedDict(dict, dAk.dict()));
562 const dictionary& d2(lookupScopedDict(diffDict, dAk.dict()));
563
564 const entry* e1Ptr = d1.findEntry(dAk.key(), keyType::REGEX);
565 const entry* e2Ptr = d2.findEntry(dAk.key(), keyType::REGEX);
566
567 if (e1Ptr && e2Ptr)
568 {
569 if (*e1Ptr == *e2Ptr)
570 {
571 const_cast<dictionary&>(d1).remove(dAk.key());
572 }
573 else if (e1Ptr->isDict() && e2Ptr->isDict())
574 {
575 removeDict
576 (
577 const_cast<dictionary&>(e1Ptr->dict()),
578 e2Ptr->dict()
579 );
580 }
581 }
582 }
583
584 const auto finder = dict.csearchScoped(scopedName, keyType::REGEX);
585
586 if (!finder.good())
587 {
588 FatalIOErrorInFunction(dictFile())
589 << "Cannot find entry " << scopedName
590 << exit(FatalIOError, 2);
591 }
592 else if (args.found("keywords"))
593 {
594 // Report keywords to stdout
595 for (const entry& e : finder.dict())
596 {
597 Info<< e.keyword() << endl;
598 }
599 }
600 else if (args.found("value"))
601 {
602 // Report value to stdout
603 if (finder.isDict())
604 {
605 Info<< finder.dict();
606 }
607 else if (finder.isStream())
608 {
609 bool separator = false;
610
611 for (const token& tok : finder.stream())
612 {
613 if (separator)
614 {
616 }
617 separator = true;
618 Info<< tok;
619 }
620 Info<< endl;
621 }
622 }
623 else
624 {
625 // Report entry to stdout
626 Info<< finder.ref();
627 }
628 }
629 }
630 else if (args.found("keywords"))
631 {
632 // Report keywords to stdout
633 for (const entry& e : dict)
634 {
635 Info<< e.keyword() << endl;
636 }
637 }
638 else if (optDiff)
639 {
640 // Report difference to stdout
641 removeDict(dict, diffDict);
642 dict.write(Info, false);
643 }
644 else
645 {
646 // Report dictionary to stdout
647 dict.write(Info, false);
648 }
649
650 // Close the input file
651 dictFile.reset();
652
653 if (changed)
654 {
655 OFstream os(dictFileName, dictFormat);
658 dict.write(os, false);
660 }
661
662 return 0;
663}
664
665
666// ************************************************************************* //
Input from file stream as an ISstream, normally using std::ifstream for the actual input.
Definition IFstream.H:55
static Ostream & writeDivider(Ostream &os)
Write the standard file section divider.
static Ostream & writeEndDivider(Ostream &os)
Write the standard end file divider.
static Ostream & writeBanner(Ostream &os, const bool noSyntaxHint=false)
Write the standard OpenFOAM file/dictionary banner.
streamFormat format() const noexcept
Get the current stream format.
static streamFormat formatEnum(const word &fmtName, const streamFormat deflt=streamFormat::ASCII)
Lookup streamFormat enum corresponding to the string (ascii | binary).
static unsigned int defaultPrecision() noexcept
Return the default precision.
Definition IOstream.H:437
Input from string buffer, using a ISstream. Always UNCOMPRESSED.
An Istream is an abstract base class for all input systems (streams, files, token lists etc)....
Definition Istream.H:60
virtual void rewind()=0
Rewind the stream so that it may be read again.
Definition Istream.C:214
void putBack(const token &tok)
Put back a token (copy). Only a single put back is permitted.
Definition Istream.C:71
Output to file stream as an OSstream, normally using std::ofstream for the actual output.
Definition OFstream.H:75
Extract command arguments and options from the supplied argc and argv parameters.
Definition argList.H:119
static void noBanner()
Disable emitting the banner information.
Definition argList.C:506
static void addArgument(const string &argName, const string &usage="")
Append a (mandatory) argument to validArgs.
Definition argList.C:366
static void noJobInfo()
Suppress JobInfo, overriding controlDict setting.
Definition argList.C:582
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
Definition argList.C:389
static void addOption(const word &optName, const string &param="", const string &usage="", bool advanced=false)
Add an option to validOptions with usage information.
Definition argList.C:400
static void addNote(const string &note)
Add extra notes for the usage information.
Definition argList.C:477
static void addOptionCompat(const word &optName, std::pair< const char *, int > compat)
Specify an alias for the option name.
Definition argList.C:433
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition autoPtr.H:178
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
A keyword and a list of tokens is an 'entry'.
Definition entry.H:66
virtual bool isDict() const noexcept
True if this entry is a dictionary.
Definition entry.H:284
static bool New(dictionary &parentDict, Istream &is, const inputMode inpMode=inputMode::GLOBAL, const int endChar=0)
Construct from an Istream and insert into the dictionary.
Definition entryIO.C:98
virtual const dictionary & dict() const =0
Return dictionary, if entry is a dictionary, otherwise Fatal.
static int disableFunctionEntries
Enable or disable use of function entries and variable expansions.
Definition entry.H:139
A class for handling file names.
Definition fileName.H:75
static bool log
Report to stdout which file is included.
@ LITERAL
String literal.
Definition keyType.H:82
@ REGEX
Regular expression.
Definition keyType.H:83
static void disable() noexcept
Disallow profiling - turns the InfoSwitch off.
Definition profiling.C:113
bool contains(char c) const noexcept
True if string contains given character (cf. C++23).
Definition string.H:412
A token holds an item read from Istream.
Definition token.H:70
bool isPunctuation() const noexcept
Token is PUNCTUATION.
Definition tokenI.H:650
@ BEGIN_BLOCK
Begin block [isseparator].
Definition token.H:178
@ SPACE
Space [isspace].
Definition token.H:144
bool isWord() const noexcept
Token is word-variant (WORD, DIRECTIVE).
Definition tokenI.H:1004
bool read(Istream &is)
Read a token from Istream, calls reset() first.
Definition tokenIO.C:146
A class for handling words, derived from Foam::string.
Definition word.H:66
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition error.H:629
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
Functions to search 'etc' directories for configuration files etc.
OBJstream os(runTime.globalPath()/outputName)
void set(List< bool > &bools, const labelUList &locations)
Set the listed locations (assign 'true').
Definition BitOps.C:35
constexpr auto key(const Type &t) noexcept
Helper function to return the enum value.
Namespace for OpenFOAM.
List< word > wordList
List of word.
Definition fileName.H:60
messageStream Info
Information stream (stdout output on master, null elsewhere).
fileName findEtcFile(const fileName &name, const bool mandatory=false, unsigned short location=0777)
Search for a single FILE within the etc directories.
Definition etcFiles.C:439
OSstream Sout
OSstream wrapped stdout (std::cout).
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
IOerror FatalIOError
Error stream (stdout output on all processes), with additional 'FOAM FATAL IO ERROR' header text and ...
messageStream InfoErr
Information stream (stderr output on master, null elsewhere).
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
word format(conversionProperties.get< word >("format"))
dict add("bounds", meshBb)
dictionary dict
Foam::argList args(argc, argv)
volScalarField & e