diff --git a/applications/utilities/preProcessing/changeDictionary/changeDictionary.C b/applications/utilities/preProcessing/changeDictionary/changeDictionary.C index e62f361c4..1d5211ba7 100644 --- a/applications/utilities/preProcessing/changeDictionary/changeDictionary.C +++ b/applications/utilities/preProcessing/changeDictionary/changeDictionary.C @@ -29,10 +29,10 @@ Description 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 + 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 dictionaryReplacement { p // field to change @@ -51,15 +51,22 @@ Description } } } - @endverbatim + \endverbatim + Replacement entries starting with '~' will remove the entry. Usage - changeDictionary [OPTION] - @param -literalRE \n - Do not interpret regular expressions; treat them as any other keyword. + \param -literalRE \n + Do not interpret regular expressions or patchGroups; + treat them as any other keyword. + \param -enableFunctionEntries \n + By default all dictionary preprocessing of fields is disabled + + \param -disablePatchGroups \n + By default all keys are also checked for being patchGroups \*---------------------------------------------------------------------------*/ @@ -68,6 +75,7 @@ Usage #include "IOPtrList.H" #include "volFields.H" #include "stringListOps.H" +#include "timeSelector.H" using namespace Foam; @@ -81,7 +89,47 @@ namespace Foam // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -bool merge(dictionary&, const dictionary&, const bool); +// Extract groupPatch (= shortcut) info from boundary file info +HashTable extractPatchGroups(const dictionary& boundaryDict) +{ + HashTable groupToPatch; + + forAllConstIter(dictionary, boundaryDict, iter) + { + const word& patchName = iter().keyword(); + const dictionary& patchDict = iter().dict(); + + wordList groups; + if (patchDict.readIfPresent("inGroups", groups)) + { + forAll(groups, i) + { + HashTable::iterator fndGroup = groupToPatch.find + ( + groups[i] + ); + if (fndGroup == groupToPatch.end()) + { + groupToPatch.insert(groups[i], wordList(1, patchName)); + } + else + { + fndGroup().append(patchName); + } + } + } + } + return groupToPatch; +} + + +bool merge +( + dictionary&, + const dictionary&, + const bool, + const HashTable& +); // Add thisEntry to dictionary thisDict. @@ -90,7 +138,8 @@ bool addEntry dictionary& thisDict, entry& thisEntry, const entry& mergeEntry, - const bool literalRE + const bool literalRE, + const HashTable& shortcuts ) { bool changed = false; @@ -105,7 +154,8 @@ bool addEntry ( const_cast(thisEntry.dict()), mergeEntry.dict(), - literalRE + literalRE, + shortcuts ) ) { @@ -123,6 +173,46 @@ bool addEntry } + +// List of indices into thisKeys +labelList findMatches +( + const HashTable& shortcuts, + 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); + forAll(indices, i) + { + const word& name = shortcutNames[indices[i]]; + const wordList& keys = shortcuts[name]; + forAll(keys, j) + { + label index = findIndex(thisKeys, keys[j]); + if (index != -1) + { + matches.append(index); + } + } + } + } + return matches; +} + + // Dictionary merging/editing. // literalRE: // - true: behave like dictionary::merge, i.e. add regexps just like @@ -132,10 +222,11 @@ bool merge ( dictionary& thisDict, const dictionary& mergeDict, - const bool literalRE + const bool literalRE, + const HashTable& shortcuts ) { - bool wildCardInMergeDict = false; + const wordList shortcutNames(shortcuts.toc()); bool changed = false; @@ -155,7 +246,18 @@ bool merge { const keyType& key = mergeIter().keyword(); - if (literalRE || !key.isPattern()) + if (key[0] == '~') + { + word eraseKey = key(1, key.size()-1); + 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))) { entry* entryPtr = thisDict.lookupEntryPtr ( @@ -178,7 +280,8 @@ bool merge thisDict, *entryPtr, mergeIter(), - literalRE + literalRE, + shortcuts ) ) { @@ -195,42 +298,63 @@ bool merge } - // Pass 2. Wildcard matches (if any) on any non-match keys. + // Pass 2. Wildcard or shortcut matches (if any) on any non-match keys. if (!literalRE && thisKeysSet.size() > 0) { + // Pick up remaining dictionary entries wordList thisKeys(thisKeysSet.toc()); forAllConstIter(IDLList, mergeDict, mergeIter) { const keyType& key = mergeIter().keyword(); - if (key.isPattern()) + if (key[0] == '~') { - // Find all matching entries in the original thisDict + word eraseKey = key(1, key.size()-1); - if (!wildCardInMergeDict) - { - wildCardInMergeDict = true; - WarningIn("changeDictionary()") - << "Detected wildcard " << key - << " in changeDictionaryDict" << endl - << "The behaviour of wildcards has changed -" - << " they are now interpreted by changeDictionary." - << endl << "Please take care or use the -literalRE" - << " command line option to revert to" - << " previous behaviour." << endl; - } - - labelList matches = findStrings(key, thisKeys); + // List of indices into thisKeys + labelList matches + ( + findMatches + ( + shortcuts, + shortcutNames, + thisKeys, + eraseKey + ) + ); + // Remove all matches forAll(matches, i) { - label matchI = matches[i]; + const word& thisKey = thisKeys[matches[i]]; + thisKeysSet.erase(thisKey); + } + changed = true; + } + else + { + // List of indices into thisKeys + labelList matches + ( + findMatches + ( + shortcuts, + shortcutNames, + thisKeys, + key + ) + ); + + // Add all matches + forAll(matches, i) + { + const word& thisKey = thisKeys[matches[i]]; entry& thisEntry = const_cast ( - thisDict.lookupEntry(thisKeys[matchI], false, false) + thisDict.lookupEntry(thisKey, false, false) ); if @@ -240,7 +364,9 @@ bool merge thisDict, thisEntry, mergeIter(), - literalRE + literalRE, + HashTable(0) // no shortcuts + // at deeper levels ) ) { @@ -255,20 +381,54 @@ bool merge } -// Main program: int main(int argc, char *argv[]) { - argList::validOptions.insert("instance", "instance"); - argList::validOptions.insert("literalRE", ""); + #include "addDictOption.H" + argList::addOption + ( + "instance", + "name", + "override instance setting (default is the time name)" + ); + + // Add explicit time option + timeSelector::addOptions(); + + argList::addBoolOption + ( + "literalRE", + "treat regular expressions literally (i.e., as a keyword)" + ); + argList::addBoolOption + ( + "enableFunctionEntries", + "enable expansion of dictionary directives - #include, #codeStream etc" + ); + argList::addBoolOption + ( + "disablePatchGroups", + "disable matching keys to patch groups" + ); + #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) + { + FatalErrorIn(args.executable()) + << "No times selected." << exit(FatalError); + } + runTime.setTime(times[0], 0); + word instance = args.optionLookupOrDefault("instance", runTime.timeName()); + #include "createNamedMesh.H" - bool literalRE = args.optionFound("literalRE"); - + const bool literalRE = args.optionFound("literalRE"); if (literalRE) { Info<< "Not interpreting any regular expressions (RE)" @@ -277,6 +437,29 @@ int main(int argc, char *argv[]) << " not present." << endl; } + const bool enableEntries = args.optionFound("enableFunctionEntries"); + if (enableEntries) + { + Info<< "Allowing dictionary preprocessing ('#include', '#codeStream')." + << endl; + } + + int oldFlag = entry::disableFunctionEntries(); + if (!enableEntries) + { + // By default disable dictionary expansion for fields + entry::disableFunctionEntries = 1; + } + + + const bool disablePatchGroups = args.optionFound("disablePatchGroups"); + if (disablePatchGroups) + { + Info<< "Not interpreting any keys in the changeDictionary" + << " as patchGroups" + << endl; + } + fileName regionPrefix = ""; if (regionName != fvMesh::defaultRegion) @@ -284,30 +467,88 @@ int main(int argc, char *argv[]) regionPrefix = regionName; } - word instance = runTime.timeName(); - if (args.options().found("instance")) - { - instance = args.options()["instance"]; - } + + // Make sure we do not use the master-only reading since we read + // fields (different per processor) as dictionaries. + regIOobject::fileModificationChecking = regIOobject::timeStamp; + // Get the replacement rules from a dictionary - IOdictionary dict - ( - IOobject - ( - "changeDictionaryDict", - runTime.system(), - mesh, - IOobject::MUST_READ, - IOobject::NO_WRITE - ) - ); + + const word dictName("changeDictionaryDict"); + #include "setSystemMeshDictionaryIO.H" + IOdictionary dict(dictIO); + const dictionary& replaceDicts = dict.subDict("dictionaryReplacement"); Info<< "Read dictionary " << dict.name() << " with replacements for dictionaries " << replaceDicts.toc() << endl; + + // Always read boundary to get patch groups + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Info<< "Reading polyMesh/boundary file to extract patch names" + << endl; + + // Read PtrList of dictionary as dictionary. + const word oldTypeName = IOPtrList::typeName; + const_cast(IOPtrList::typeName) = word::null; + IOPtrList dictList + ( + IOobject + ( + "boundary", + runTime.findInstance + ( + regionPrefix/polyMesh::meshSubDir, + "boundary", + IOobject::READ_IF_PRESENT + ), + polyMesh::meshSubDir, + mesh, + IOobject::READ_IF_PRESENT, + IOobject::NO_WRITE, + false + ) + ); + const_cast(IOPtrList::typeName) = oldTypeName; + // Fake type back to what was in field + const_cast(dictList.type()) = dictList.headerClassName(); + + // Temporary convert to dictionary + dictionary fieldDict; + forAll(dictList, i) + { + fieldDict.add(dictList[i].keyword(), dictList[i].dict()); + } + + if (dictList.size()) + { + Info<< "Loaded dictionary " << dictList.name() + << " with entries " << fieldDict.toc() << endl; + } + + // Extract any patchGroups information (= shortcut for set of + // patches) + HashTable patchGroups; + if (!disablePatchGroups) + { + patchGroups = extractPatchGroups(fieldDict); + if (patchGroups.size()) + { + Info<< "Extracted patch groups:" << endl; + wordList groups(patchGroups.sortedToc()); + forAll(groups, i) + { + Info<< " group " << groups[i] << " with patches " + << patchGroups[groups[i]] << endl; + } + } + } + + // Every replacement is a dictionary name and a keyword in this forAllConstIter(dictionary, replaceDicts, fieldIter) @@ -323,46 +564,12 @@ int main(int argc, char *argv[]) Info<< "Special handling of " << fieldName << " as polyMesh/boundary file." << endl; - // Read PtrList of dictionary as dictionary. - const word oldTypeName = IOPtrList::typeName; - const_cast(IOPtrList::typeName) = word::null; - IOPtrList dictList - ( - IOobject - ( - fieldName, - runTime.findInstance - ( - regionPrefix/polyMesh::meshSubDir, - fieldName - ), - polyMesh::meshSubDir, - mesh, - IOobject::MUST_READ, - IOobject::NO_WRITE, - false - ) - ); - const_cast(IOPtrList::typeName) = oldTypeName; - // Fake type back to what was in field - const_cast(dictList.type()) = dictList.headerClassName(); - - // Temporary convert to dictionary - dictionary fieldDict; - forAll(dictList, i) - { - fieldDict.add(dictList[i].keyword(), dictList[i].dict()); - } - - Info<< "Loaded dictionary " << fieldName - << " with entries " << fieldDict.toc() << endl; - // Get the replacement dictionary for the field const dictionary& replaceDict = fieldIter().dict(); Info<< "Merging entries from " << replaceDict.toc() << endl; // Merge the replacements in - merge(fieldDict, replaceDict, literalRE); + merge(fieldDict, replaceDict, literalRE, patchGroups); Info<< "fieldDict:" << fieldDict << endl; @@ -370,6 +577,7 @@ int main(int argc, char *argv[]) wordList doneKeys(dictList.size()); label nEntries = fieldDict.size(); + forAll(dictList, i) { doneKeys[i] = dictList[i].keyword(); @@ -385,16 +593,22 @@ int main(int argc, char *argv[]) ); fieldDict.remove(doneKeys[i]); } + // Add remaining entries label sz = dictList.size(); dictList.setSize(nEntries); forAllConstIter(dictionary, fieldDict, iter) { - dictList.set(sz, iter().clone()); + dictList.set(sz++, iter().clone()); } - Info<< "Writing modified fieldDict " << fieldName << endl; - dictList.write(); + Info<< "Writing modified " << fieldName << endl; + dictList.writeObject + ( + runTime.writeFormat(), + runTime.writeFormat(), + IOstream::UNCOMPRESSED + ); } else { @@ -411,11 +625,12 @@ int main(int argc, char *argv[]) fieldName, instance, mesh, - IOobject::MUST_READ, + IOobject::MUST_READ_IF_MODIFIED, IOobject::NO_WRITE, false ) ); + const_cast(IOdictionary::typeName) = oldTypeName; // Fake type back to what was in field const_cast(fieldDict.type()) = fieldDict.headerClassName(); @@ -428,13 +643,15 @@ int main(int argc, char *argv[]) Info<< "Merging entries from " << replaceDict.toc() << endl; // Merge the replacements in - merge(fieldDict, replaceDict, literalRE); + merge(fieldDict, replaceDict, literalRE, patchGroups); Info<< "Writing modified fieldDict " << fieldName << endl; fieldDict.regIOobject::write(); } } + entry::disableFunctionEntries = oldFlag; + Info<< endl; Info<< "End\n" << endl; diff --git a/applications/utilities/preProcessing/changeDictionary/changeDictionaryDict b/applications/utilities/preProcessing/changeDictionary/changeDictionaryDict index 9b8f1848b..46ecaef29 100644 --- a/applications/utilities/preProcessing/changeDictionary/changeDictionaryDict +++ b/applications/utilities/preProcessing/changeDictionary/changeDictionaryDict @@ -20,7 +20,7 @@ dictionaryReplacement { ".*" { - type directMappedPatch; + type mappedPatch; } } @@ -86,5 +86,4 @@ dictionaryReplacement } } - // ************************************************************************* // diff --git a/src/foam/include/addDictOption.H b/src/foam/include/addDictOption.H new file mode 100644 index 000000000..f538e5729 --- /dev/null +++ b/src/foam/include/addDictOption.H @@ -0,0 +1,10 @@ +// +// addDictOption.H +// ~~~~~~~~~~~~~~~~~ + + Foam::argList::addOption + ( + "dict", + "file", + "read control dictionary from specified location" + ); diff --git a/src/foam/include/setSystemMeshDictionaryIO.H b/src/foam/include/setSystemMeshDictionaryIO.H new file mode 100644 index 000000000..6e89f94c3 --- /dev/null +++ b/src/foam/include/setSystemMeshDictionaryIO.H @@ -0,0 +1,33 @@ +// +// setSystemMeshDictionaryIO.H +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + fileName dictPath = ""; + if (args.optionFound("dict")) + { + dictPath = args["dict"]; + if (isDir(dictPath)) + { + dictPath = dictPath / dictName; + } + } + + IOobject dictIO + ( + dictName, + runTime.system(), + mesh, + IOobject::MUST_READ_IF_MODIFIED, + IOobject::NO_WRITE + ); + + if (dictPath.size()) + { + dictIO = IOobject + ( + dictPath, + mesh, + IOobject::MUST_READ_IF_MODIFIED, + IOobject::NO_WRITE + ); + }