changeDictionary.C 20.3 KB
Newer Older
Henry's avatar
Henry committed
1
2
3
4
/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
5
    \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
6
     \\/     M anipulation  | Copyright (C) 2016-2019 OpenCFD Ltd.
Henry's avatar
Henry committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-------------------------------------------------------------------------------
License
    This file is part of OpenFOAM.

    OpenFOAM is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.

    You should have received a copy of the GNU General Public License
    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.

Application
    changeDictionary

27
28
29
Group
    grpPreProcessingUtilities

Henry's avatar
Henry committed
30
31
32
33
34
35
36
37
38
Description
    Utility to change dictionary entries, e.g. can be used to change the patch
    type in the field and polyMesh/boundary files.

    Reads dictionaries (fields) and entries to change from a dictionary.
    E.g. to make the \em movingWall a \em fixedValue for \em p but all other
    \em Walls a zeroGradient boundary condition, the
    \c system/changeDictionaryDict would contain the following:
    \verbatim
39
    p                           // field to change
Henry's avatar
Henry committed
40
    {
41
        boundaryField
Henry's avatar
Henry committed
42
        {
43
            ".*Wall"            // entry to change
Henry's avatar
Henry committed
44
            {
45
46
47
48
49
50
                type            zeroGradient;
            }
            movingWall          // entry to change
            {
                type            fixedValue;
                value           uniform 123.45;
Henry's avatar
Henry committed
51
52
53
54
55
56
57
            }
        }
    }
    \endverbatim
    Replacement entries starting with '~' will remove the entry.

Usage
58
    \b changeDictionary [OPTION]
Henry's avatar
Henry committed
59

60
61
62
    Options:
      - \par -subDict
        Specify the subDict name of the replacements dictionary.
Henry's avatar
Henry committed
63

64
65
66
      - \par -literalRE
        Do not interpret regular expressions or patchGroups; treat them as any
        other keyword.
Henry's avatar
Henry committed
67

68
69
      - \par -enableFunctionEntries
        Enable function entries (default: disabled)
Henry's avatar
Henry committed
70

71
72
      - \par -disablePatchGroups
        Disable the default checking for keys being patchGroups
Henry's avatar
Henry committed
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

\*---------------------------------------------------------------------------*/

#include "argList.H"
#include "IOobjectList.H"
#include "IOPtrList.H"
#include "volFields.H"
#include "stringListOps.H"
#include "timeSelector.H"

using namespace Foam;

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

namespace Foam
{
    defineTemplateTypeNameAndDebug(IOPtrList<entry>, 0);
}


// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

95
// Extract groupPatch info from boundary file info
96
HashTable<wordList> extractPatchGroups(const dictionary& boundaryDict)
Henry's avatar
Henry committed
97
{
98
    HashTable<wordList> groupToPatch;
Henry's avatar
Henry committed
99

100
    for (const entry& dEntry : boundaryDict)
Henry's avatar
Henry committed
101
    {
102
103
104
105
106
        if (!dEntry.isDict())
        {
            continue;
        }

107
108
        const word& patchName = dEntry.keyword();
        const dictionary& patchDict = dEntry.dict();
Henry's avatar
Henry committed
109

110
111
112
113
        wordList groupNames;
        patchDict.readIfPresent("inGroups", groupNames);

        for (const word& groupName : groupNames)
Henry's avatar
Henry committed
114
        {
115
116
117
118
119
120
            auto groupIter = groupToPatch.find(groupName);
            if (groupIter.found())
            {
                (*groupIter).append(patchName);
            }
            else
Henry's avatar
Henry committed
121
            {
122
                groupToPatch.insert(groupName, wordList(one(), patchName));
Henry's avatar
Henry committed
123
124
125
            }
        }
    }
126

Henry's avatar
Henry committed
127
128
129
130
131
132
    return groupToPatch;
}


bool merge
(
133
    const bool addNonExisting,
Henry's avatar
Henry committed
134
135
136
    dictionary&,
    const dictionary&,
    const bool,
137
    const HashTable<wordList>&
Henry's avatar
Henry committed
138
139
140
141
142
143
144
145
146
147
);


