diff --git a/src/fileFormats/Make/files b/src/fileFormats/Make/files index cf37026392c8c3978a521d7e1634638287604a26..928615cb5beca0050b91edc26dff5496e8c32c82 100644 --- a/src/fileFormats/Make/files +++ b/src/fileFormats/Make/files @@ -25,6 +25,7 @@ $(part)/surface/ensightOutputSurface.C ensight/read/ensightReadFile.C ensight/type/ensightPTraits.C +abaqus/ABAQUSCore.C nastran/NASCore.C obj/OBJstream.C fire/FIRECore.C diff --git a/src/fileFormats/abaqus/ABAQUSCore.C b/src/fileFormats/abaqus/ABAQUSCore.C new file mode 100644 index 0000000000000000000000000000000000000000..356bffa3c1d99211cbca07db08f5ba4e25198f47 --- /dev/null +++ b/src/fileFormats/abaqus/ABAQUSCore.C @@ -0,0 +1,695 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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 "ABAQUSCore.H" +#include "IFstream.H" +#include "ListOps.H" +#include "stringOps.H" +#include "UIListStream.H" + +#undef Foam_readAbaqusSurface + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +static Foam::Map<Foam::labelList> abaqusToFoamFaceAddr_; + + +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Use peek(), get(), unget() to detect and skip lines that appear to be +// "** comment" lines. +// +// Uses a mix of std::istream and ISstream methods +// since we need the low-level get()/unget() + +static void skipComments(ISstream& iss) +{ + #if OPENFOAM < 2002 + string line; + #endif + + auto& is = iss.stdStream(); + + bool isComment = true; + while (isComment) + { + isComment = ('*' == is.peek()); + + if (isComment) + { + // Get and check the next one + (void) is.get(); + + isComment = ('*' == is.peek()); + if (isComment) + { + // Found "** ..." (a comment) - read/discard + // ISstream::getLine to keep track of the line numbers + + #if OPENFOAM >= 2002 + iss.getLine(nullptr); + #else + iss.getLine(line); + #endif + } + else + { + // Not a comment + // - unget the '*', implicitly break out of loop + is.unget(); + } + } + } +} + + +// Get an identifier of the form "NSET=..." (case-insensitive) +// Return the string on success, an empty string on failure + +static string getIdentifier(const word& keyword, string& inputLine) +{ + // Strip out whitespace (not a valid Abaqus identifier anyhow) + // - makes parsing easier, avoids tab/carriage-returns etc. + + stringOps::inplaceRemoveSpace(inputLine); + + // Do string comparisons in upper-case + + const auto key(stringOps::upper(keyword)); + const auto line(stringOps::upper(inputLine)); + + // Extract "..,key=value,key2=value," + + // Not sure if we need the additional ',' prefix + // in search to avoid similar keys. + + auto beg = line.find("," + key + "="); + + if (beg != std::string::npos) + { + // Skip past the '=' + beg += key.size() + 2; + + // The closing comma + auto len = line.find(',', beg); + if (len != std::string::npos) + { + len -= beg; + } + + // Substring from inputLine (not uppercase!) + return inputLine.substr(beg, len); + } + + // Not found + return string(); +} + + +// Walk the string content (CSV format) to append integer labels +// until the line is exhausted or the list is full. +// +// Return false on read error or if the line exhausted while getting +// element. + +static bool appendCsvLabels +( + const std::string& line, + labelUList& elemNodes, + label& nodei +) +{ + const label nNodes = elemNodes.size(); + + std::size_t pos = 0; + + while (nodei < nNodes && pos != std::string::npos) + { + auto beg = pos; + auto len = line.find(',', pos); + + if (len == std::string::npos) + { + pos = len; + } + else + { + pos = len + 1; + len -= beg; + } + + if (readLabel(line.substr(beg, len), elemNodes[nodei])) + { + ++nodei; + } + else + { + // Read error, or need another line + return false; + } + } + + return (nodei >= nNodes); +} + + +} // End namespace Foam + + +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +const Foam::Map<Foam::labelList>& +Foam::fileFormats::ABAQUSCore::abaqusToFoamFaceAddr() +{ + if (abaqusToFoamFaceAddr_.empty()) + { + abaqusToFoamFaceAddr_.emplace(abaqusTet, labelList({3, 2, 0, 1})); + abaqusToFoamFaceAddr_.emplace(abaqusPrism, labelList({0, 1, 4, 3, 2})); + abaqusToFoamFaceAddr_.emplace(abaqusHex, labelList({4, 5, 2, 1, 3, 0})); + } + + return abaqusToFoamFaceAddr_; +} + + +Foam::fileFormats::ABAQUSCore::shapeType +Foam::fileFormats::ABAQUSCore::getElementType(const std::string& elemTypeName) +{ + // Check for element-type + #undef checkElemType + #define checkElemType(test) (elemTypeName.find(test) != std::string::npos) + + if + ( + checkElemType("S3") + || checkElemType("CPE3") + || checkElemType("2D3") + ) + { + return shapeType::abaqusTria; + } + else if + ( + checkElemType("S4") + || checkElemType("CPE4") + || checkElemType("2D4") + || checkElemType("CPEG4") + ) + { + return shapeType::abaqusQuad; + } + else if + ( + checkElemType("3D4") // C3D4*, Q3D4, ... + ) + { + return shapeType::abaqusTet; + } + else if + ( + checkElemType("3D5") // C3D5* + ) + { + return shapeType::abaqusPyr; + } + else if + ( + checkElemType("3D6") // C3D6* + ) + { + return shapeType::abaqusPrism; + } + else if + ( + checkElemType("3D8") // C3D8* + ) + { + return shapeType::abaqusHex; + } + + #undef checkElemType + + return shapeType::abaqusUnknownShape; +} + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::label +Foam::fileFormats::ABAQUSCore::readHelper::addNewElset +( + const std::string& setName +) +{ + if (elsetMap_.empty()) + { + // Always have a lookup for empty string + elsetMap_.set(string::null, 0); + } + + if (setName.empty()) + { + return 0; + } + + // Direct case-sensitive lookup - it might be there + + label setId = elsetMap_.lookup(setName, -1); + if (setId >= 0) + { + return setId; + } + + + // Case-insensitive search, use upper-case + + const auto needle(stringOps::upper(setName)); + + forAllConstIters(elsetMap_, iter) + { + const auto haystack(stringOps::upper(iter.key())); + + if (needle == haystack) + { + return iter.val(); + } + } + + // Not there. Save at the next location + setId = elsetMap_.size(); + elsetMap_.set(setName, setId); + + return setId; +} + + +Foam::label +Foam::fileFormats::ABAQUSCore::readHelper::readPoints +( + ISstream& is +) +{ + const label initialCount = points_.size(); + + char sep; // Comma separator (dummy) + label id; + point p; + + string line; + + // Read nodes (points) until next "*Section" + while (is.peek() != '*' && is.peek() != EOF) + { + // Grab the line and wrap as string-stream + is.getLine(line); + UIListStream ss(line.data(), line.length()); + + if (line.empty()) + { + // Not sure if we should terminate on blank lines? + continue; + } + + // Parse line for ID, X, Y, Z + ss >> id >> sep >> p.x() >> sep >> p.y() >> sep >> p.z(); + + nodeIds_.append(id); + points_.append(p); + } + + return (points_.size() - initialCount); +} + + +Foam::label +Foam::fileFormats::ABAQUSCore::readHelper::readElements +( + ISstream& is, + const ABAQUSCore::shapeType shape, + const label setId +) +{ + // Info<< "*Element" << nl; + + const label nNodes = ABAQUSCore::nPoints(shape); + + if (!nNodes) + { + return 0; + } + + const label initialCount = elemTypes_.size(); + + char sep; // Comma separator (dummy) + label id; + labelList elemNodes(nNodes, Zero); + + string line; + + // Read element connectivity until next "*Section" + + // Parse for ID, node1, node2, ... + while (is.peek() != '*' && is.peek() != EOF) + { + // elemNodes = Zero; for sanity checks? + + is >> id >> sep; + + label nodei = 0; + while (nodei < nNodes) + { + // Grab the rest of the line, walk through CSV fields + is.getLine(line); + + appendCsvLabels(line, elemNodes, nodei); + } + + // Checks? + + connectivity_.append(elemNodes); + elemTypes_.append(shape); + elemIds_.append(id); + elsetIds_.append(setId); + } + + return (elemTypes_.size() - initialCount); +} + + +void Foam::fileFormats::ABAQUSCore::readHelper::read +( + ISstream& is +) +{ + clear(); + + label nread; + string line; + + while (is.good()) + { + is.getLine(line); + + // Start processing on "*Section-Name", + // but skip "** comments" etc + if (line[0] != '*' || !std::isalpha(line[1])) + { + continue; + } + + // Some abaqus files use upper-case or mixed-case for section names, + // convert all to upper-case for ease. + + string upperLine(stringOps::upper(line)); + + // "*Nodes" section + if (upperLine.starts_with("*NODE")) + { + // Ignore "NSET=...", we cannot do anything useful with it + + skipComments(is); + + nread = readPoints(is); + + if (verbose_) + { + InfoErr + << "Read " << nread << " *NODE entries" << nl; + } + continue; + } + + // "*Element" section + if (upperLine.starts_with("*ELEMENT,")) + { + // Must have "TYPE=..." + auto elemTypeName = getIdentifier("TYPE", line); + + // May have "ELSET=..." on the same line + string elsetName(getIdentifier("ELSET", line)); + + const shapeType shape(getElementType(elemTypeName)); + + if (!ABAQUSCore::nPoints(shape)) + { + // Unknown/unsupported + if (verbose_) + { + InfoErr + << "Ignore abaqus element type: " + << elemTypeName << nl; + } + continue; + } + + const label elsetId = addNewElset(elsetName); + + skipComments(is); + + nread = readElements(is, shape, elsetId); + + if (verbose_) + { + InfoErr + << "Read " << nread << " *ELEMENT entries (" + << elemTypeName << ") elset=" + << elsetName << nl; + } + continue; + } + + + // "*Surface" section + if (upperLine.starts_with("*SURFACE,")) + { + #ifdef Foam_readAbaqusSurface + + skipComments(is); + + #else + Info<< "Reading of abaqus surfaces not implemented" << nl; + #endif + + continue; + } + } +} + + +void Foam::fileFormats::ABAQUSCore::readHelper::purge_solids() +{ + // Negative set + bitSet select(elemTypes_.size(), false); + + forAll(elemTypes_, i) + { + if (!isValidType(elemTypes_[i]) || isSolidType(elemTypes_[i])) + { + select.set(i); + } + } + + if (select.any()) + { + select.flip(); + + inplaceSubset(select, connectivity_); + inplaceSubset(select, elemTypes_); + + inplaceSubset(select, elemIds_); + inplaceSubset(select, elsetIds_); + } +} + + +void Foam::fileFormats::ABAQUSCore::readHelper::compact_nodes() +{ + if (!nodeIds_.empty()) + { + // Has original 1-based ids + // + // Need to convert to local (0-based) points + // in the order in which we read them + // and compact unused values + + // Could construct a sort order to preserve the original + // point order, but that is not likely relevant for anyone. + + + // Which original node ids actually being used by elements? + + // We may have many ids, but speculate that they are sparse + // and have high element numbers. + + // Use a Map instead of labelList. + + Map<label> nodeIdRemapping(2*points_.size()); + + // Pass 1: which nodes are being used? + + for (const labelList& elem : connectivity_) + { + for (const label origId : elem) + { + nodeIdRemapping(origId) = 0; // any value + } + } + + // Define compact local points, finalize the node id remapping + + label nPoints = 0; + labelList oldToNewLocal(nodeIds_.size(), -1); + + forAll(nodeIds_, i) + { + const label origId = nodeIds_[i]; + + if (nodeIdRemapping.found(origId)) + { + oldToNewLocal[i] = nPoints; + nodeIdRemapping(origId) = nPoints; + ++nPoints; + } + } + + // Prune out -1 values (shrinks list) + inplaceReorder(oldToNewLocal, points_, true); + + // Relabel the elements + + for (labelList& elem : connectivity_) + { + for (label& id : elem) + { + id = nodeIdRemapping[id]; + } + } + + // Done! + nodeIds_.clear(); + } + else + { + // Already numbered (0-based), but perhaps not compacted + + // Which node ids actually being used by elements? + bitSet usedNodeIds(points_.size()); + + for (const labelList& elem : connectivity_) + { + usedNodeIds.set(elem); + } + + // Compact the numbers + labelList oldToNewLocal = invert(points_.size(), usedNodeIds); + + // Prune out -1 values (shrinks list) + inplaceReorder(oldToNewLocal, points_, true); + + + // Renumber non-compact to compact + for (labelList& elem : connectivity_) + { + inplaceRenumber(oldToNewLocal, elem); + } + } +} + + +void Foam::fileFormats::ABAQUSCore::writePoints +( + Ostream& os, + const UList<point>& points, + const scalar scaleFactor +) +{ + if (points.empty()) + { + return; + } + + // Set the precision of the points data to 10 + os.precision(10); + + // Force decimal point for Fortran input + os.setf(std::ios::showpoint); + + label vertId = 1; // 1-based vertex labels + + os << "*NODE" << nl; + for (const point& p : points) + { + // Convert [m] -> [mm] etc + os << " " + << vertId << ", " + << (scaleFactor * p.x()) << ',' + << (scaleFactor * p.y()) << ',' + << (scaleFactor * p.z()) << nl; + + ++vertId; + } +} + + +Foam::label Foam::fileFormats::ABAQUSCore::faceDecomposition +( + const UList<point>& points, + const UList<face>& faces, + labelList& decompOffsets, + DynamicList<face>& decompFaces +) +{ + // On-demand face decomposition (triangulation) + + decompOffsets.resize(faces.size()+1); + decompFaces.clear(); + + auto offsetIter = decompOffsets.begin(); + *offsetIter = 0; // The first offset is always zero + + for (const face& f : faces) + { + const label n = f.size(); + + if (n != 3 && n != 4) + { + // Decompose non-tri/quad into tris + f.triangles(points, decompFaces); + } + + // The end offset, which is the next begin offset + *(++offsetIter) = decompFaces.size(); + } + + return decompFaces.size(); +} + + +// ************************************************************************* // diff --git a/src/fileFormats/abaqus/ABAQUSCore.H b/src/fileFormats/abaqus/ABAQUSCore.H new file mode 100644 index 0000000000000000000000000000000000000000..6360952ea35c134480ec53d5ff4188a86bd36905 --- /dev/null +++ b/src/fileFormats/abaqus/ABAQUSCore.H @@ -0,0 +1,306 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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::fileFormats::ABAQUSCore + +Description + Core routines used when reading/writing ABAQUS files. + + Face mappings for abaqus deduced from libmesh internals + + Tet4 cells + \table + Face | OpenFOAM | libmesh | abaqus | starcd + (1 2 3) | 0 | 2 | 2 | 5 + (0 3 2) | 1 | 3 | 3 | 4 + (0 1 3) | 2 | 1 | 1 | 2 + (0 2 1) | 3 | 0 | 0 | 0 + \endtable + + Pyr5 cells + \table + Face | OpenFOAM | libmesh | abaqus | starcd + (0 3 2 1) | 0 | 4 | n/a | 0 + (0 4 3) | 1 | 3 | n/a | 4 + (3 4 2) | 2 | 2 | n/a | 3 + (1 2 4) | 3 | 1 | n/a | 5 + (0 1 4) | 4 | 0 | n/a | 2 + \endtable + + Prism6 cells + \table + Face | OpenFOAM | libmesh | abaqus | starcd + (0 2 1) | 0 | 0 | 0 | 0 + (3 4 5) | 1 | 4 | 1 | 1 + (0 3 5 2) | 2 | 3 | 4 | 4 + (1 2 5 4) | 3 | 2 | 3 | 5 + (0 1 4 3) | 4 | 1 | 2 | 2 + \endtable + + Hex8 cells + \table + Face | OpenFOAM | libmesh | abaqus | starcd + (0 4 7 3) | 0 | 4 | 5 | 4 + (1 2 6 5) | 1 | 2 | 3 | 5 + (0 1 5 4) | 2 | 1 | 2 | 2 + (3 7 6 2) | 3 | 3 | 4 | 3 + (0 3 2 1) | 4 | 0 | 0 | 0 + (4 5 6 7) | 5 | 5 | 1 | 1 + \endtable + +SourceFiles + ABAQUSCore.C + +\*---------------------------------------------------------------------------*/ + +#ifndef ABAQUSCore_H +#define ABAQUSCore_H + +#include "Fstream.H" +#include "Enum.H" +#include "Map.H" +#include "face.H" +#include "point.H" +#include "DynamicList.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +namespace fileFormats +{ + +/*---------------------------------------------------------------------------*\ + Class fileFormats::ABAQUSCore Declaration +\*---------------------------------------------------------------------------*/ + +class ABAQUSCore +{ +public: + + // Public Data, Declarations + + //- Shape-Type - the values are for internal use only! + enum shapeType : uint8_t + { + abaqusUnknownShape = 0, + abaqusTria = 0x03, + abaqusQuad = 0x04, + abaqusTet = 0x84, + abaqusPyr = 0x85, + abaqusPrism = 0x86, + abaqusHex = 0x88 + }; + + + // Public Functions + + //- Classify named element type (eg, S4R) to known/supported + //- element types. + // The input string must be Uppercase! + static shapeType getElementType(const std::string& elemTypeName); + + //- The number of points associated with the element type + inline static int nPoints(shapeType tag) + { + return (tag & 0x3F); + } + + //- True if element type is not unknown/invalid + inline static bool isValidType(shapeType tag) + { + return tag; + } + + //- True if element type is a 2D shell + inline static bool isShellType(shapeType tag) + { + return (tag & 0x07) && !(tag & 0x80); + } + + //- True if element type is a 3D element + inline static bool isSolidType(shapeType tag) + { + return (tag & 0x80); + } + + +protected: + + // Protected Member Functions + + //- Face addressing from ABAQUS faces to OpenFOAM faces. + // For hex, prism, tet primitive shapes. + static const Map<labelList>& abaqusToFoamFaceAddr(); + + + // Protected Classes + + //- Raw reader structure + struct readHelper + { + // General + + //- Additional verbosity + bool verbose_; + + + // Point Handling + + //- Locations of the points (nodes) + DynamicList<point> points_; + + //- The 1-based abaqus Id for the point (node) + DynamicList<label> nodeIds_; + + + // Element Handling + + //- The element connectivity. + // Initially uses the abaqus node Id (1-based) + // but remapped to 0-based compact form later. + DynamicList<labelList> connectivity_; + + //- The 1-based abaqus Id for the element + DynamicList<label> elemIds_; + + //- The element types + DynamicList<ABAQUSCore::shapeType> elemTypes_; + + //- The element set ids + DynamicList<label> elsetIds_; + + //- Mapping of elem set names + HashTable<label, string> elsetMap_; + + + // Constructos + + //- Default construct + explicit readHelper(const bool verbosity=false) + : + verbose_(verbosity) + {} + + + // Member Functions + + //- Clear out contents. + void clear() + { + points_.clear(); + nodeIds_.clear(); + + connectivity_.clear(); + elemTypes_.clear(); + + elemIds_.clear(); + elsetIds_.clear(); + elsetMap_.clear(); + } + + //- Add a new element set name or return an existing one. + // Case-insensitive. + label addNewElset(const std::string& setName); + + + //- Read an abaqus input file + void read(ISstream& is); + + //- Read entries within a "*Nodes" section. + // Appends to points and nodeIds lists. + // + // \return the number of points read + label readPoints(ISstream& is); + + //- Read entries within an "*Element" section. + // If the shape is known/supported, appends to + // connectivity, elemType, elemIds lists. + // + // \return the number of elements read + label readElements + ( + ISstream& is, + const ABAQUSCore::shapeType shape, + const label setId = 0 + ); + + + //- Remove non-shell elements and compact the points + void purge_solids(); + + //- Compact unused points and relabel connectivity + void compact_nodes(); + }; + + + // Constructors + + //- Default construct + ABAQUSCore() = default; + +public: + + // Public Member Functions + + //- Write '*NODE' header and entries to file, optionally with scaling + // This is a no-op for an empty list + static void writePoints + ( + Ostream& os, + const UList<point>& points, + const scalar scaleFactor = 1.0 + ); + + //- Calculate face decomposition for non tri/quad faces + // + // \param points the surface points + // \param faces the surface faces + // \param decompOffsets begin/end offsets (size+1) into decompFaces + // \param decompFaces List of non-tri/quad decomposed into triangles + // + // \return number of decomposed faces + static label faceDecomposition + ( + const UList<point>& points, + const UList<face>& faces, + labelList& decompOffsets, + DynamicList<face>& decompFaces + ); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace fileFormats +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/surfMesh/Make/files b/src/surfMesh/Make/files index 155153d07e8226d901068a8b69868d6094f2e7f5..05c1609192630e2f78fe1ae360a1bb0cac45e9ab 100644 --- a/src/surfMesh/Make/files +++ b/src/surfMesh/Make/files @@ -25,6 +25,8 @@ surfZone/surfZoneIOList.C surfaceFormats = surfaceFormats $(surfaceFormats)/surfaceFormatsCore.C +$(surfaceFormats)/abaqus/ABAQUSsurfaceFormatCore.C +$(surfaceFormats)/abaqus/ABAQUSsurfaceFormatRunTime.C $(surfaceFormats)/ac3d/AC3DsurfaceFormatCore.C $(surfaceFormats)/ac3d/AC3DsurfaceFormatRunTime.C $(surfaceFormats)/fire/FLMAsurfaceFormatRunTime.C @@ -59,6 +61,7 @@ triSurface/patches/surfacePatch.C writers = writers $(writers)/surfaceWriter.C +$(writers)/abaqus/abaqusSurfaceWriter.C $(writers)/boundaryData/boundaryDataSurfaceWriter.C $(writers)/ensight/ensightSurfaceWriter.C $(writers)/foam/foamSurfaceWriter.C diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C new file mode 100644 index 0000000000000000000000000000000000000000..a9677707bb541dcd3ae62fc9e953e00d159ae4f9 --- /dev/null +++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C @@ -0,0 +1,351 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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 "ABAQUSsurfaceFormat.H" +#include "IFstream.H" +#include "IOmanip.H" +#include "faceTraits.H" +#include "stringOps.H" + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +template<class Face> +inline Foam::label Foam::fileFormats::ABAQUSsurfaceFormat<Face>::writeShell +( + Ostream& os, + const Face& f, + label elemId, + const std::string& elsetName, + bool header +) +{ + const label n = f.size(); + + if (n == 4) + { + if (header) + { + os << "*ELEMENT, TYPE=S4"; + + if (!elsetName.empty()) + { + os << ", ELSET=" << elsetName; + } + os << nl; + } + + os << " " + << (++elemId) << ',' + << (f[0] + 1) << ',' + << (f[1] + 1) << ',' + << (f[2] + 1) << ',' + << (f[3] + 1) << nl; + } + else + { + if (header) + { + os << "*ELEMENT, TYPE=S3"; + + if (!elsetName.empty()) + { + os << ", ELSET=" << elsetName; + } + os << nl; + } + + if (n == 3) + { + os << " " + << (++elemId) << ',' + << (f[0] + 1) << ',' + << (f[1] + 1) << ',' + << (f[2] + 1) << nl; + } + else + { + // simple triangulation about f[0]. + // better triangulation should have been done before + + for (label fp1 = 1; fp1 < f.size() - 1; ++fp1) + { + const label fp2 = f.fcIndex(fp1); + + os << " " + << (++elemId) << ',' + << (f[0] + 1) << ',' + << (f[fp1] + 1) << ',' + << (f[fp2] + 1) << nl; + } + } + } + + return elemId; +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +template<class Face> +Foam::fileFormats::ABAQUSsurfaceFormat<Face>::ABAQUSsurfaceFormat +( + const fileName& filename +) +{ + read(filename); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +template<class Face> +bool Foam::fileFormats::ABAQUSsurfaceFormat<Face>::read +( + const fileName& filename +) +{ + // Clear everything + this->clear(); + + IFstream is(filename); + if (!is.good()) + { + FatalErrorInFunction + << "Cannot read file " << filename << nl + << exit(FatalError); + } + + + ABAQUSCore::readHelper reader(true); + + reader.read(is); + + // This needs more work + + // No solids + reader.purge_solids(); + reader.compact_nodes(); + + + // Convert connectivity to faces + + DynamicList<Face> dynFaces(reader.connectivity_.size()); + + for (labelList& conn : reader.connectivity_) + { + dynFaces.append(Face(std::move(conn))); + } + + + // Rationalize the zones (elset) + + // Only retain element sets that are actually used + labelHashSet elsetUsed(reader.elsetIds_); + + labelList newToOldZone(elsetUsed.sortedToc()); + + // Extra safety + if (newToOldZone.empty()) + { + newToOldZone.resize(1, Zero); + } + + Map<label> oldToNewZone(2*newToOldZone.size()); + + forAll(newToOldZone, zonei) + { + oldToNewZone.set(newToOldZone[zonei], zonei); + } + + wordList zoneNames(newToOldZone.size()); + labelList zoneSizes(newToOldZone.size(), Zero); + + forAllConstIters(reader.elsetMap_, iter) + { + const label zonei = oldToNewZone.lookup(iter.val(), -1); + + if (zonei >= 0) + { + zoneNames[zonei] = word::validate(iter.key()); + } + } + + // No empty strings + forAll(zoneNames, zonei) + { + if (zoneNames[zonei].empty()) + { + zoneNames[zonei] = surfZoneIdentifier::defaultName(zonei); + } + } + + // Steal the elset Ids for our zones + DynamicList<label> dynZones(std::move(reader.elsetIds_)); + + // Renumber elset -> zoneId and increment the count + for (label& zonei : dynZones) + { + zonei = oldToNewZone.lookup(zonei, 0); + + ++zoneSizes[zonei]; + } + + + // Transfer to normal lists + this->storedPoints().transfer(reader.points_); + + this->sortFacesAndStore + ( + dynFaces, + dynZones, + reader.elemIds_, + true // sorted + ); + + // Add zones (retaining empty ones) + this->addZones(zoneSizes, zoneNames); + this->addZonesToFaces(); // for labelledTri + + return true; +} + + +template<class Face> +void Foam::fileFormats::ABAQUSsurfaceFormat<Face>::write +( + const fileName& filename, + const MeshedSurfaceProxy<Face>& surf, + IOstreamOption streamOpt, + const dictionary& +) +{ + // ASCII only, allow output compression + streamOpt.format(IOstream::ASCII); + + const UList<point>& pointLst = surf.points(); + const UList<Face>& faceLst = surf.surfFaces(); + const UList<label>& faceMap = surf.faceMap(); + const UList<label>& elemIds = surf.faceIds(); + + // for no zones, suppress the group name + const surfZoneList zones = + ( + surf.surfZones().empty() + ? surfaceFormatsCore::oneZone(faceLst, "") + : surf.surfZones() + ); + + const bool useFaceMap = (surf.useFaceMap() && zones.size() > 1); + + // Possible to use faceIds? + bool useOrigFaceIds = + (!useFaceMap && elemIds.size() == faceLst.size()); + + if (useOrigFaceIds) + { + // Not possible with on-the-fly face decomposition + + for (const auto& f : faceLst) + { + if (f.size() > 4) + { + useOrigFaceIds = false; + break; + } + } + } + + + OFstream os(filename, streamOpt); + if (!os.good()) + { + FatalErrorInFunction + << "Cannot write file " << filename << nl + << exit(FatalError); + } + + + os << "*HEADING" << nl; + + os << nl + << "**" << nl + << "** Points" << nl + << "**" << nl; + + writePoints(os, pointLst); + + os << "**" << nl + << "** Faces" << nl + << "**" << nl + << nl; + + + // Simple tracking for change of element type/set + labelPair prevOutput(-1, -1); + + label faceIndex = 0; + label zoneIndex = 0; + label elemId = 0; + + for (const surfZone& zone : zones) + { + for (label nLocal = zone.size(); nLocal--; ++faceIndex) + { + const label facei = + (useFaceMap ? faceMap[faceIndex] : faceIndex); + + const Face& f = faceLst[facei]; + + if (useOrigFaceIds) + { + elemId = elemIds[facei]; + } + + const label n = f.size(); + + bool header = + (prevOutput.first() != n || prevOutput.second() != zoneIndex); + + if (header) + { + // Update values + prevOutput.first() = n; + prevOutput.second() = zoneIndex; + } + + elemId = writeShell(os, f, elemId, zone.name(), header); + } + + ++zoneIndex; + } + + os << "**" << nl + << "**" << nl; +} + + +// ************************************************************************* // diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.H b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.H new file mode 100644 index 0000000000000000000000000000000000000000..57b1ff4d41e23b891cbd3a7c4cd0baa435e66549 --- /dev/null +++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.H @@ -0,0 +1,144 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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::fileFormats::ABAQUSsurfaceFormat + +Description + Abaqus surface reader. + + Output stream options: + - ASCII only + - compression on/off + + Output dictionary options: ignored + +SourceFiles + ABAQUSsurfaceFormat.C + +\*---------------------------------------------------------------------------*/ + +#ifndef ABAQUSsurfaceFormat_H +#define ABAQUSsurfaceFormat_H + +#include "MeshedSurface.H" +#include "MeshedSurfaceProxy.H" +#include "UnsortedMeshedSurface.H" +#include "ABAQUSsurfaceFormatCore.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace fileFormats +{ + +/*---------------------------------------------------------------------------*\ + Class fileFormats::ABAQUSsurfaceFormat Declaration +\*---------------------------------------------------------------------------*/ + +template<class Face> +class ABAQUSsurfaceFormat +: + public MeshedSurface<Face>, + public ABAQUSsurfaceFormatCore +{ + // Private Member Functions + + //- Output S3 or S4 + inline static label writeShell + ( + Ostream& os, + const Face& f, + label elemId, //!< 0-based element Id + const std::string& elsetName, + bool header = true + ); + + +public: + + // Constructors + + //- Default construct + ABAQUSsurfaceFormat() = default; + + //- Read construct from file name + ABAQUSsurfaceFormat(const fileName& filename); + + + //- Destructor + virtual ~ABAQUSsurfaceFormat() = default; + + + // Static Member Functions + + //- Write surface mesh components by proxy + static void write + ( + const fileName& filename, + const MeshedSurfaceProxy<Face>& surf, + IOstreamOption streamOpt = IOstreamOption(), + const dictionary& /*unused*/ = dictionary::null + ); + + + // Member Functions + + //- Read from file + virtual bool read + ( + const fileName& filename + ); + + //- Write surface mesh to file + virtual void write + ( + const fileName& name, + IOstreamOption streamOpt = IOstreamOption(), + const dictionary& options = dictionary::null + ) const + { + write(name, MeshedSurfaceProxy<Face>(*this), streamOpt, options); + } +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace fileFormats +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#ifdef NoRepository + #include "ABAQUSsurfaceFormat.C" +#endif + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.C b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.C new file mode 100644 index 0000000000000000000000000000000000000000..4e2192dd6a5f51b1c3627ab0160326370080cf3a --- /dev/null +++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.C @@ -0,0 +1,39 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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 "ABAQUSsurfaceFormatCore.H" +#include "clock.H" +#include "regExp.H" +#include "IFstream.H" +#include "ListOps.H" +#include "stringOps.H" +#include "UIListStream.H" + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + + +// ************************************************************************* // diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.H b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.H new file mode 100644 index 0000000000000000000000000000000000000000..742fb706fa990fc31a580794f931018f9bfe30c3 --- /dev/null +++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatCore.H @@ -0,0 +1,76 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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::fileFormats::ABAQUSsurfaceFormatCore + +Description + Internal class used by the ABAQUSsurfaceFormat + +SourceFiles + ABAQUSsurfaceFormatCore.C + +\*---------------------------------------------------------------------------*/ + +#ifndef ABAQUSsurfaceFormatCore_H +#define ABAQUSsurfaceFormatCore_H + +#include "Fstream.H" +#include "Ostream.H" +#include "MeshedSurface.H" +#include "DynamicList.H" +#include "ABAQUSCore.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace fileFormats +{ + +/*---------------------------------------------------------------------------*\ + Class fileFormats::ABAQUSsurfaceFormatCore Declaration +\*---------------------------------------------------------------------------*/ + +class ABAQUSsurfaceFormatCore +: + public ABAQUSCore +{ +protected: + + // Protected Static Member Functions +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace fileFormats +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatRunTime.C b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatRunTime.C new file mode 100644 index 0000000000000000000000000000000000000000..6b429b36923711df774648e59fa978fd6df71e2b --- /dev/null +++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormatRunTime.C @@ -0,0 +1,82 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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 "ABAQUSsurfaceFormat.H" + +#include "addToRunTimeSelectionTable.H" +#include "addToMemberFunctionSelectionTable.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ +namespace fileFormats +{ + +// Read MeshedSurface - tag as .abq for now (.inp name conflict with starcd) +addNamedTemplatedToRunTimeSelectionTable +( + MeshedSurface, + ABAQUSsurfaceFormat, + face, + fileExtension, + abq +); + +// Write with MeshedSurfaceProxy +addNamedTemplatedToMemberFunctionSelectionTable +( + MeshedSurfaceProxy, + ABAQUSsurfaceFormat, + face, + write, + fileExtension, + abq +); +addNamedTemplatedToMemberFunctionSelectionTable +( + MeshedSurfaceProxy, + ABAQUSsurfaceFormat, + triFace, + write, + fileExtension, + abq +); +addNamedTemplatedToMemberFunctionSelectionTable +( + MeshedSurfaceProxy, + ABAQUSsurfaceFormat, + labelledTri, + write, + fileExtension, + abq +); + +} +} + +// ************************************************************************* // diff --git a/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C new file mode 100644 index 0000000000000000000000000000000000000000..2ad3dc5f416e74af0747603ca884f2d4f5bff6ab --- /dev/null +++ b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C @@ -0,0 +1,341 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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 "abaqusSurfaceWriter.H" +#include "ABAQUSCore.H" +#include "Pair.H" +#include "IOmanip.H" +#include "OSspecific.H" +#include "surfaceWriterMethods.H" +#include "addToRunTimeSelectionTable.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ +namespace surfaceWriters +{ + defineTypeName(abaqusWriter); + addToRunTimeSelectionTable(surfaceWriter, abaqusWriter, word); + addToRunTimeSelectionTable(surfaceWriter, abaqusWriter, wordDict); +} +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Field writing implementation +#include "abaqusSurfaceWriterImpl.C" + +// Field writing methods +defineSurfaceWriterWriteFields(Foam::surfaceWriters::abaqusWriter); + + +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Write connectivity as CSV list +inline static void writeConnectivity +( + Ostream& os, + const label elemId, + const labelUList& elem +) +{ + os << " " << elemId; + + for (const label vert : elem) + { + os << ", " << (vert + 1); + } + + os << nl; +} + +} // End namespace Foam + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +void Foam::surfaceWriters::abaqusWriter::writeFace +( + Ostream& os, + const labelUList& f, + const label elemId, + const label propId, + bool header +) const +{ + // Only called with 3 or 4 points! + + if (header) + { + os << "*ELEMENT, TYPE=S" << f.size(); + + if (propId >= 0) + { + os << ", ELSET=_" << propId; + } + + os << nl; + } + + writeConnectivity(os, elemId, f); +} + + +void Foam::surfaceWriters::abaqusWriter::writeGeometry +( + Ostream& os, + const meshedSurf& surf, + labelList& decompOffsets, + DynamicList<face>& decompFaces +) const +{ + const pointField& points = surf.points(); + const faceList& faces = surf.faces(); + const labelList& zones = surf.zoneIds(); + const labelList& elemIds = surf.faceIds(); + + // Possible to use faceIds? + bool useOrigFaceIds = (elemIds.size() == faces.size()); + + if (useOrigFaceIds) + { + // Not possible with on-the-fly face decomposition + for (const auto& f : faces) + { + if (f.size() > 4) + { + useOrigFaceIds = false; + break; + } + } + } + + + os << "*HEADING" << nl; + + os << nl + << "**" << nl + << "** Points" << nl + << "**" << nl; + + fileFormats::ABAQUSCore::writePoints(os, points, geometryScale_); + + + // Write faces, with on-the-fly decomposition (triangulation) + decompOffsets.resize(faces.size()+1); + decompFaces.clear(); + + decompOffsets[0] = 0; // The first offset is always zero + + os << "**" << nl + << "** Faces" << nl + << "**" << nl + << nl; + + + // Simple tracking for change of element type/set + labelPair prevOutput(-1, -1); + + label elemId = 0; // The element-id + forAll(faces, facei) + { + const face& f = faces[facei]; + + if (useOrigFaceIds) + { + // When available and not decomposed + elemId = elemIds[facei]; + } + + // 1-offset for PID + const label propId = 1 + (facei < zones.size() ? zones[facei] : 0); + + const label n = f.size(); + + bool header = + (prevOutput.first() != n || prevOutput.second() != propId); + + if (header) + { + // Update values + prevOutput.first() = n; + prevOutput.second() = propId; + } + + if (n == 3 || n == 4) + { + writeFace(os, f, ++elemId, propId, header); + } + else + { + // Decompose into tris + prevOutput.first() = 3; + + f.triangles(points, decompFaces); + + for + ( + label decompi = decompOffsets[facei]; + decompi < decompFaces.size(); + ++decompi + ) + { + writeFace + ( + os, + decompFaces[decompi], + ++elemId, + propId, + header + ); + + header = false; + } + } + + // The end offset, which is the next begin offset + decompOffsets[facei+1] = decompFaces.size(); + } + + os << "**" << nl + << "**" << nl; +} + + +Foam::Ostream& Foam::surfaceWriters::abaqusWriter::writeFooter +( + Ostream& os, + const meshedSurf& surf +) const +{ + return os; +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::surfaceWriters::abaqusWriter::abaqusWriter() +: + surfaceWriter(), + geometryScale_(1), + fieldScale_(), + noGeometry_(false) +{} + + +Foam::surfaceWriters::abaqusWriter::abaqusWriter +( + const dictionary& options +) +: + surfaceWriter(options), + geometryScale_(options.getOrDefault<scalar>("scale", 1)), + fieldScale_(options.subOrEmptyDict("fieldScale")), + noGeometry_(options.getOrDefault("noGeometry", false)) +{} + + +Foam::surfaceWriters::abaqusWriter::abaqusWriter +( + const meshedSurf& surf, + const fileName& outputPath, + bool parallel, + const dictionary& options +) +: + abaqusWriter(options) +{ + open(surf, outputPath, parallel); +} + + +Foam::surfaceWriters::abaqusWriter::abaqusWriter +( + const pointField& points, + const faceList& faces, + const fileName& outputPath, + bool parallel, + const dictionary& options +) +: + abaqusWriter(options) +{ + open(points, faces, outputPath, parallel); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +Foam::fileName Foam::surfaceWriters::abaqusWriter::write() +{ + checkOpen(); + + // Geometry: rootdir/<TIME>/surfaceName.abq + + fileName outputFile = outputPath_; + if (useTimeDir() && !timeName().empty()) + { + // Splice in time-directory + outputFile = outputPath_.path() / timeName() / outputPath_.name(); + } + outputFile.ext("abq"); + + if (verbose_) + { + Info<< "Writing abaqus geometry to " << outputFile << endl; + } + + + const meshedSurf& surf = surface(); + + if (Pstream::master() || !parallel_) + { + if (!isDir(outputFile.path())) + { + mkDir(outputFile.path()); + } + + OFstream os(outputFile); + + labelList decompOffsets; + DynamicList<face> decompFaces; + + writeGeometry(os, surf, decompOffsets, decompFaces); + + writeFooter(os, surf) << nl; + } + + wroteGeom_ = true; + return outputFile; +} + + +// ************************************************************************* // diff --git a/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.H b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.H new file mode 100644 index 0000000000000000000000000000000000000000..e9fafc8d2dd286d764909c1dfab79dc210975e1d --- /dev/null +++ b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.H @@ -0,0 +1,208 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2012-2016 OpenFOAM Foundation + Copyright (C) 2015-2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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::surfaceWriters::abaqusWriter + +Description + A surface writer for the ABAQUS file format - both surface mesh and fields + + The formatOptions for abaqus: + \table + Property | Description | Required | Default + scale | output geometry scaling | no | 1 + fieldScale | output field scaling (dictionary) | no | empty + noGeometry | BETA (suppress geometry output) | no | false + \endtable + + For example, + \verbatim + formatOptions + { + abaqus + { + scale 1000; // [m] -> [mm] + fieldScale + { + "p.*" 0.01; // [Pa] -> [mbar] + } + } + } + \endverbatim + + \heading Output file locations + + Needs to be adjusted. Currently writes output in raw format. + +Note + Output variable scaling does not apply to integer types such as Ids. + +SourceFiles + abaqusSurfaceWriter.C + abaqusSurfaceWriterImpl.C + +\*---------------------------------------------------------------------------*/ + +#ifndef abaqusSurfaceWriter_H +#define abaqusSurfaceWriter_H + +#include "surfaceWriter.H" +#include "ABAQUSCore.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace surfaceWriters +{ + +/*---------------------------------------------------------------------------*\ + Class abaqusWriter Declaration +\*---------------------------------------------------------------------------*/ + +class abaqusWriter +: + public surfaceWriter +{ + // Private Data + + //- Output geometry scaling + const scalar geometryScale_; + + //- Output field scaling + const dictionary fieldScale_; + + //- BETA feature + bool noGeometry_; + + + // Private Member Functions + + //- Write face element (tri/quad) + void writeFace + ( + Ostream& os, + const labelUList& f, + const label elemId, //!< 1-based Element Id + const label propId, //!< 1-based Property Id + const bool header = true + ) const; + + //- Write the surface mesh geometry, tracking face decomposition + // + // \param decompOffsets begin/end offsets (size+1) into decompFaces + // \param decompFaces Non tri/quad decomposed into triangles + void writeGeometry + ( + Ostream& os, + const meshedSurf& surf, + labelList& decompOffsets, + DynamicList<face>& decompFaces + ) const; + + //- Write the footer information + Ostream& writeFooter(Ostream& os, const meshedSurf& surf) const; + + //- Write a face-based value + template<class Type> + Ostream& writeFaceValue + ( + Ostream& os, + const Type& value, + const label elemId //!< 1-based Element Id + ) const; + + //- Templated write operation + template<class Type> + fileName writeTemplate + ( + const word& fieldName, //!< Name of field + const Field<Type>& localValues //!< Local field values to write + ); + + +public: + + //- Declare type-name, virtual type (with debug switch) + TypeNameNoDebug("abaqus"); + + + // Constructors + + //- Default construct + abaqusWriter(); + + //- Construct with some output options + explicit abaqusWriter(const dictionary& options); + + //- Construct from components + abaqusWriter + ( + const meshedSurf& surf, + const fileName& outputPath, + bool parallel = Pstream::parRun(), + const dictionary& options = dictionary() + ); + + //- Construct from components + abaqusWriter + ( + const pointField& points, + const faceList& faces, + const fileName& outputPath, + bool parallel = Pstream::parRun(), + const dictionary& options = dictionary() + ); + + + //- Destructor + virtual ~abaqusWriter() = default; + + + // Member Functions + + //- Write surface geometry to file. + virtual fileName write(); // override + + declareSurfaceWriterWriteMethod(label); + declareSurfaceWriterWriteMethod(scalar); + declareSurfaceWriterWriteMethod(vector); + declareSurfaceWriterWriteMethod(sphericalTensor); + declareSurfaceWriterWriteMethod(symmTensor); + declareSurfaceWriterWriteMethod(tensor); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace surfaceWriters +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/surfMesh/writers/abaqus/abaqusSurfaceWriterImpl.C b/src/surfMesh/writers/abaqus/abaqusSurfaceWriterImpl.C new file mode 100644 index 0000000000000000000000000000000000000000..9258bc424600687867e2c087ba2fa5e40aa71366 --- /dev/null +++ b/src/surfMesh/writers/abaqus/abaqusSurfaceWriterImpl.C @@ -0,0 +1,261 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 OpenCFD Ltd. +------------------------------------------------------------------------------- +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 "OFstream.H" +#include "IOmanip.H" +#include "OSspecific.H" + +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ + // Emit each component + template<class Type> + static inline void writeData(Ostream& os, const Type& val) + { + for (direction cmpt=0; cmpt < pTraits<Type>::nComponents; ++cmpt) + { + os << ' ' << component(val, cmpt); + } + os << nl; + } + +} // End namespace Foam + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +template<class Type> +Foam::Ostream& Foam::surfaceWriters::abaqusWriter::writeFaceValue +( + Ostream& os, + const Type& value, + const label elemId +) const +{ + os << elemId; + writeData(os, value); + + return os; +} + + +template<class Type> +Foam::fileName Foam::surfaceWriters::abaqusWriter::writeTemplate +( + const word& fieldName, + const Field<Type>& localValues +) +{ + checkOpen(); + + // Field: rootdir/<TIME>/field/surfaceName.abq + + fileName outputFile = outputPath_.path(); + if (useTimeDir() && !timeName().empty()) + { + // Splice in time-directory + outputFile /= timeName(); + } + outputFile /= fieldName / outputPath_.name(); + outputFile.ext("raw"); // stop-gap + + + // Output scaling for the variable, but not for integer types. + // could also solve with clever templating + + const scalar varScale = + ( + std::is_integral<Type>::value + ? scalar(1) + : fieldScale_.getOrDefault<scalar>(fieldName, 1) + ); + + if (verbose_) + { + Info<< "Writing field " << fieldName; + if (!equal(varScale, 1)) + { + Info<< " (scaling " << varScale << ')'; + } + Info<< " to " << outputFile << endl; + } + + + // geometry merge() implicit + tmp<Field<Type>> tfield = mergeField(localValues); + + const meshedSurf& surf = surface(); + + if (Pstream::master() || !parallel_) + { + const auto& values = tfield(); + + if (!isDir(outputFile.path())) + { + mkDir(outputFile.path()); + } + + // const scalar timeValue(0); + + + // Additional bookkeeping for decomposing non tri/quad + labelList decompOffsets; + DynamicList<face> decompFaces; + + // Separate geometry + + if (noGeometry_) + { + fileFormats::ABAQUSCore::faceDecomposition + ( + surf.points(), + surf.faces(), + decompOffsets, + decompFaces + ); + } + else + { + OFstream osGeom(outputFile.lessExt().ext("abq")); + writeGeometry(osGeom, surf, decompOffsets, decompFaces); + } + + // Raw output values. To be improved + + OFstream os(outputFile); + + if (verbose_) + { + Info<< "Writing raw (abaqus) file to " << os.name() << endl; + // Info<< "Writing raw abaqus file to " << os.name() << endl; + } + + + // Regular (undecomposed) faces + const faceList& faces = surf.faces(); + const labelList& elemIds = surf.faceIds(); + + + // Possible to use faceIds? + // Not possible with on-the-fly face decomposition + const bool useOrigFaceIds = + ( + elemIds.size() == faces.size() + && decompFaces.empty() + ); + + + label elemId = 0; + + if (this->isPointData()) + { + forAll(faces, facei) + { + if (useOrigFaceIds) + { + // When available and not decomposed + elemId = elemIds[facei]; + } + + const label beginElemId = elemId; + + // Any face decomposition + for + ( + label decompi = decompOffsets[facei]; + decompi < decompOffsets[facei+1]; + ++decompi + ) + { + const face& f = decompFaces[decompi]; + + Type v = Zero; + for (const label verti : f) + { + v += values[verti]; + } + v *= (varScale / f.size()); + + writeFaceValue(os, v, ++elemId); + } + + + // Face not decomposed + if (beginElemId == elemId) + { + const face& f = faces[facei]; + + Type v = Zero; + for (const label verti : f) + { + v += values[verti]; + } + v *= (varScale / f.size()); + + writeFaceValue(os, v, ++elemId); + } + } + } + else + { + auto valIter = values.cbegin(); + + forAll(faces, facei) + { + if (useOrigFaceIds) + { + // When available and not decomposed + elemId = elemIds[facei]; + } + + const Type v(varScale * *valIter); + ++valIter; + + label nValues = + max + ( + label(1), + (decompOffsets[facei+1] - decompOffsets[facei]) + ); + + while (nValues--) + { + writeFaceValue(os, v, ++elemId); + } + } + } + + writeFooter(os, surf); + } + + wroteGeom_ = true; + return outputFile; +} + + +// ************************************************************************* //