View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0002396 | OpenFOAM | Bug | public | 2016-12-15 12:41 | 2018-04-20 21:27 |
Reporter | MattijsJ | Assigned To | henry | ||
Priority | normal | Severity | minor | Reproducibility | always |
Status | resolved | Resolution | fixed | ||
Platform | GNU/Linux | OS | OpenSuSE | OS Version | 13.2 |
Product Version | dev | ||||
Fixed in Version | dev | ||||
Summary | 0002396: dictionary scoped look up too forgiving? | ||||
Description | Following dictionary: someDict { subDict { myKey myValue; } } geometry { $:someDict2.subDict; // note: someDict missspelled } in v3/v4 it complains that there is no 'someDict2' entry at the top level in dev it does not complain but just takes it for a non-existing entry called 'someDict2.subDict'. This is because in dev we extended the lookup at any level to include words with a '.' (to e.g. handle key 'motorBike.stl'). Hmm. Any suggestions? | ||||
Tags | No tags attached. | ||||
|
I didn't take a proper look at the implementation that was done, but I had gotten the impression that names with dots were only valid if they were in quotes. The other possibility is to go the same way that regular expressions do their job: a dot is a dot only when escaped with a backslash, e.g.: $:someDict.subDict $:someDict.motorbike\.stl |
|
Quotes makes dictionary entries wildcards which are stored completely separate and have separate matching rules. Backslash: it is pretty exceptional that we're trying to use '.' in a word used for looking up things in a dictionary so a special interpretation of the keyword is not a big problem. Attached dictionary.C that implements it: foamDictionary ./snappyHexMeshDict -entry 'geometry.motorBike\.obj' Feel free to test & break it. dictionary.C (27,670 bytes)
/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- 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/>. \*---------------------------------------------------------------------------*/ #include "dictionary.H" #include "primitiveEntry.H" #include "dictionaryEntry.H" #include "regExp.H" #include "OSHA1stream.H" #include "DynamicList.H" /* * * * * * * * * * * * * * * Static Member Data * * * * * * * * * * * * * */ namespace Foam { defineTypeNameAndDebug(dictionary, 0); const dictionary dictionary::null; bool dictionary::writeOptionalEntries ( debug::infoSwitch("writeOptionalEntries", 0) ); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // const Foam::entry* Foam::dictionary::lookupScopedSubEntryPtr ( const word& keyword, bool recursive, bool patternMatch ) const { if (keyword[0] == '.') { // Starting with a '.'. Go up for every 2nd '.' found const dictionary* dictPtr = this; string::size_type begVar = 1; string::const_iterator iter = keyword.begin() + begVar; string::size_type endVar = begVar; while (iter != keyword.end() && *iter == '.') { ++iter; ++endVar; // Go to parent if (&dictPtr->parent_ == &dictionary::null) { FatalIOErrorInFunction ( *this ) << "No parent of current dictionary" << " when searching for " << keyword.substr(begVar, keyword.size()-begVar) << exit(FatalIOError); } dictPtr = &dictPtr->parent_; } return dictPtr->lookupScopedSubEntryPtr ( keyword.substr(endVar), false, patternMatch ); } else { string::size_type dotPos = string::npos; word firstWord(keyword); string::size_type filteredi = 0; for (string::size_type i = 0; i < keyword.size(); ++i) { if (keyword[i] == '\\' && i < keyword.size()-1) { // Copy any character following \\, even if '.' ++i; firstWord[filteredi++] = keyword[i]; } else if (keyword[i] == '.') { dotPos = i; break; } else { firstWord[filteredi++] = keyword[i]; } } firstWord.resize(filteredi); if (dotPos == string::npos) { // Non-scoped lookup return lookupEntryPtr(firstWord, recursive, patternMatch); } else { // Look up the first word const entry* entPtr = lookupScopedSubEntryPtr ( firstWord, false, //recursive patternMatch ); if (!entPtr) { FatalIOErrorInFunction ( *this ) << "keyword " << firstWord << " is undefined in dictionary " << name() << endl << "Valid keywords are " << keys() << exit(FatalIOError); } if (entPtr->isDict()) { return entPtr->dict().lookupScopedSubEntryPtr ( keyword.substr(dotPos, keyword.size()-dotPos), false, patternMatch ); } else { return nullptr; } } } } bool Foam::dictionary::findInPatterns ( const bool patternMatch, const word& Keyword, DLList<entry*>::const_iterator& wcLink, DLList<autoPtr<regExp>>::const_iterator& reLink ) const { if (patternEntries_.size()) { while (wcLink != patternEntries_.end()) { if ( patternMatch ? reLink()->match(Keyword) : wcLink()->keyword() == Keyword ) { return true; } ++reLink; ++wcLink; } } return false; } bool Foam::dictionary::findInPatterns ( const bool patternMatch, const word& Keyword, DLList<entry*>::iterator& wcLink, DLList<autoPtr<regExp>>::iterator& reLink ) { if (patternEntries_.size()) { while (wcLink != patternEntries_.end()) { if ( patternMatch ? reLink()->match(Keyword) : wcLink()->keyword() == Keyword ) { return true; } ++reLink; ++wcLink; } } return false; } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::dictionary::dictionary() : parent_(dictionary::null) {} Foam::dictionary::dictionary(const fileName& name) : dictionaryName(name), parent_(dictionary::null) {} Foam::dictionary::dictionary ( const dictionary& parentDict, const dictionary& dict ) : dictionaryName(dict.name()), IDLList<entry>(dict, *this), parent_(parentDict) { forAllIter(IDLList<entry>, *this, iter) { hashedEntries_.insert(iter().keyword(), &iter()); if (iter().keyword().isPattern()) { patternEntries_.insert(&iter()); patternRegexps_.insert ( autoPtr<regExp>(new regExp(iter().keyword())) ); } } } Foam::dictionary::dictionary ( const dictionary& dict ) : dictionaryName(dict.name()), IDLList<entry>(dict, *this), parent_(dictionary::null) { forAllIter(IDLList<entry>, *this, iter) { hashedEntries_.insert(iter().keyword(), &iter()); if (iter().keyword().isPattern()) { patternEntries_.insert(&iter()); patternRegexps_.insert ( autoPtr<regExp>(new regExp(iter().keyword())) ); } } } Foam::dictionary::dictionary ( const dictionary* dictPtr ) : parent_(dictionary::null) { if (dictPtr) { operator=(*dictPtr); } } Foam::dictionary::dictionary ( const dictionary& parentDict, const Xfer<dictionary>& dict ) : parent_(parentDict) { transfer(dict()); name() = parentDict.name() + '.' + name(); } Foam::dictionary::dictionary ( const Xfer<dictionary>& dict ) : parent_(dictionary::null) { transfer(dict()); } Foam::autoPtr<Foam::dictionary> Foam::dictionary::clone() const { return autoPtr<dictionary>(new dictionary(*this)); } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::dictionary::~dictionary() { // cerr<< "~dictionary() " << name() << " " << long(this) << std::endl; } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // const Foam::dictionary& Foam::dictionary::topDict() const { const dictionary& p = parent(); if (&p != this && !p.name().empty()) { return p.topDict(); } else { return *this; } } Foam::label Foam::dictionary::startLineNumber() const { if (size()) { return first()->startLineNumber(); } else { return -1; } } Foam::label Foam::dictionary::endLineNumber() const { if (size()) { return last()->endLineNumber(); } else { return -1; } } Foam::SHA1Digest Foam::dictionary::digest() const { OSHA1stream os; // Process entries forAllConstIter(IDLList<entry>, *this, iter) { os << *iter; } return os.digest(); } Foam::tokenList Foam::dictionary::tokens() const { // Serialize dictionary into a string OStringStream os; write(os, false); IStringStream is(os.str()); // Parse string as tokens DynamicList<token> tokens; token t; while (is.read(t)) { tokens.append(t); } return tokenList(tokens.xfer()); } bool Foam::dictionary::found ( const word& keyword, bool recursive, bool patternMatch ) const { if (hashedEntries_.found(keyword)) { return true; } else { if (patternMatch && patternEntries_.size()) { DLList<entry*>::const_iterator wcLink = patternEntries_.begin(); DLList<autoPtr<regExp>>::const_iterator reLink = patternRegexps_.begin(); // Find in patterns using regular expressions only if (findInPatterns(patternMatch, keyword, wcLink, reLink)) { return true; } } if (recursive && &parent_ != &dictionary::null) { return parent_.found(keyword, recursive, patternMatch); } else { return false; } } } const Foam::entry* Foam::dictionary::lookupEntryPtr ( const word& keyword, bool recursive, bool patternMatch ) const { HashTable<entry*>::const_iterator iter = hashedEntries_.find(keyword); if (iter == hashedEntries_.end()) { if (patternMatch && patternEntries_.size()) { DLList<entry*>::const_iterator wcLink = patternEntries_.begin(); DLList<autoPtr<regExp>>::const_iterator reLink = patternRegexps_.begin(); // Find in patterns using regular expressions only if (findInPatterns(patternMatch, keyword, wcLink, reLink)) { return wcLink(); } } if (recursive && &parent_ != &dictionary::null) { return parent_.lookupEntryPtr(keyword, recursive, patternMatch); } else { return nullptr; } } return iter(); } Foam::entry* Foam::dictionary::lookupEntryPtr ( const word& keyword, bool recursive, bool patternMatch ) { HashTable<entry*>::iterator iter = hashedEntries_.find(keyword); if (iter == hashedEntries_.end()) { if (patternMatch && patternEntries_.size()) { DLList<entry*>::iterator wcLink = patternEntries_.begin(); DLList<autoPtr<regExp>>::iterator reLink = patternRegexps_.begin(); // Find in patterns using regular expressions only if (findInPatterns(patternMatch, keyword, wcLink, reLink)) { return wcLink(); } } if (recursive && &parent_ != &dictionary::null) { return const_cast<dictionary&>(parent_).lookupEntryPtr ( keyword, recursive, patternMatch ); } else { return nullptr; } } return iter(); } const Foam::entry& Foam::dictionary::lookupEntry ( const word& keyword, bool recursive, bool patternMatch ) const { const entry* entryPtr = lookupEntryPtr(keyword, recursive, patternMatch); if (entryPtr == nullptr) { FatalIOErrorInFunction ( *this ) << "keyword " << keyword << " is undefined in dictionary " << name() << exit(FatalIOError); } return *entryPtr; } Foam::ITstream& Foam::dictionary::lookup ( const word& keyword, bool recursive, bool patternMatch ) const { return lookupEntry(keyword, recursive, patternMatch).stream(); } const Foam::entry* Foam::dictionary::lookupScopedEntryPtr ( const word& keyword, bool recursive, bool patternMatch ) const { if (keyword[0] == ':') { // Go up to top level const dictionary* dictPtr = this; while (&dictPtr->parent_ != &dictionary::null) { dictPtr = &dictPtr->parent_; } // At top. Recurse to find entries return dictPtr->lookupScopedSubEntryPtr ( keyword.substr(1, keyword.size()-1), false, patternMatch ); } else { return lookupScopedSubEntryPtr ( keyword, recursive, patternMatch ); } } bool Foam::dictionary::substituteScopedKeyword(const word& keyword) { word varName = keyword(1, keyword.size()-1); // Lookup the variable name in the given dictionary const entry* ePtr = lookupScopedEntryPtr(varName, true, true); // If defined insert its entries into this dictionary if (ePtr != nullptr) { const dictionary& addDict = ePtr->dict(); forAllConstIter(IDLList<entry>, addDict, iter) { add(iter()); } return true; } return false; } bool Foam::dictionary::isDict(const word& keyword) const { // Find non-recursive with patterns const entry* entryPtr = lookupEntryPtr(keyword, false, true); if (entryPtr) { return entryPtr->isDict(); } else { return false; } } const Foam::dictionary* Foam::dictionary::subDictPtr(const word& keyword) const { const entry* entryPtr = lookupEntryPtr(keyword, false, true); if (entryPtr) { return &entryPtr->dict(); } else { return nullptr; } } Foam::dictionary* Foam::dictionary::subDictPtr(const word& keyword) { entry* entryPtr = lookupEntryPtr(keyword, false, true); if (entryPtr) { return &entryPtr->dict(); } else { return nullptr; } } const Foam::dictionary& Foam::dictionary::subDict(const word& keyword) const { const entry* entryPtr = lookupEntryPtr(keyword, false, true); if (entryPtr == nullptr) { FatalIOErrorInFunction ( *this ) << "keyword " << keyword << " is undefined in dictionary " << name() << exit(FatalIOError); } return entryPtr->dict(); } Foam::dictionary& Foam::dictionary::subDict(const word& keyword) { entry* entryPtr = lookupEntryPtr(keyword, false, true); if (entryPtr == nullptr) { FatalIOErrorInFunction ( *this ) << "keyword " << keyword << " is undefined in dictionary " << name() << exit(FatalIOError); } return entryPtr->dict(); } Foam::dictionary Foam::dictionary::subOrEmptyDict ( const word& keyword, const bool mustRead ) const { const entry* entryPtr = lookupEntryPtr(keyword, false, true); if (entryPtr == nullptr) { if (mustRead) { FatalIOErrorInFunction ( *this ) << "keyword " << keyword << " is undefined in dictionary " << name() << exit(FatalIOError); return entryPtr->dict(); } else { return dictionary(*this, dictionary(name() + '.' + keyword)); } } else { return entryPtr->dict(); } } Foam::wordList Foam::dictionary::toc() const { wordList keys(size()); label nKeys = 0; forAllConstIter(IDLList<entry>, *this, iter) { keys[nKeys++] = iter().keyword(); } return keys; } Foam::wordList Foam::dictionary::sortedToc() const { return hashedEntries_.sortedToc(); } Foam::List<Foam::keyType> Foam::dictionary::keys(bool patterns) const { List<keyType> keys(size()); label nKeys = 0; forAllConstIter(IDLList<entry>, *this, iter) { if (iter().keyword().isPattern() ? patterns : !patterns) { keys[nKeys++] = iter().keyword(); } } keys.setSize(nKeys); return keys; } bool Foam::dictionary::add(entry* entryPtr, bool mergeEntry) { HashTable<entry*>::iterator iter = hashedEntries_.find ( entryPtr->keyword() ); if (mergeEntry && iter != hashedEntries_.end()) { // Merge dictionary with dictionary if (iter()->isDict() && entryPtr->isDict()) { iter()->dict().merge(entryPtr->dict()); delete entryPtr; return true; } else { // Replace existing dictionary with entry or vice versa IDLList<entry>::replace(iter(), entryPtr); delete iter(); hashedEntries_.erase(iter); if (hashedEntries_.insert(entryPtr->keyword(), entryPtr)) { entryPtr->name() = name() + '.' + entryPtr->keyword(); if (entryPtr->keyword().isPattern()) { patternEntries_.insert(entryPtr); patternRegexps_.insert ( autoPtr<regExp>(new regExp(entryPtr->keyword())) ); } return true; } else { IOWarningInFunction((*this)) << "problem replacing entry "<< entryPtr->keyword() << " in dictionary " << name() << endl; IDLList<entry>::remove(entryPtr); delete entryPtr; return false; } } } if (hashedEntries_.insert(entryPtr->keyword(), entryPtr)) { entryPtr->name() = name() + '.' + entryPtr->keyword(); IDLList<entry>::append(entryPtr); if (entryPtr->keyword().isPattern()) { patternEntries_.insert(entryPtr); patternRegexps_.insert ( autoPtr<regExp>(new regExp(entryPtr->keyword())) ); } return true; } else { IOWarningInFunction((*this)) << "attempt to add entry "<< entryPtr->keyword() << " which already exists in dictionary " << name() << endl; delete entryPtr; return false; } } void Foam::dictionary::add(const entry& e, bool mergeEntry) { add(e.clone(*this).ptr(), mergeEntry); } void Foam::dictionary::add(const keyType& k, const word& w, bool overwrite) { add(new primitiveEntry(k, token(w)), overwrite); } void Foam::dictionary::add ( const keyType& k, const Foam::string& s, bool overwrite ) { add(new primitiveEntry(k, token(s)), overwrite); } void Foam::dictionary::add(const keyType& k, const label l, bool overwrite) { add(new primitiveEntry(k, token(l)), overwrite); } void Foam::dictionary::add(const keyType& k, const scalar s, bool overwrite) { add(new primitiveEntry(k, token(s)), overwrite); } void Foam::dictionary::add ( const keyType& k, const dictionary& d, bool mergeEntry ) { add(new dictionaryEntry(k, *this, d), mergeEntry); } void Foam::dictionary::set(entry* entryPtr) { entry* existingPtr = lookupEntryPtr(entryPtr->keyword(), false, true); // Clear dictionary so merge acts like overwrite if (existingPtr && existingPtr->isDict()) { existingPtr->dict().clear(); } add(entryPtr, true); } void Foam::dictionary::set(const entry& e) { set(e.clone(*this).ptr()); } void Foam::dictionary::set(const keyType& k, const dictionary& d) { set(new dictionaryEntry(k, *this, d)); } bool Foam::dictionary::remove(const word& Keyword) { HashTable<entry*>::iterator iter = hashedEntries_.find(Keyword); if (iter != hashedEntries_.end()) { // Delete from patterns first DLList<entry*>::iterator wcLink = patternEntries_.begin(); DLList<autoPtr<regExp>>::iterator reLink = patternRegexps_.begin(); // Find in pattern using exact match only if (findInPatterns(false, Keyword, wcLink, reLink)) { patternEntries_.remove(wcLink); patternRegexps_.remove(reLink); } IDLList<entry>::remove(iter()); delete iter(); hashedEntries_.erase(iter); return true; } else { return false; } } bool Foam::dictionary::changeKeyword ( const keyType& oldKeyword, const keyType& newKeyword, bool forceOverwrite ) { // No change if (oldKeyword == newKeyword) { return false; } HashTable<entry*>::iterator iter = hashedEntries_.find(oldKeyword); // oldKeyword not found - do nothing if (iter == hashedEntries_.end()) { return false; } if (iter()->keyword().isPattern()) { FatalIOErrorInFunction ( *this ) << "Old keyword "<< oldKeyword << " is a pattern." << "Pattern replacement not yet implemented." << exit(FatalIOError); } HashTable<entry*>::iterator iter2 = hashedEntries_.find(newKeyword); // newKeyword already exists if (iter2 != hashedEntries_.end()) { if (forceOverwrite) { if (iter2()->keyword().isPattern()) { // Delete from patterns first DLList<entry*>::iterator wcLink = patternEntries_.begin(); DLList<autoPtr<regExp>>::iterator reLink = patternRegexps_.begin(); // Find in patterns using exact match only if (findInPatterns(false, iter2()->keyword(), wcLink, reLink)) { patternEntries_.remove(wcLink); patternRegexps_.remove(reLink); } } IDLList<entry>::replace(iter2(), iter()); delete iter2(); hashedEntries_.erase(iter2); } else { IOWarningInFunction ( *this ) << "cannot rename keyword "<< oldKeyword << " to existing keyword " << newKeyword << " in dictionary " << name() << endl; return false; } } // Change name and HashTable, but leave DL-List untouched iter()->keyword() = newKeyword; iter()->name() = name() + '.' + newKeyword; hashedEntries_.erase(oldKeyword); hashedEntries_.insert(newKeyword, iter()); if (newKeyword.isPattern()) { patternEntries_.insert(iter()); patternRegexps_.insert ( autoPtr<regExp>(new regExp(newKeyword)) ); } return true; } bool Foam::dictionary::merge(const dictionary& dict) { // Check for assignment to self if (this == &dict) { FatalIOErrorInFunction(*this) << "attempted merge to self for dictionary " << name() << abort(FatalIOError); } bool changed = false; forAllConstIter(IDLList<entry>, dict, iter) { HashTable<entry*>::iterator fnd = hashedEntries_.find(iter().keyword()); if (fnd != hashedEntries_.end()) { // Recursively merge sub-dictionaries // TODO: merge without copying if (fnd()->isDict() && iter().isDict()) { if (fnd()->dict().merge(iter().dict())) { changed = true; } } else { add(iter().clone(*this).ptr(), true); changed = true; } } else { // Not found - just add add(iter().clone(*this).ptr()); changed = true; } } return changed; } void Foam::dictionary::clear() { IDLList<entry>::clear(); hashedEntries_.clear(); patternEntries_.clear(); patternRegexps_.clear(); } void Foam::dictionary::transfer(dictionary& dict) { // Changing parents probably doesn't make much sense, // but what about the names? name() = dict.name(); IDLList<entry>::transfer(dict); hashedEntries_.transfer(dict.hashedEntries_); patternEntries_.transfer(dict.patternEntries_); patternRegexps_.transfer(dict.patternRegexps_); } Foam::Xfer<Foam::dictionary> Foam::dictionary::xfer() { return xferMove(*this); } // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * // Foam::ITstream& Foam::dictionary::operator[](const word& keyword) const { return lookup(keyword); } void Foam::dictionary::operator=(const dictionary& rhs) { // Check for assignment to self if (this == &rhs) { FatalIOErrorInFunction(*this) << "attempted assignment to self for dictionary " << name() << abort(FatalIOError); } name() = rhs.name(); clear(); // Create clones of the entries in the given dictionary // resetting the parentDict to this dictionary forAllConstIter(IDLList<entry>, rhs, iter) { add(iter().clone(*this).ptr()); } } void Foam::dictionary::operator+=(const dictionary& rhs) { // Check for assignment to self if (this == &rhs) { FatalIOErrorInFunction(*this) << "attempted addition assignment to self for dictionary " << name() << abort(FatalIOError); } forAllConstIter(IDLList<entry>, rhs, iter) { add(iter().clone(*this).ptr()); } } void Foam::dictionary::operator|=(const dictionary& rhs) { // Check for assignment to self if (this == &rhs) { FatalIOErrorInFunction(*this) << "attempted assignment to self for dictionary " << name() << abort(FatalIOError); } forAllConstIter(IDLList<entry>, rhs, iter) { if (!found(iter().keyword())) { add(iter().clone(*this).ptr()); } } } void Foam::dictionary::operator<<=(const dictionary& rhs) { // Check for assignment to self if (this == &rhs) { FatalIOErrorInFunction(*this) << "attempted assignment to self for dictionary " << name() << abort(FatalIOError); } forAllConstIter(IDLList<entry>, rhs, iter) { set(iter().clone(*this).ptr()); } } /* * * * * * * * * * * * * * * * Global operators * * * * * * * * * * * * * */ Foam::dictionary Foam::operator+ ( const dictionary& dict1, const dictionary& dict2 ) { dictionary sum(dict1); sum += dict2; return sum; } Foam::dictionary Foam::operator| ( const dictionary& dict1, const dictionary& dict2 ) { dictionary sum(dict1); sum |= dict2; return sum; } // ************************************************************************* // |
|
With that version recursive substitution still works as long as the word includes the '\': motorBike.obj 10; var motorBike\.obj; b ${$var}; produces b 10; |
|
The real problem is that motorBike.obj should never be a keyword. Syntax in snappyHexMeshDict, surfaceFeatureExtractDict, that uses the filename as a keyword is inconsistent. motorBike.obj { type triSurfaceMesh; name motorBike; } should be motorBike { type triSurfaceMesh; name motorBike.obj; // or filename motorBike.obj } The filename should not be one level higher than the type because the need for a filename is a consequence of the type being triSurfaceMesh. Same with surfaceFeatureExtract, where the consequences for the user are much worse because of the need to have a separate dictionary for every surface. motorBike.obj { extractionMethod extractFromSurface; ... Here, the dictionary name could be the method, and the dictionary include an entry for all surfaces, e.g. extractFromSurface { surfaces (motorBike.obj anotherSurface.obj); ... |
|
triSurfaceMesh.H (9,113 bytes)
/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- 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/>. Class Foam::triSurfaceMesh Description IOoject and searching on triSurface Note: when constructing from dictionary has optional parameters: - scale : scaling factor. - tolerance : relative tolerance for doing intersections (see triangle::intersection) - minQuality: discard triangles with low quality when getting normal SourceFiles triSurfaceMesh.C \*---------------------------------------------------------------------------*/ #ifndef triSurfaceMesh_H #define triSurfaceMesh_H #include "treeBoundBox.H" #include "searchableSurface.H" #include "objectRegistry.H" #include "indexedOctree.H" #include "treeDataTriSurface.H" #include "treeDataPrimitivePatch.H" #include "treeDataEdge.H" #include "EdgeMap.H" #include "triSurface.H" #include "triSurfaceRegionSearch.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { /*---------------------------------------------------------------------------*\ Class triSurfaceMesh Declaration \*---------------------------------------------------------------------------*/ class triSurfaceMesh : public searchableSurface, public objectRegistry, // so we can store fields public triSurface, public triSurfaceRegionSearch { private: // Private member data //- Supplied fileName override fileName fName_; //- Optional min triangle quality. Triangles below this get // ignored for normal calculation scalar minQuality_; //- Search tree for boundary edges. mutable autoPtr<indexedOctree<treeDataEdge>> edgeTree_; //- Names of regions mutable wordList regions_; //- Is surface closed mutable label surfaceClosed_; // Private Member Functions ////- Helper: find instance of files without header //static word findRawInstance //( // const Time&, // const fileName&, // const word& //); //- Return fileName to load IOobject from static fileName checkFile(const IOobject& io); //- Return fileName to load IOobject from. Optional override of fileName static fileName checkFile(const IOobject&, const dictionary&); //- Helper function for isSurfaceClosed static bool addFaceToEdge ( const edge&, EdgeMap<label>& ); //- Check whether surface is closed without calculating any permanent // addressing. bool isSurfaceClosed() const; //- Steps to next intersection. Adds smallVec and starts tracking // from there. static void getNextIntersections ( const indexedOctree<treeDataTriSurface>& octree, const point& start, const point& end, const vector& smallVec, DynamicList<pointIndexHit, 1, 1>& hits ); //- Disallow default bitwise copy construct triSurfaceMesh(const triSurfaceMesh&); //- Disallow default bitwise assignment void operator=(const triSurfaceMesh&); public: //- Runtime type information TypeName("triSurfaceMesh"); // Constructors //- Construct from triSurface triSurfaceMesh(const IOobject&, const triSurface&); //- Construct read. triSurfaceMesh(const IOobject& io); //- Construct from IO and dictionary (used by searchableSurface). // Dictionary may contain a 'scale' entry (eg, 0.001: mm -> m) triSurfaceMesh ( const IOobject& io, const dictionary& dict ); //- Destructor virtual ~triSurfaceMesh(); //- Clear storage void clearOut(); // Member Functions //- Move points virtual void movePoints(const pointField&); //- Demand driven construction of octree for boundary edges const indexedOctree<treeDataEdge>& edgeTree() const; // searchableSurface implementation virtual const wordList& regions() const; //- Whether supports volume type below. I.e. whether is closed. virtual bool hasVolumeType() const; //- Range of local indices that can be returned. virtual label size() const { return triSurface::size(); } //- Get representative set of element coordinates // Usually the element centres (should be of length size()). virtual tmp<pointField> coordinates() const; //- Get bounding spheres (centre and radius squared). Any point // on surface is guaranteed to be inside. virtual void boundingSpheres ( pointField& centres, scalarField& radiusSqr ) const; //- Get the points that define the surface. virtual tmp<pointField> points() const; // Does any part of the surface overlap the supplied bound box? virtual bool overlaps(const boundBox& bb) const; virtual void findNearest ( const pointField& sample, const scalarField& nearestDistSqr, List<pointIndexHit>& ) const; virtual void findNearest ( const pointField& sample, const scalarField& nearestDistSqr, const labelList& regionIndices, List<pointIndexHit>& ) const; virtual void findLine ( const pointField& start, const pointField& end, List<pointIndexHit>& ) const; virtual void findLineAny ( const pointField& start, const pointField& end, List<pointIndexHit>& ) const; //- Get all intersections in order from start to end. virtual void findLineAll ( const pointField& start, const pointField& end, List<List<pointIndexHit>>& ) const; //- From a set of points and indices get the region virtual void getRegion ( const List<pointIndexHit>&, labelList& region ) const; //- From a set of points and indices get the normal virtual void getNormal ( const List<pointIndexHit>&, vectorField& normal ) const; //- Determine type (inside/outside/mixed) for point. unknown if // cannot be determined (e.g. non-manifold surface) virtual void getVolumeType ( const pointField&, List<volumeType>& ) const; // Other //- WIP. Store element-wise field. virtual void setField(const labelList& values); //- WIP. From a set of hits (points and // indices) get the specified field. Misses do not get set. virtual void getField(const List<pointIndexHit>&, labelList&) const; // regIOobject implementation bool writeData(Ostream&) const { NotImplemented; return false; } //- Write using given format, version and compression virtual bool writeObject ( IOstream::streamFormat fmt, IOstream::versionNumber ver, IOstream::compressionType cmp ) const; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // #endif // ************************************************************************* // |
|
Attached triSurfaceMesh[CH] to accept motorBike { type triSurfaceMesh; fileName "motorBike.obj"; } triSurfaceMesh-2.H (9,113 bytes)
/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- 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/>. Class Foam::triSurfaceMesh Description IOoject and searching on triSurface Note: when constructing from dictionary has optional parameters: - scale : scaling factor. - tolerance : relative tolerance for doing intersections (see triangle::intersection) - minQuality: discard triangles with low quality when getting normal SourceFiles triSurfaceMesh.C \*---------------------------------------------------------------------------*/ #ifndef triSurfaceMesh_H #define triSurfaceMesh_H #include "treeBoundBox.H" #include "searchableSurface.H" #include "objectRegistry.H" #include "indexedOctree.H" #include "treeDataTriSurface.H" #include "treeDataPrimitivePatch.H" #include "treeDataEdge.H" #include "EdgeMap.H" #include "triSurface.H" #include "triSurfaceRegionSearch.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { /*---------------------------------------------------------------------------*\ Class triSurfaceMesh Declaration \*---------------------------------------------------------------------------*/ class triSurfaceMesh : public searchableSurface, public objectRegistry, // so we can store fields public triSurface, public triSurfaceRegionSearch { private: // Private member data //- Supplied fileName override fileName fName_; //- Optional min triangle quality. Triangles below this get // ignored for normal calculation scalar minQuality_; //- Search tree for boundary edges. mutable autoPtr<indexedOctree<treeDataEdge>> edgeTree_; //- Names of regions mutable wordList regions_; //- Is surface closed mutable label surfaceClosed_; // Private Member Functions ////- Helper: find instance of files without header //static word findRawInstance //( // const Time&, // const fileName&, // const word& //); //- Return fileName to load IOobject from static fileName checkFile(const IOobject& io); //- Return fileName to load IOobject from. Optional override of fileName static fileName checkFile(const IOobject&, const dictionary&); //- Helper function for isSurfaceClosed static bool addFaceToEdge ( const edge&, EdgeMap<label>& ); //- Check whether surface is closed without calculating any permanent // addressing. bool isSurfaceClosed() const; //- Steps to next intersection. Adds smallVec and starts tracking // from there. static void getNextIntersections ( const indexedOctree<treeDataTriSurface>& octree, const point& start, const point& end, const vector& smallVec, DynamicList<pointIndexHit, 1, 1>& hits ); //- Disallow default bitwise copy construct triSurfaceMesh(const triSurfaceMesh&); //- Disallow default bitwise assignment void operator=(const triSurfaceMesh&); public: //- Runtime type information TypeName("triSurfaceMesh"); // Constructors //- Construct from triSurface triSurfaceMesh(const IOobject&, const triSurface&); //- Construct read. triSurfaceMesh(const IOobject& io); //- Construct from IO and dictionary (used by searchableSurface). // Dictionary may contain a 'scale' entry (eg, 0.001: mm -> m) triSurfaceMesh ( const IOobject& io, const dictionary& dict ); //- Destructor virtual ~triSurfaceMesh(); //- Clear storage void clearOut(); // Member Functions //- Move points virtual void movePoints(const pointField&); //- Demand driven construction of octree for boundary edges const indexedOctree<treeDataEdge>& edgeTree() const; // searchableSurface implementation virtual const wordList& regions() const; //- Whether supports volume type below. I.e. whether is closed. virtual bool hasVolumeType() const; //- Range of local indices that can be returned. virtual label size() const { return triSurface::size(); } //- Get representative set of element coordinates // Usually the element centres (should be of length size()). virtual tmp<pointField> coordinates() const; //- Get bounding spheres (centre and radius squared). Any point // on surface is guaranteed to be inside. virtual void boundingSpheres ( pointField& centres, scalarField& radiusSqr ) const; //- Get the points that define the surface. virtual tmp<pointField> points() const; // Does any part of the surface overlap the supplied bound box? virtual bool overlaps(const boundBox& bb) const; virtual void findNearest ( const pointField& sample, const scalarField& nearestDistSqr, List<pointIndexHit>& ) const; virtual void findNearest ( const pointField& sample, const scalarField& nearestDistSqr, const labelList& regionIndices, List<pointIndexHit>& ) const; virtual void findLine ( const pointField& start, const pointField& end, List<pointIndexHit>& ) const; virtual void findLineAny ( const pointField& start, const pointField& end, List<pointIndexHit>& ) const; //- Get all intersections in order from start to end. virtual void findLineAll ( const pointField& start, const pointField& end, List<List<pointIndexHit>>& ) const; //- From a set of points and indices get the region virtual void getRegion ( const List<pointIndexHit>&, labelList& region ) const; //- From a set of points and indices get the normal virtual void getNormal ( const List<pointIndexHit>&, vectorField& normal ) const; //- Determine type (inside/outside/mixed) for point. unknown if // cannot be determined (e.g. non-manifold surface) virtual void getVolumeType ( const pointField&, List<volumeType>& ) const; // Other //- WIP. Store element-wise field. virtual void setField(const labelList& values); //- WIP. From a set of hits (points and // indices) get the specified field. Misses do not get set. virtual void getField(const List<pointIndexHit>&, labelList&) const; // regIOobject implementation bool writeData(Ostream&) const { NotImplemented; return false; } //- Write using given format, version and compression virtual bool writeObject ( IOstream::streamFormat fmt, IOstream::versionNumber ver, IOstream::compressionType cmp ) const; }; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // #endif // ************************************************************************* // |
|
triSurfaceMesh.C triSurfaceMesh.C (20,840 bytes)
/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- 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/>. \*---------------------------------------------------------------------------*/ #include "triSurfaceMesh.H" #include "Random.H" #include "addToRunTimeSelectionTable.H" #include "EdgeMap.H" #include "triSurfaceFields.H" #include "Time.H" #include "PatchTools.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(triSurfaceMesh, 0); addToRunTimeSelectionTable(searchableSurface, triSurfaceMesh, dict); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // //// Special version of Time::findInstance that does not check headerOk //// to search for instances of raw files //Foam::word Foam::triSurfaceMesh::findRawInstance //( // const Time& runTime, // const fileName& dir, // const word& name //) //{ // // Check current time first // if (isFile(runTime.path()/runTime.timeName()/dir/name)) // { // return runTime.timeName(); // } // instantList ts = runTime.times(); // label instanceI; // // for (instanceI = ts.size()-1; instanceI >= 0; --instanceI) // { // if (ts[instanceI].value() <= runTime.timeOutputValue()) // { // break; // } // } // // // continue searching from here // for (; instanceI >= 0; --instanceI) // { // if (isFile(runTime.path()/ts[instanceI].name()/dir/name)) // { // return ts[instanceI].name(); // } // } // // // // not in any of the time directories, try constant // // // Note. This needs to be a hard-coded constant, rather than the // // constant function of the time, because the latter points to // // the case constant directory in parallel cases // // if (isFile(runTime.path()/runTime.constant()/dir/name)) // { // return runTime.constant(); // } // // FatalErrorInFunction // << "Cannot find file \"" << name << "\" in directory " // << runTime.constant()/dir // << exit(FatalError); // // return runTime.constant(); //} Foam::fileName Foam::triSurfaceMesh::checkFile(const IOobject& io) { const fileName fName(io.filePath()); if (fName.empty()) { FatalErrorInFunction << "Cannot find triSurfaceMesh starting from " << io.objectPath() << exit(FatalError); } return fName; } Foam::fileName Foam::triSurfaceMesh::checkFile ( const IOobject& io, const dictionary& dict ) { fileName fName; if (dict.readIfPresent("fileName", fName, false, false)) { fName.expand(); if (!fName.isAbsolute()) { fName = io.objectPath().path()/fName; } if (!exists(fName)) { FatalErrorInFunction << "Cannot find triSurfaceMesh at " << fName << exit(FatalError); } } else { fName = io.filePath(); if (!exists(fName)) { FatalErrorInFunction << "Cannot find triSurfaceMesh starting from " << io.objectPath() << exit(FatalError); } } return fName; } bool Foam::triSurfaceMesh::addFaceToEdge ( const edge& e, EdgeMap<label>& facesPerEdge ) { EdgeMap<label>::iterator eFnd = facesPerEdge.find(e); if (eFnd != facesPerEdge.end()) { if (eFnd() == 2) { return false; } eFnd()++; } else { facesPerEdge.insert(e, 1); } return true; } bool Foam::triSurfaceMesh::isSurfaceClosed() const { const pointField& pts = triSurface::points(); // Construct pointFaces. Let's hope surface has compact point // numbering ... labelListList pointFaces; invertManyToMany(pts.size(), *this, pointFaces); // Loop over all faces surrounding point. Count edges emanating from point. // Every edge should be used by two faces exactly. // To prevent doing work twice per edge only look at edges to higher // point EdgeMap<label> facesPerEdge(100); forAll(pointFaces, pointi) { const labelList& pFaces = pointFaces[pointi]; facesPerEdge.clear(); forAll(pFaces, i) { const triSurface::FaceType& f = triSurface::operator[](pFaces[i]); label fp = findIndex(f, pointi); // Something weird: if I expand the code of addFaceToEdge in both // below instances it gives a segmentation violation on some // surfaces. Compiler (4.3.2) problem? // Forward edge label nextPointi = f[f.fcIndex(fp)]; if (nextPointi > pointi) { bool okFace = addFaceToEdge ( edge(pointi, nextPointi), facesPerEdge ); if (!okFace) { return false; } } // Reverse edge label prevPointi = f[f.rcIndex(fp)]; if (prevPointi > pointi) { bool okFace = addFaceToEdge ( edge(pointi, prevPointi), facesPerEdge ); if (!okFace) { return false; } } } // Check for any edges used only once. forAllConstIter(EdgeMap<label>, facesPerEdge, iter) { if (iter() != 2) { return false; } } } return true; } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io, const triSurface& s) : searchableSurface(io), objectRegistry ( IOobject ( io.name(), io.instance(), io.local(), io.db(), io.readOpt(), io.writeOpt(), false // searchableSurface already registered under name ) ), triSurface(s), triSurfaceRegionSearch(s), minQuality_(-1), surfaceClosed_(-1) { const pointField& pts = triSurface::points(); bounds() = boundBox(pts); } Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io) : // Find instance for triSurfaceMesh searchableSurface(io), //( // IOobject // ( // io.name(), // io.time().findInstance(io.local(), word::null), // io.local(), // io.db(), // io.readOpt(), // io.writeOpt(), // io.registerObject() // ) //), // Reused found instance in objectRegistry objectRegistry ( IOobject ( io.name(), static_cast<const searchableSurface&>(*this).instance(), io.local(), io.db(), io.readOpt(), io.writeOpt(), false // searchableSurface already registered under name ) ), triSurface ( checkFile ( static_cast<const searchableSurface&>(*this) ) ), triSurfaceRegionSearch(static_cast<const triSurface&>(*this)), minQuality_(-1), surfaceClosed_(-1) { const pointField& pts = triSurface::points(); bounds() = boundBox(pts); } Foam::triSurfaceMesh::triSurfaceMesh ( const IOobject& io, const dictionary& dict ) : searchableSurface(io), //( // IOobject // ( // io.name(), // io.time().findInstance(io.local(), word::null), // io.local(), // io.db(), // io.readOpt(), // io.writeOpt(), // io.registerObject() // ) //), // Reused found instance in objectRegistry objectRegistry ( IOobject ( io.name(), static_cast<const searchableSurface&>(*this).instance(), io.local(), io.db(), io.readOpt(), io.writeOpt(), false // searchableSurface already registered under name ) ), triSurface ( checkFile ( static_cast<const searchableSurface&>(*this), dict ) ), triSurfaceRegionSearch(static_cast<const triSurface&>(*this), dict), minQuality_(-1), surfaceClosed_(-1) { // Reading from supplied fileName instead of objectPath/filePath dict.readIfPresent("fileName", fName_, false, false); scalar scaleFactor = 0; // allow rescaling of the surface points // eg, CAD geometries are often done in millimeters if (dict.readIfPresent("scale", scaleFactor) && scaleFactor > 0) { Info<< searchableSurface::name() << " : using scale " << scaleFactor << endl; triSurface::scalePoints(scaleFactor); } const pointField& pts = triSurface::points(); bounds() = boundBox(pts); // Have optional minimum quality for normal calculation if (dict.readIfPresent("minQuality", minQuality_) && minQuality_ > 0) { Info<< searchableSurface::name() << " : ignoring triangles with quality < " << minQuality_ << " for normals calculation." << endl; } } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::triSurfaceMesh::~triSurfaceMesh() { clearOut(); } void Foam::triSurfaceMesh::clearOut() { triSurfaceRegionSearch::clearOut(); edgeTree_.clear(); triSurface::clearOut(); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // Foam::tmp<Foam::pointField> Foam::triSurfaceMesh::coordinates() const { tmp<pointField> tPts(new pointField(8)); pointField& pt = tPts.ref(); // Use copy to calculate face centres so they don't get stored pt = PrimitivePatch<triSurface::FaceType, SubList, const pointField&> ( SubList<triSurface::FaceType>(*this, triSurface::size()), triSurface::points() ).faceCentres(); return tPts; } void Foam::triSurfaceMesh::boundingSpheres ( pointField& centres, scalarField& radiusSqr ) const { centres = coordinates(); radiusSqr.setSize(size()); radiusSqr = 0.0; const pointField& pts = triSurface::points(); forAll(*this, facei) { const labelledTri& f = triSurface::operator[](facei); const point& fc = centres[facei]; forAll(f, fp) { const point& pt = pts[f[fp]]; radiusSqr[facei] = max(radiusSqr[facei], Foam::magSqr(fc-pt)); } } // Add a bit to make sure all points are tested inside radiusSqr += Foam::sqr(SMALL); } Foam::tmp<Foam::pointField> Foam::triSurfaceMesh::points() const { return triSurface::points(); } bool Foam::triSurfaceMesh::overlaps(const boundBox& bb) const { const indexedOctree<treeDataTriSurface>& octree = tree(); labelList indices = octree.findBox(treeBoundBox(bb)); return !indices.empty(); } void Foam::triSurfaceMesh::movePoints(const pointField& newPoints) { triSurfaceRegionSearch::clearOut(); edgeTree_.clear(); triSurface::movePoints(newPoints); } const Foam::indexedOctree<Foam::treeDataEdge>& Foam::triSurfaceMesh::edgeTree() const { if (edgeTree_.empty()) { // Boundary edges labelList bEdges ( identity ( nEdges() -nInternalEdges() ) + nInternalEdges() ); treeBoundBox bb(Zero, Zero); if (bEdges.size()) { label nPoints; PatchTools::calcBounds ( static_cast<const triSurface&>(*this), bb, nPoints ); // Random number generator. Bit dodgy since not exactly random ;-) Random rndGen(65431); // Slightly extended bb. Slightly off-centred just so on symmetric // geometry there are less face/edge aligned items. bb = bb.extend(rndGen, 1e-4); bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL); bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL); } scalar oldTol = indexedOctree<treeDataEdge>::perturbTol(); indexedOctree<treeDataEdge>::perturbTol() = tolerance(); edgeTree_.reset ( new indexedOctree<treeDataEdge> ( treeDataEdge ( false, // cachebb edges(), // edges localPoints(), // points bEdges // selected edges ), bb, // bb maxTreeDepth(), // maxLevel 10, // leafsize 3.0 // duplicity ) ); indexedOctree<treeDataEdge>::perturbTol() = oldTol; } return edgeTree_(); } const Foam::wordList& Foam::triSurfaceMesh::regions() const { if (regions_.empty()) { regions_.setSize(patches().size()); forAll(regions_, regionI) { regions_[regionI] = patches()[regionI].name(); } } return regions_; } // Find out if surface is closed. bool Foam::triSurfaceMesh::hasVolumeType() const { if (surfaceClosed_ == -1) { if (isSurfaceClosed()) { surfaceClosed_ = 1; } else { surfaceClosed_ = 0; } } return surfaceClosed_ == 1; } void Foam::triSurfaceMesh::findNearest ( const pointField& samples, const scalarField& nearestDistSqr, List<pointIndexHit>& info ) const { triSurfaceSearch::findNearest(samples, nearestDistSqr, info); } void Foam::triSurfaceMesh::findNearest ( const pointField& samples, const scalarField& nearestDistSqr, const labelList& regionIndices, List<pointIndexHit>& info ) const { triSurfaceRegionSearch::findNearest ( samples, nearestDistSqr, regionIndices, info ); } void Foam::triSurfaceMesh::findLine ( const pointField& start, const pointField& end, List<pointIndexHit>& info ) const { triSurfaceSearch::findLine(start, end, info); } void Foam::triSurfaceMesh::findLineAny ( const pointField& start, const pointField& end, List<pointIndexHit>& info ) const { triSurfaceSearch::findLineAny(start, end, info); } void Foam::triSurfaceMesh::findLineAll ( const pointField& start, const pointField& end, List<List<pointIndexHit>>& info ) const { triSurfaceSearch::findLineAll(start, end, info); } void Foam::triSurfaceMesh::getRegion ( const List<pointIndexHit>& info, labelList& region ) const { region.setSize(info.size()); forAll(info, i) { if (info[i].hit()) { region[i] = triSurface::operator[](info[i].index()).region(); } else { region[i] = -1; } } } void Foam::triSurfaceMesh::getNormal ( const List<pointIndexHit>& info, vectorField& normal ) const { const triSurface& s = static_cast<const triSurface&>(*this); const pointField& pts = s.points(); normal.setSize(info.size()); if (minQuality_ >= 0) { // Make sure we don't use triangles with low quality since // normal is not reliable. const labelListList& faceFaces = s.faceFaces(); forAll(info, i) { if (info[i].hit()) { label facei = info[i].index(); normal[i] = s[facei].normal(pts); scalar qual = s[facei].tri(pts).quality(); if (qual < minQuality_) { // Search neighbouring triangles const labelList& fFaces = faceFaces[facei]; forAll(fFaces, j) { label nbrI = fFaces[j]; scalar nbrQual = s[nbrI].tri(pts).quality(); if (nbrQual > qual) { qual = nbrQual; normal[i] = s[nbrI].normal(pts); } } } normal[i] /= mag(normal[i]) + VSMALL; } else { // Set to what? normal[i] = Zero; } } } else { forAll(info, i) { if (info[i].hit()) { label facei = info[i].index(); // Cached: //normal[i] = faceNormals()[facei]; // Uncached normal[i] = s[facei].normal(pts); normal[i] /= mag(normal[i]) + VSMALL; } else { // Set to what? normal[i] = Zero; } } } } void Foam::triSurfaceMesh::setField(const labelList& values) { autoPtr<triSurfaceLabelField> fldPtr ( new triSurfaceLabelField ( IOobject ( "values", objectRegistry::time().timeName(), // instance "triSurface", // local *this, IOobject::NO_READ, IOobject::AUTO_WRITE ), *this, dimless, labelField(values) ) ); // Store field on triMesh fldPtr.ptr()->store(); } void Foam::triSurfaceMesh::getField ( const List<pointIndexHit>& info, labelList& values ) const { if (foundObject<triSurfaceLabelField>("values")) { values.setSize(info.size()); const triSurfaceLabelField& fld = lookupObject<triSurfaceLabelField> ( "values" ); forAll(info, i) { if (info[i].hit()) { values[i] = fld[info[i].index()]; } } } } void Foam::triSurfaceMesh::getVolumeType ( const pointField& points, List<volumeType>& volType ) const { volType.setSize(points.size()); scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol(); indexedOctree<treeDataTriSurface>::perturbTol() = tolerance(); forAll(points, pointi) { const point& pt = points[pointi]; if (!tree().bb().contains(pt)) { // Have to calculate directly as outside the octree volType[pointi] = tree().shapes().getVolumeType(tree(), pt); } else { // - use cached volume type per each tree node volType[pointi] = tree().getVolumeType(pt); } } indexedOctree<treeDataTriSurface>::perturbTol() = oldTol; } bool Foam::triSurfaceMesh::writeObject ( IOstream::streamFormat fmt, IOstream::versionNumber ver, IOstream::compressionType cmp ) const { fileName fullPath; if (fName_.size()) { // Override file name fullPath = fName_; fullPath.expand(); if (!fullPath.isAbsolute()) { // Add directory from regIOobject fullPath = searchableSurface::objectPath().path()/fullPath; } } else { fullPath = searchableSurface::objectPath(); } if (!mkDir(fullPath.path())) { return false; } triSurface::write(fullPath); if (!isFile(fullPath)) { return false; } return true; } // ************************************************************************* // |
|
Isn't the problem that a '.' is a valid word character and thus automatically a valid character for a keyword, but the '.' has also been double-purposed as a scoping character? IMO a consistent solution would be to have '/' as the scoping character, which is definitely not a valid word character and require surrounding scoped variables with ${ }. |
|
Allowing "." as a valid keyword character was arguably not ideal, but we probably need to live with it. As far as I can tell, it is only included as a character in keywords that are: 1) filenames, e.g. motorBike.obj 2) fields of particular phases, e.g. alpha.water, T.steam Using filenames as keywords is bad design. I suggest that is eliminated throughout the code. That leaves only fields of phases as the one exception, which I propose we handle with "\." unless that causes some kind of critical failure. PS Surely it is too late for "/" as the scoping character? which was considered, but rejected as a confusing scoping character. I cannot recall whether using "_" for fields of phases, e.g. alpha_water, was only rejected because of p_rgh, but I guess that would have been better. |
|
I've thought about this as well and forgot to write down sooner what I've remembered/associated so far, also because I still wanted to double-check some of these possibilities. Either way, before it's too late, adding to what Chris has already listed, here are the additional situations that may need to be kept in mind: - Using slash '/' as a scoping character could potentially lead to problems when using '#calc', although I haven't tested yet if it even supports these scoped variables; but I guess that using '${}' would have solved that issue, as Mark mentioned. - There is a particular type of object/file naming convention which may also interfere with the parsing of the scopes, for example, names such as 'thermo:psi' and 'cloudname:UCoeff'. I haven't checked how these are parsed/interpreted, but could potentially conflict with the colon as a prefix (e.g. '$:thermo:psi.boundaryField.inlet.value'). - Using the new convention to have file names explicitly defined through a keyword, is a big advantage for people who work a lot with CAD and then have file names like "30+-0.1mm washer.stl", which would be a nightmare when trying to mesh a few hundred or more of them with 'snappyHexMesh'. |
|
Update on my comment " Using filenames as keywords is bad design. I suggest that is eliminated throughout the code." The use in snappyHexDict (motorBike.obj...) was fixed by the following commit: https://github.com/OpenFOAM/OpenFOAM-dev/commit/80e22788e4c6fe0dbdfe2cb5004eefc0fb49c121 The use in surfaceFeatureExtract has not been fixed, as suggested: motorBike.obj { extractionMethod extractFromSurface; ... should be replaced with extractFromSurface { surfaces (motorBike.obj anotherSurface.obj); |
|
I believe that with the recent commit aaed6290d06976caf4c878a41734d5b9a0c50d3c - https://github.com/OpenFOAM/OpenFOAM-dev/commit/aaed6290d06976caf4c878a41734d5b9a0c50d3c - this report can now be marked as resolved, given that 'surfaceFeatureExtract' can be discontinued in the (near) future, now that the new 'surfaceFeatures' utility as come to existence. |
Date Modified | Username | Field | Change |
---|---|---|---|
2016-12-15 12:41 | MattijsJ | New Issue | |
2016-12-15 12:46 | wyldckat | Note Added: 0007487 | |
2016-12-15 18:02 | MattijsJ | File Added: dictionary.C | |
2016-12-15 18:02 | MattijsJ | Note Added: 0007489 | |
2016-12-16 10:05 | MattijsJ | Note Added: 0007497 | |
2016-12-16 10:21 | chris | Note Added: 0007500 | |
2016-12-16 10:55 | henry | Note Edited: 0007500 | |
2016-12-20 16:00 | MattijsJ | File Added: triSurfaceMesh.H | |
2016-12-20 16:01 | MattijsJ | File Added: triSurfaceMesh-2.H | |
2016-12-20 16:01 | MattijsJ | Note Added: 0007538 | |
2016-12-20 16:01 | MattijsJ | File Added: triSurfaceMesh.C | |
2016-12-20 16:01 | MattijsJ | Note Added: 0007539 | |
2016-12-20 21:01 |
|
Note Added: 0007540 | |
2017-01-05 17:20 | chris | Note Added: 0007598 | |
2017-01-05 23:19 | wyldckat | Note Added: 0007600 | |
2017-06-30 12:32 | chris | Note Added: 0008306 | |
2017-06-30 13:22 | chris | Assigned To | => chris |
2017-06-30 13:22 | chris | Status | new => feedback |
2017-06-30 13:23 | chris | Assigned To | chris => |
2017-06-30 13:23 | chris | Status | feedback => assigned |
2017-06-30 13:24 | chris | Assigned To | => chris |
2017-06-30 13:24 | chris | Status | assigned => new |
2017-06-30 13:25 | chris | Assigned To | chris => |
2018-04-20 21:26 | wyldckat | Note Added: 0009510 | |
2018-04-20 21:27 | wyldckat | Assigned To | => henry |
2018-04-20 21:27 | wyldckat | Status | new => resolved |
2018-04-20 21:27 | wyldckat | Resolution | open => fixed |
2018-04-20 21:27 | wyldckat | Fixed in Version | => dev |