// Add thisEntry to dictionary thisDict.
bool addEntry
(
    dictionary& thisDict,
    entry& thisEntry,
    const entry& mergeEntry,
    const bool literalRE,
148
    const HashTable<wordList>& shortcuts
Henry's avatar
Henry committed
149
150
151
152
153
154
155
156
157
158
159
160
)
{
    bool changed = false;

    // Recursively merge sub-dictionaries
    // TODO: merge without copying
    if (thisEntry.isDict() && mergeEntry.isDict())
    {
        if
        (
            merge
            (
161
                true,
Henry's avatar
Henry committed
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
                const_cast<dictionary&>(thisEntry.dict()),
                mergeEntry.dict(),
                literalRE,
                shortcuts
            )
        )
        {
            changed = true;
        }
    }
    else
    {
        // Should use in-place modification instead of adding
        thisDict.add(mergeEntry.clone(thisDict).ptr(), true);
        changed = true;
    }

    return changed;
}


// List of indices into thisKeys
labelList findMatches
(
186
    const HashTable<wordList>& shortcuts,
Henry's avatar
Henry committed
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
    const wordList& shortcutNames,
    const wordList& thisKeys,
    const keyType& key
)
{
    labelList matches;

    if (key.isPattern())
    {
        // Wildcard match
        matches = findStrings(key, thisKeys);
    }
    else if (shortcuts.size())
    {
        // See if patchGroups expand to valid thisKeys
        labelList indices = findStrings(key, shortcutNames);
203
204

        for (const label idx : indices)
Henry's avatar
Henry committed
205
        {
206
            const word& name = shortcutNames[idx];
Henry's avatar
Henry committed
207
208
209
            const wordList& keys = shortcuts[name];
            forAll(keys, j)
            {
210
                const label index = thisKeys.find(keys[j]);
Henry's avatar
Henry committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
                if (index != -1)
                {
                    matches.append(index);
                }
            }
        }
    }
    return matches;
}


// Dictionary merging/editing.
// literalRE:
// - true: behave like dictionary::merge, i.e. add regexps just like
//   any other key.
// - false : interpret wildcard as a rule for items to be matched.
bool merge
(
229
    const bool addNonExisting,
Henry's avatar
Henry committed
230
231
232
    dictionary& thisDict,
    const dictionary& mergeDict,
    const bool literalRE,
233
    const HashTable<wordList>& shortcuts
Henry's avatar
Henry committed
234
235
236
237
238
239
240
)
{
    const wordList shortcutNames(shortcuts.toc());

    bool changed = false;

    // Save current (non-wildcard) keys before adding items.
241
    wordHashSet thisKeysSet;
Henry's avatar
Henry committed
242
    {
243
        for (const word& k : thisDict.keys(false))
Henry's avatar
Henry committed
244
        {
245
            thisKeysSet.insert(k);
Henry's avatar
Henry committed
246
247
248
249
250
        }
    }

    // Pass 1. All literal matches

251
    for (const entry& mergeEntry : mergeDict)
Henry's avatar
Henry committed
252
    {
253
        const keyType& key = mergeEntry.keyword();
Henry's avatar
Henry committed
254
255
256

        if (key[0] == '~')
        {
257
            const word eraseKey = key.substr(1);
Henry's avatar
Henry committed
258
259
260
261
262
263
264
265
266
267
            if (thisDict.remove(eraseKey))
            {
                // Mark thisDict entry as having been match for wildcard
                // handling later on.
                thisKeysSet.erase(eraseKey);
            }
            changed = true;
        }
        else if (literalRE || !(key.isPattern() || shortcuts.found(key)))
        {
268
            entry* eptr = thisDict.findEntry(key, keyType::LITERAL);
Henry's avatar
Henry committed
269

270
            if (eptr)
Henry's avatar
Henry committed
271
272
273
            {
                // Mark thisDict entry as having been match for wildcard
                // handling later on.
274
                thisKeysSet.erase(eptr->keyword());
Henry's avatar
Henry committed
275
276
277
278
279
280

                if
                (
                    addEntry
                    (
                        thisDict,
281
                       *eptr,
282
                        mergeEntry,
Henry's avatar
Henry committed
283
284
285
286
287
288
289
290
291
292
                        literalRE,
                        shortcuts
                    )
                )
                {
                    changed = true;
                }
            }
            else
            {
293
294
                if (addNonExisting)
                {
295
296
                    // Not found - just add
                    thisDict.add(mergeEntry.clone(thisDict).ptr());
297
298
299
300
301
302
303
304
                    changed = true;
                }
                else
                {
                    IOWarningInFunction(mergeDict)
                        << "Ignoring non-existing entry " << key
                        << endl;
                }
Henry's avatar
Henry committed
305
306
307
308
309
310
311
            }
        }
    }


    // Pass 2. Wildcard or shortcut matches (if any) on any non-match keys.

312
    if (!literalRE && thisKeysSet.size())
Henry's avatar
Henry committed
313
314
315
316
    {
        // Pick up remaining dictionary entries
        wordList thisKeys(thisKeysSet.toc());

317
        for (const entry& mergeEntry : mergeDict)
Henry's avatar
Henry committed
318
        {
319
            const keyType& key = mergeEntry.keyword();
Henry's avatar
Henry committed
320
321
322

            if (key[0] == '~')
            {
323
                const word eraseKey = key.substr(1);
Henry's avatar
Henry committed
324
325
326
327
328
329
330
331
332
333
334
335
336
337

                // List of indices into thisKeys
                labelList matches
                (
                    findMatches
                    (
                        shortcuts,
                        shortcutNames,
                        thisKeys,
                        eraseKey
                    )
                );

                // Remove all matches
338
                for (const label matchi : matches)
Henry's avatar
Henry committed
339
                {
340
341
                    const word& k = thisKeys[matchi];
                    thisKeysSet.erase(k);
Henry's avatar
Henry committed
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
                }
                changed = true;
            }
            else
            {
                // List of indices into thisKeys
                labelList matches
                (
                    findMatches
                    (
                        shortcuts,
                        shortcutNames,
                        thisKeys,
                        key
                    )
                );

                // Add all matches
360
                for (const label matchi : matches)
Henry's avatar
Henry committed
361
                {
362
                    const word& k = thisKeys[matchi];
Henry's avatar
Henry committed
363

364
                    entry* eptr = thisDict.findEntry(k, keyType::LITERAL);
Henry's avatar
Henry committed
365
366
367
368
369
370

                    if
                    (
                        addEntry
                        (
                            thisDict,
371
                           *eptr,
372
                            mergeEntry,
Henry's avatar
Henry committed
373
                            literalRE,
374
375
                            HashTable<wordList>(0)    // no shortcuts
                                                      // at deeper levels
Henry's avatar
Henry committed
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
                        )
                    )
                    {
                        changed = true;
                    }
                }
            }
        }
    }

    return changed;
}



