changeDictionary.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) 2011-2016 OpenFOAM Foundation
9 Copyright (C) 2016-2022 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 changeDictionary
29
30Group
31 grpPreProcessingUtilities
32
33Description
34 Utility to change dictionary entries, e.g. can be used to change the patch
35 type in the field and polyMesh/boundary files.
36
37 Reads dictionaries (fields) and entries to change from a dictionary.
38 E.g. to make the \em movingWall a \em fixedValue for \em p but all other
39 \em Walls a zeroGradient boundary condition, the
40 \c system/changeDictionaryDict would contain the following:
41 \verbatim
42 p // field to change
43 {
44 boundaryField
45 {
46 ".*Wall" // entry to change
47 {
48 type zeroGradient;
49 }
50 movingWall // entry to change
51 {
52 type fixedValue;
53 value uniform 123.45;
54 }
55 }
56 }
57 \endverbatim
58 Replacement entries starting with '~' will remove the entry.
59
60Usage
61 \b changeDictionary [OPTION]
62
63 Options:
64 - \par -subDict
65 Specify the subDict name of the replacements dictionary.
66
67 - \par -literalRE
68 Do not interpret regular expressions or patchGroups; treat them as any
69 other keyword.
70
71 - \par -enableFunctionEntries
72 Enable function entries (default: disabled)
73
74 - \par -disablePatchGroups
75 Disable the default checking for keys being patchGroups
76
77\*---------------------------------------------------------------------------*/
78
79#include "argList.H"
80#include "IOobjectList.H"
81#include "IOPtrList.H"
82#include "volFields.H"
83#include "stringListOps.H"
84#include "timeSelector.H"
85
86using namespace Foam;
87
88// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
89
90namespace Foam
91{
93}
94
95
96// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
97
98// Extract groupPatch info from boundary file info
99HashTable<wordList> extractPatchGroups(const dictionary& boundaryDict)
100{
101 HashTable<wordList> groupToPatch;
102
103 for (const entry& dEntry : boundaryDict)
104 {
105 if (!dEntry.isDict())
106 {
107 continue;
108 }
109
110 const word& patchName = dEntry.keyword();
111 const dictionary& patchDict = dEntry.dict();
112
113 wordList groupNames;
114 patchDict.readIfPresent("inGroups", groupNames);
115
116 for (const word& groupName : groupNames)
117 {
118 auto groupIter = groupToPatch.find(groupName);
119 if (groupIter.found())
120 {
121 (*groupIter).append(patchName);
122 }
123 else
124 {
125 groupToPatch.insert(groupName, wordList(one{}, patchName));
126 }
127 }
128 }
129
130 return groupToPatch;
131}
132
133
134bool merge
135(
136 const bool addNonExisting,
137 dictionary&,
138 const dictionary&,
139 const bool,
141);
142
143
144// Add thisEntry to dictionary thisDict.
145bool addEntry
146(
147 dictionary& thisDict,
148 entry& thisEntry,
149 const entry& mergeEntry,
150 const bool literalRE,
151 const HashTable<wordList>& shortcuts
152)
153{
154 bool changed = false;
155
156 // Recursively merge sub-dictionaries
157 // TODO: merge without copying
158 if (thisEntry.isDict() && mergeEntry.isDict())
159 {
160 if
161 (
162 merge
163 (
164 true,
165 const_cast<dictionary&>(thisEntry.dict()),
166 mergeEntry.dict(),
167 literalRE,
168 shortcuts
169 )
170 )
171 {
172 changed = true;
173 }
174 }
175 else
176 {
177 // Should use in-place modification instead of adding
178 thisDict.add(mergeEntry.clone(thisDict).ptr(), true);
179 changed = true;
180 }
181
182 return changed;
183}
184
185
186// List of indices into thisKeys
187labelList findMatches
188(
189 const HashTable<wordList>& shortcuts,
190 const wordList& shortcutNames,
191 const wordList& thisKeys,
192 const keyType& key
193)
194{
195 labelList matches;
196
197 if (key.isPattern())
198 {
199 // Wildcard match
200 matches = findStrings(key, thisKeys);
201 }
202 else if (shortcuts.size())
203 {
204 // See if patchGroups expand to valid thisKeys
205 labelList indices = findStrings(key, shortcutNames);
206
207 for (const label idx : indices)
208 {
209 const word& name = shortcutNames[idx];
210 const wordList& keys = shortcuts[name];
211 forAll(keys, j)
212 {
213 const label index = thisKeys.find(keys[j]);
214 if (index != -1)
215 {
216 matches.append(index);
217 }
218 }
219 }
220 }
221 return matches;
222}
223
224
225// Dictionary merging/editing.
226// literalRE:
227// - true: behave like dictionary::merge, i.e. add regexps just like
228// any other key.
229// - false : interpret wildcard as a rule for items to be matched.
230bool merge
231(
232 const bool addNonExisting,
233 dictionary& thisDict,
234 const dictionary& mergeDict,
235 const bool literalRE,
236 const HashTable<wordList>& shortcuts
237)
238{
239 const wordList shortcutNames(shortcuts.toc());
240
241 bool changed = false;
242
243 // Save current (non-wildcard) keys before adding items.
244 wordHashSet thisKeysSet;
245 {
246 for (const word& k : thisDict.keys(false))
247 {
248 thisKeysSet.insert(k);
249 }
250 }
251
252 // Pass 1. All literal matches
253
254 for (const entry& mergeEntry : mergeDict)
255 {
256 const keyType& key = mergeEntry.keyword();
257
258 if (key[0] == '~')
259 {
260 const word eraseKey = key.substr(1);
261 if (thisDict.remove(eraseKey))
262 {
263 // Mark thisDict entry as having been match for wildcard
264 // handling later on.
265 thisKeysSet.erase(eraseKey);
266 }
267 changed = true;
268 }
269 else if (literalRE || !(key.isPattern() || shortcuts.found(key)))
270 {
271 entry* eptr = thisDict.findEntry(key, keyType::LITERAL);
272
273 if (eptr)
274 {
275 // Mark thisDict entry as having been match for wildcard
276 // handling later on.
277 thisKeysSet.erase(eptr->keyword());
278
279 if
280 (
281 addEntry
282 (
283 thisDict,
284 *eptr,
285 mergeEntry,
286 literalRE,
287 shortcuts
288 )
289 )
290 {
291 changed = true;
292 }
293 }
294 else
295 {
296 if (addNonExisting)
297 {
298 // Not found - just add
299 thisDict.add(mergeEntry.clone(thisDict).ptr());
300 changed = true;
301 }
302 else
303 {
304 IOWarningInFunction(mergeDict)
305 << "Ignoring non-existing entry " << key
306 << endl;
307 }
308 }
309 }
310 }
311
312
313 // Pass 2. Wildcard or shortcut matches (if any) on any non-match keys.
314
315 if (!literalRE && thisKeysSet.size())
316 {
317 // Pick up remaining dictionary entries
318 wordList thisKeys(thisKeysSet.toc());
319
320 for (const entry& mergeEntry : mergeDict)
321 {
322 const keyType& key = mergeEntry.keyword();
323
324 if (key[0] == '~')
325 {
326 const word eraseKey = key.substr(1);
327
328 // List of indices into thisKeys
329 labelList matches
330 (
331 findMatches
332 (
333 shortcuts,
334 shortcutNames,
335 thisKeys,
336 eraseKey
337 )
338 );
339
340 // Remove all matches
341 for (const label matchi : matches)
342 {
343 const word& k = thisKeys[matchi];
344 thisKeysSet.erase(k);
345 }
346 changed = true;
347 }
348 else
349 {
350 // List of indices into thisKeys
351 labelList matches
352 (
353 findMatches
354 (
355 shortcuts,
356 shortcutNames,
357 thisKeys,
358 key
359 )
360 );
361
362 // Add all matches
363 for (const label matchi : matches)
364 {
365 const word& k = thisKeys[matchi];
366
367 entry* eptr = thisDict.findEntry(k, keyType::LITERAL);
368
369 if
370 (
371 addEntry
372 (
373 thisDict,
374 *eptr,
375 mergeEntry,
376 literalRE,
377 HashTable<wordList>(0) // no shortcuts
378 // at deeper levels
379 )
380 )
381 {
382 changed = true;
383 }
384 }
385 }
386 }
387 }
388
389 return changed;
390}
391
392
393
394int main(int argc, char *argv[])
395{
396 argList::addNote
397 (
398 "Utility to change dictionary entries"
399 " (such as the patch type for fields and polyMesh/boundary files)."
400 );
401
402 argList::addOption("dict", "file", "Alternative changeDictionaryDict");
403
404 argList::addOption
405 (
406 "subDict",
407 "name",
408 "Specify the subDict name of the replacements dictionary"
409 );
410 argList::addOption
411 (
412 "instance",
413 "name",
414 "Override instance setting (default is the time name)"
415 );
416
417 // Add explicit time option
418 timeSelector::addOptions();
419
420 argList::addBoolOption
421 (
422 "literalRE",
423 "Treat regular expressions literally (i.e., as a keyword)"
424 );
425 argList::addBoolOption
426 (
427 "enableFunctionEntries",
428 "Enable expansion of dictionary directives - #include, #codeStream etc"
429 );
430 argList::addBoolOption
431 (
432 "disablePatchGroups",
433 "Disable matching keys to patch groups"
434 );
435
436 #include "addRegionOption.H"
437
438 #include "setRootCase.H"
439 #include "createTime.H"
440
441 // Optionally override controlDict time with -time options
442 instantList times = timeSelector::selectIfPresent(runTime, args);
443 if (times.size() < 1)
444 {
446 << "No times selected." << exit(FatalError);
447 }
448 forAll(times, timei)
449 {
450 word instance;
451 if (args.readIfPresent("instance", instance))
452 {
453 if (times.size() > 1)
454 {
456 << "Multiple times selected with 'instance' option"
457 << exit(FatalError);
458 }
459 }
460 else
461 {
462 runTime.setTime(times[timei], timei);
463 instance = runTime.timeName();
464 }
465
466 #include "createNamedMesh.H"
467
468 const bool literalRE = args.found("literalRE");
469 if (literalRE)
470 {
471 Info<< "Not interpreting any regular expressions (RE)"
472 << " in the changeDictionaryDict." << endl
473 << "Instead they are handled as any other entry, i.e. added if"
474 << " not present." << endl;
475 }
476
477 const bool enableEntries = args.found("enableFunctionEntries");
478 if (enableEntries)
479 {
480 Info<< "Allowing dictionary preprocessing (#include, #codeStream)."
481 << endl;
482 }
483
484 const int oldFlag = entry::disableFunctionEntries;
485 if (!enableEntries)
486 {
487 // By default disable dictionary expansion for fields
488 entry::disableFunctionEntries = 1;
489 }
490
491
492 const bool disablePatchGroups = args.found("disablePatchGroups");
493 if (disablePatchGroups)
494 {
495 Info<< "Not interpreting any keys in the changeDictionary"
496 << " as patchGroups"
497 << endl;
498 }
499
500
501 // Make sure we do not use the master-only reading since we read
502 // fields (different per processor) as dictionaries.
503 IOobject::fileModificationChecking = IOobject::timeStamp;
504
505
506 // Get the replacement rules from a dictionary
507
508 const word dictName("changeDictionaryDict");
511
512 const dictionary* replaceDictsPtr = &dict;
513
514 if (args.found("subDict"))
515 {
516 replaceDictsPtr = &dict.subDict(args["subDict"]);
517 }
518
519 const dictionary& replaceDicts = *replaceDictsPtr;
520
521 Info<< "Read dictionary " << dict.name()
522 << " with replacements for dictionaries "
523 << replaceDicts.toc() << endl;
524
525
526
527 // Always read boundary to get patch groups
528 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
529
530 Info<< "Reading polyMesh/boundary file to extract patch names"
531 << endl;
532
533 // Read PtrList of dictionary as dictionary.
534 const word oldTypeName = IOPtrList<entry>::typeName;
536 IOPtrList<entry> dictList
537 (
539 (
540 "boundary",
541 runTime.findInstance
542 (
543 polyMesh::regionName(regionName)/polyMesh::meshSubDir,
544 "boundary",
545 IOobject::READ_IF_PRESENT
546 ),
547 polyMesh::meshSubDir,
548 mesh,
549 IOobject::READ_IF_PRESENT,
550 IOobject::NO_WRITE,
551 false
552 )
553 );
554 const_cast<word&>(IOPtrList<entry>::typeName) = oldTypeName;
555
556 // Fake type back to what was in field
557 const_cast<word&>(dictList.type()) = dictList.headerClassName();
558
559 // Temporary convert to dictionary
560 dictionary fieldDict;
561 for (const entry& e : dictList)
562 {
563 if (e.isDict())
564 {
565 fieldDict.add(e.keyword(), e.dict());
566 }
567 }
568
569 if (dictList.size())
570 {
571 Info<< "Loaded dictionary " << dictList.name()
572 << " with entries " << fieldDict.toc() << endl;
573 }
574
575 // Extract any patchGroups information (= shortcut for set of
576 // patches)
577 HashTable<wordList> patchGroups;
578 if (!disablePatchGroups)
579 {
580 patchGroups = extractPatchGroups(fieldDict);
581 if (patchGroups.size())
582 {
583 Info<< "Extracted patch groups:" << endl;
584 wordList groups(patchGroups.sortedToc());
585 forAll(groups, i)
586 {
587 Info<< " group " << groups[i] << " with patches "
588 << patchGroups[groups[i]] << endl;
589 }
590 }
591 }
592
593
594 // Every replacement is a dictionary name and a keyword in this
595
596 for (const entry& replaceEntry : replaceDicts)
597 {
598 if (!replaceEntry.isDict())
599 {
600 // Could also warn
601 continue;
602 }
603
604 const word& fieldName = replaceEntry.keyword();
605 const dictionary& replaceDict = replaceEntry.dict();
606
607 Info<< "Replacing entries in dictionary " << fieldName << endl;
608
609 // Handle 'boundary' specially:
610 // - is PtrList of dictionaries
611 // - is in polyMesh/
612 if (fieldName == "boundary")
613 {
614 Info<< "Special handling of " << fieldName
615 << " as polyMesh/boundary file." << endl;
616
617 // Merge the replacements in. Do not add non-existing entries.
618 Info<< "Merging entries from " << replaceDict.toc() << endl;
619 merge(false, fieldDict, replaceDict, literalRE, patchGroups);
620
621 Info<< "fieldDict:" << fieldDict << endl;
622
623 // Convert back into dictList
624 wordList doneKeys(dictList.size());
625
626 label nEntries = fieldDict.size();
627 nEntries = 0;
628
629 forAll(dictList, i)
630 {
631 doneKeys[i] = dictList[i].keyword();
632
633 const entry* ePtr = fieldDict.findEntry
634 (
635 doneKeys[i],
636 keyType::REGEX
637 );
638 // Check that it hasn't been removed from fieldDict
639 if (ePtr)
640 {
641 dictList.set(nEntries++, ePtr->clone());
642 fieldDict.remove(doneKeys[i]);
643 }
644 }
645
646 // Add remaining entries
647 for (const entry& e : fieldDict)
648 {
649 dictList.set(nEntries++, e.clone());
650 }
651 dictList.setSize(nEntries);
652
653 Info<< "Writing modified " << fieldName << endl;
654 dictList.writeObject
655 (
656 IOstreamOption(runTime.writeFormat()),
657 true
658 );
659 }
660 else
661 {
662 // Read dictionary
663 // Note: disable class type checking so we can load field
664 Info<< "Loading dictionary " << fieldName << endl;
665 const word oldTypeName = IOdictionary::typeName;
666 const_cast<word&>(IOdictionary::typeName) = word::null;
667
668 IOobject fieldHeader
669 (
670 fieldName,
671 instance,
672 mesh,
673 IOobject::MUST_READ_IF_MODIFIED,
674 IOobject::NO_WRITE,
675 false
676 );
677
678 if (fieldHeader.typeHeaderOk<IOdictionary>(false))
679 {
680 IOdictionary fieldDict(fieldHeader);
681
682 const_cast<word&>(IOdictionary::typeName) = oldTypeName;
683
684 // Fake type back to what was in field
685 const_cast<word&>(fieldDict.type()) =
686 fieldDict.headerClassName();
687
688 Info<< "Loaded dictionary " << fieldName
689 << " with entries " << fieldDict.toc() << endl;
690
691 // Merge the replacements in (allow adding)
692 Info<< "Merging entries from " << replaceDict.toc() << endl;
693 merge(true, fieldDict, replaceDict, literalRE, patchGroups);
694
695 Info<< "Writing modified fieldDict " << fieldName << endl;
696 fieldDict.regIOobject::write();
697 }
698 else
699 {
701 << "Requested field to change " << fieldName
702 << " does not exist in " << fieldHeader.path() << endl;
703 }
704 }
705
706 entry::disableFunctionEntries = oldFlag;
707 }
708 }
709
710 Info<< "\nEnd\n" << endl;
711
712 return 0;
713}
714
715
716// ************************************************************************* //
label k
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition: HashSet.H:191
A HashTable similar to std::unordered_map.
Definition: HashTable.H:123
List< Key > sortedToc() const
The table of contents (the keys) in sorted order.
Definition: HashTable.C:137
List< Key > toc() const
The table of contents (the keys) in unsorted order.
Definition: HashTable.C:122
bool found(const Key &key) const
Return true if hashed entry is found in table.
Definition: HashTableI.H:100
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:180
label size() const noexcept
The number of elements in table.
Definition: HashTableI.H:52
iterator find(const Key &key)
Find and return an iterator set at the hashed entry.
Definition: HashTableI.H:114
bool erase(const iterator &iter)
Erase an entry specified by given iterator.
Definition: HashTable.C:440
A PtrList of objects of type <T> with automated input and output.
Definition: IOPtrList.H:57
IOdictionary is derived from dictionary and IOobject to give the dictionary automatic IO functionalit...
Definition: IOdictionary.H:57
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:170
The IOstreamOption is a simple container for options an IOstream can normally have.
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:175
label find(const T &val, label pos=0) const
Find index of the first occurrence of the value.
Definition: UList.C:212
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
bool found(const word &optName) const
Return true if the named option is found.
Definition: argListI.H:178
bool readIfPresent(const word &optName, T &val) const
Read a value from the named option if present.
Definition: argListI.H:323
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
entry * findEntry(const word &keyword, enum keyType::option matchOpt=keyType::REGEX)
Find for an entry (non-const access) with the given keyword.
Definition: dictionaryI.H:97
bool remove(const word &keyword)
Remove an entry specified by keyword.
List< keyType > keys(bool patterns=false) const
Return the list of available keys or patterns.
Definition: dictionary.C:622
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
entry * add(entry *entryPtr, bool mergeEntry=false)
Add a new entry.
Definition: dictionary.C:640
wordList toc() const
Return the table of contents.
Definition: dictionary.C:602
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:70
virtual bool isDict() const noexcept
Return true if this entry is a dictionary.
Definition: entry.H:233
virtual autoPtr< entry > clone(const dictionary &parentDict) const =0
Construct on freestore as copy with reference to the.
const keyType & keyword() const noexcept
Return keyword.
Definition: entry.H:195
virtual const dictionary & dict() const =0
Return dictionary, if entry is a dictionary.
A class for handling keywords in dictionaries.
Definition: keyType.H:71
A class representing the concept of 1 (one) that can be used to avoid manipulating objects known to b...
Definition: one.H:62
A class for handling words, derived from Foam::string.
Definition: word.H:68
#define defineTemplateTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information for templates, useful.
Definition: className.H:132
dynamicFvMesh & mesh
engineTime & runTime
Foam::word regionName(Foam::polyMesh::defaultRegion)
Required Variables.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
const word dictName("faMeshDefinition")
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
#define WarningInFunction
Report a warning using Foam::Warning.
auto key(const Type &t) -> typename std::enable_if< std::is_enum< Type >::value, typename std::underlying_type< Type >::type >::type
Definition: foamGltfBase.H:108
Namespace for OpenFOAM.
messageStream Info
Information stream (stdout output on master, null elsewhere)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
labelList findStrings(const regExp &matcher, const UList< StringType > &input, const bool invert=false)
Return list indices for strings matching the regular expression.
Definition: stringListOps.H:87
dictionary dict
IOobject dictIO
Foam::argList args(argc, argv)
volScalarField & e
Definition: createFields.H:11
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333
Operations on lists of strings.