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