int main(int argc, char *argv[])
{
393
394
395
396
397
398
    argList::addNote
    (
        "Utility to change dictionary entries"
        " (such as the patch type for fields and polyMesh/boundary files)."
    );

399
400
    argList::addOption("dict", "file", "Use alternative changeDictionaryDict");

Henry's avatar
Henry committed
401
    argList::addOption
402
403
404
    (
        "subDict",
        "name",
405
        "Specify the subDict name of the replacements dictionary"
406
407
    );
    argList::addOption
Henry's avatar
Henry committed
408
409
410
    (
        "instance",
        "name",
411
        "Override instance setting (default is the time name)"
Henry's avatar
Henry committed
412
413
414
415
416
417
418
419
    );

    // Add explicit time option
    timeSelector::addOptions();

    argList::addBoolOption
    (
        "literalRE",
420
        "Treat regular expressions literally (i.e., as a keyword)"
Henry's avatar
Henry committed
421
422
423
424
    );
    argList::addBoolOption
    (
        "enableFunctionEntries",
425
        "Enable expansion of dictionary directives - #include, #codeStream etc"
Henry's avatar
Henry committed
426
427
428
429
    );
    argList::addBoolOption
    (
        "disablePatchGroups",
430
        "Disable matching keys to patch groups"
Henry's avatar
Henry committed
431
432
433
434
435
436
437
438
439
440
441
    );

    #include "addRegionOption.H"

    #include "setRootCase.H"
    #include "createTime.H"

    // Optionally override controlDict time with -time options
    instantList times = timeSelector::selectIfPresent(runTime, args);
    if (times.size() < 1)
    {
442
        FatalErrorInFunction
Henry's avatar
Henry committed
443
444
            << "No times selected." << exit(FatalError);
    }
445
446
447
    forAll(times, timei)
    {
        word instance;
448
        if (args.readIfPresent("instance", instance))
449
450
451
452
453
454
455
456
457
458
459
460
461
        {
            if (times.size() > 1)
            {
                FatalErrorInFunction
                    << "Multiple times selected with 'instance' option"
                    << exit(FatalError);
            }
        }
        else
        {
            runTime.setTime(times[timei], timei);
            instance = runTime.timeName();
        }
Henry's avatar
Henry committed
462

463
        #include "createNamedMesh.H"
Henry's avatar
Henry committed
464

465
        const bool literalRE = args.found("literalRE");
466
467
468
469
470
471
472
        if (literalRE)
        {
            Info<< "Not interpreting any regular expressions (RE)"
                << " in the changeDictionaryDict." << endl
                << "Instead they are handled as any other entry, i.e. added if"
                << " not present." << endl;
        }
Henry's avatar
Henry committed
473

474
        const bool enableEntries = args.found("enableFunctionEntries");
475
476
        if (enableEntries)
        {
477
            Info<< "Allowing dictionary preprocessing (#include, #codeStream)."
478
479
                << endl;
        }
Henry's avatar
Henry committed
480

481
482
483
484
485
486
        const int oldFlag = entry::disableFunctionEntries;
        if (!enableEntries)
        {
            // By default disable dictionary expansion for fields
            entry::disableFunctionEntries = 1;
        }
Henry's avatar
Henry committed
487
488


489
        const bool disablePatchGroups = args.found("disablePatchGroups");
490
491
492
493
494
495
        if (disablePatchGroups)
        {
            Info<< "Not interpreting any keys in the changeDictionary"
                << " as patchGroups"
                << endl;
        }
Henry's avatar
Henry committed
496
497


498
        fileName regionPrefix;
499
500
501
502
        if (regionName != fvMesh::defaultRegion)
        {
            regionPrefix = regionName;
        }
Henry's avatar
Henry committed
503
504


505
506
507
        // Make sure we do not use the master-only reading since we read
        // fields (different per processor) as dictionaries.
        regIOobject::fileModificationChecking = regIOobject::timeStamp;
Henry's avatar
Henry committed
508
509


510
        // Get the replacement rules from a dictionary
Henry's avatar
Henry committed
511

512
513
514
        const word dictName("changeDictionaryDict");
        #include "setSystemMeshDictionaryIO.H"
        IOdictionary dict(dictIO);
515

516
        const dictionary* replaceDictsPtr = &dict;
517

518
        if (args.found("subDict"))
519
        {
520
            replaceDictsPtr = &dict.subDict(args["subDict"]);
521
        }
522

523
        const dictionary& replaceDicts = *replaceDictsPtr;
Henry's avatar
Henry committed
524

525
526
527
        Info<< "Read dictionary " << dict.name()
            << " with replacements for dictionaries "
            << replaceDicts.toc() << endl;
Henry's avatar
Henry committed
528
529
530



531
532
        // Always read boundary to get patch groups
        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Henry's avatar
Henry committed
533

534
535
536
537
538
539
540
        Info<< "Reading polyMesh/boundary file to extract patch names"
            << endl;

        // Read PtrList of dictionary as dictionary.
        const word oldTypeName = IOPtrList<entry>::typeName;
        const_cast<word&>(IOPtrList<entry>::typeName) = word::null;
        IOPtrList<entry> dictList
Henry's avatar
Henry committed
541
        (
542
            IOobject
Henry's avatar
Henry committed
543
544
            (
                "boundary",
545
546
547
548
549
550
551
552
553
554
555
556
557
558
                runTime.findInstance
                (
                    regionPrefix/polyMesh::meshSubDir,
                    "boundary",
                    IOobject::READ_IF_PRESENT
                ),
                polyMesh::meshSubDir,
                mesh,
                IOobject::READ_IF_PRESENT,
                IOobject::NO_WRITE,
                false
            )
        );
        const_cast<word&>(IOPtrList<entry>::typeName) = oldTypeName;
559

560
561
        // Fake type back to what was in field
        const_cast<word&>(dictList.type()) = dictList.headerClassName();
Henry's avatar
Henry committed
562

563
564
        // Temporary convert to dictionary
        dictionary fieldDict;
565
        for (const entry& e : dictList)
566
        {
567
568
569
570
            if (e.isDict())
            {
                fieldDict.add(e.keyword(), e.dict());
            }
571
        }
Henry's avatar
Henry committed
572

573
574
575
576
577
        if (dictList.size())
        {
            Info<< "Loaded dictionary " << dictList.name()
                << " with entries " << fieldDict.toc() << endl;
        }
Henry's avatar
Henry committed
578

579
580
        // Extract any patchGroups information (= shortcut for set of
        // patches)
581
        HashTable<wordList> patchGroups;
582
        if (!disablePatchGroups)
Henry's avatar
Henry committed
583
        {
584
585
            patchGroups = extractPatchGroups(fieldDict);
            if (patchGroups.size())
Henry's avatar
Henry committed
586
            {
587
588
589
590
591
592
593
                Info<< "Extracted patch groups:" << endl;
                wordList groups(patchGroups.sortedToc());
                forAll(groups, i)
                {
                    Info<< "    group " << groups[i] << " with patches "
                        << patchGroups[groups[i]] << endl;
                }
Henry's avatar
Henry committed
594
595
596
597
            }
        }


598
        // Every replacement is a dictionary name and a keyword in this
Henry's avatar
Henry committed
599

600
        for (const entry& replaceEntry : replaceDicts)
Henry's avatar
Henry committed
601
        {
602
603
604
605
606
607
            if (!replaceEntry.isDict())
            {
                // Could also warn
                continue;
            }

608
609
610
            const word& fieldName = replaceEntry.keyword();
            const dictionary& replaceDict = replaceEntry.dict();

611
            Info<< "Replacing entries in dictionary " << fieldName << endl;
Henry's avatar
Henry committed
612

613
614
615
616
617
618
619
            // Handle 'boundary' specially:
            // - is PtrList of dictionaries
            // - is in polyMesh/
            if (fieldName == "boundary")
            {
                Info<< "Special handling of " << fieldName
                    << " as polyMesh/boundary file." << endl;
Henry's avatar
Henry committed
620

621
                // Merge the replacements in. Do not add non-existing entries.
622
                Info<< "Merging entries from " << replaceDict.toc() << endl;
623
                merge(false, fieldDict, replaceDict, literalRE, patchGroups);
Henry's avatar
Henry committed
624

625
                Info<< "fieldDict:" << fieldDict << endl;
Henry's avatar
Henry committed
626

627
628
                // Convert back into dictList
                wordList doneKeys(dictList.size());
Henry's avatar
Henry committed
629

630
                label nEntries = fieldDict.size();
631
                nEntries = 0;
632
633
634
635

                forAll(dictList, i)
                {
                    doneKeys[i] = dictList[i].keyword();
636
637

                    const entry* ePtr = fieldDict.findEntry
Henry's avatar
Henry committed
638
                    (
639
640
                        doneKeys[i],
                        keyType::REGEX
641
                    );
642
643
644
645
646
647
                    // Check that it hasn't been removed from fieldDict
                    if (ePtr)
                    {
                        dictList.set(nEntries++, ePtr->clone());
                        fieldDict.remove(doneKeys[i]);
                    }
648
649
650
                }

                // Add remaining entries
651
                for (const entry& e : fieldDict)
652
                {
653
                    dictList.set(nEntries++, e.clone());
654
                }
655
                dictList.setSize(nEntries);
656
657
658
659
660

                Info<< "Writing modified " << fieldName << endl;
                dictList.writeObject
                (
                    runTime.writeFormat(),
661
                    IOstream::currentVersion,
662
663
                    IOstream::UNCOMPRESSED,
                    true
Henry's avatar
Henry committed
664
665
                );
            }
666
            else
Henry's avatar
Henry committed
667
            {
668
669
670
671
672
                // Read dictionary
                // Note: disable class type checking so we can load field
                Info<< "Loading dictionary " << fieldName << endl;
                const word oldTypeName = IOdictionary::typeName;
                const_cast<word&>(IOdictionary::typeName) = word::null;
Henry's avatar
Henry committed
673

674
675
676
677
678
679
680
681
682
                IOobject fieldHeader
                (
                    fieldName,
                    instance,
                    mesh,
                    IOobject::MUST_READ_IF_MODIFIED,
                    IOobject::NO_WRITE,
                    false
                );
Henry's avatar
Henry committed
683

684
685
686
                if (fieldHeader.typeHeaderOk<IOdictionary>(false))
                {
                    IOdictionary fieldDict(fieldHeader);
Henry's avatar
Henry committed
687

688
                    const_cast<word&>(IOdictionary::typeName) = oldTypeName;
Henry's avatar
Henry committed
689

690
691
692
                    // Fake type back to what was in field
                    const_cast<word&>(fieldDict.type()) =
                        fieldDict.headerClassName();
Henry's avatar
Henry committed
693

694
695
                    Info<< "Loaded dictionary " << fieldName
                        << " with entries " << fieldDict.toc() << endl;
696

697
                    // Merge the replacements in (allow adding)
698
                    Info<< "Merging entries from " << replaceDict.toc() << endl;
699
                    merge(true, fieldDict, replaceDict, literalRE, patchGroups);
700

701
702
703
704
705
706
707
708
709
                    Info<< "Writing modified fieldDict " << fieldName << endl;
                    fieldDict.regIOobject::write();
                }
                else
                {
                    WarningInFunction
                        << "Requested field to change " << fieldName
                        << " does not exist in " << fieldHeader.path() << endl;
                }
710
            }
711
712

            entry::disableFunctionEntries = oldFlag;
Henry's avatar
Henry committed
713
714
715
        }
    }

716
    Info<< "\nEnd\n" << endl;
Henry's avatar
Henry committed
717
718
719
720
721
722

    return 0;
}


// ************************************************************************* //