From 1d398d8cae95e65c5e8273a9db3a8a7a209c584d Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Tue, 8 Sep 2020 14:49:34 +0200 Subject: [PATCH 1/8] ENH: handle abaqus SURFACE input (#1600) - read surfaces which are defined in terms of solid element sides. Eg, ``` *ELEMENT, TYPE=C3D4, ELSET=... 1, ... 2, ... *SURFACE, NAME=Things, TYPE=ELEMENT 1, S1 2, S1 ``` The element and side number are encoded as a synthetic face id according to -(10 * elemId + sideNum) but the underlying solid geometry is discarded, since there is no reasonable way to pass it through the surface sampling mechanism. --- src/fileFormats/abaqus/ABAQUSCore.C | 191 ++++++++++++++++-- src/fileFormats/abaqus/ABAQUSCore.H | 66 +++++- .../abaqus/ABAQUSsurfaceFormat.C | 1 + 3 files changed, 242 insertions(+), 16 deletions(-) diff --git a/src/fileFormats/abaqus/ABAQUSCore.C b/src/fileFormats/abaqus/ABAQUSCore.C index 356bffa3c1..c99f217b03 100644 --- a/src/fileFormats/abaqus/ABAQUSCore.C +++ b/src/fileFormats/abaqus/ABAQUSCore.C @@ -30,8 +30,9 @@ License #include "ListOps.H" #include "stringOps.H" #include "UIListStream.H" - -#undef Foam_readAbaqusSurface +#include "cellModel.H" +#include +#include // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -319,11 +320,10 @@ Foam::fileFormats::ABAQUSCore::readHelper::readPoints const label initialCount = points_.size(); char sep; // Comma separator (dummy) + string line; label id; point p; - string line; - // Read nodes (points) until next "*Section" while (is.peek() != '*' && is.peek() != EOF) { @@ -368,10 +368,10 @@ Foam::fileFormats::ABAQUSCore::readHelper::readElements const label initialCount = elemTypes_.size(); char sep; // Comma separator (dummy) - label id; - labelList elemNodes(nNodes, Zero); - string line; + label id; + + labelList elemNodes(nNodes, Zero); // Read element connectivity until next "*Section" @@ -403,6 +403,130 @@ Foam::fileFormats::ABAQUSCore::readHelper::readElements } +Foam::label +Foam::fileFormats::ABAQUSCore::readHelper::readSurfaceElements +( + ISstream& is, + const label setId +) +{ + // Info<< "*Surface" << nl; + + // Models for supported solids (need to face mapping) + const cellModel& tet = cellModel::ref(cellModel::TET); + const cellModel& prism = cellModel::ref(cellModel::PRISM); + const cellModel& hex = cellModel::ref(cellModel::HEX); + + // Face mapping from Abaqus cellModel to OpenFOAM cellModel + const auto& abqToFoamFaceMap = abaqusToFoamFaceAddr(); + + const label initialCount = elemTypes_.size(); + + char sep; // Comma separator (dummy) + string line; + label id; + + // Read until next "*Section" + + // Parse for elemId, sideId. + // Eg, "1235, S1" + while (is.peek() != '*' && is.peek() != EOF) + { + is >> id >> sep; + is.getLine(line); + + const word sideName(word::validate(stringOps::upper(line))); + + if + ( + sideName.size() != 2 + || sideName[0] != 'S' + || !std::isdigit(sideName[1]) + ) + { + Info<< "Abaqus reader: unsupported surface element side " + << id << ", " << sideName << nl; + continue; + } + + const label index = elemIds_.find(id); + if (id <= 0 || index < 0) + { + Info<< "Abaqus reader: unsupported surface element " + << id << nl; + continue; + } + + const auto faceIdIter = abqToFoamFaceMap.cfind(elemTypes_[index]); + if (!faceIdIter.found()) + { + Info<< "Abaqus reader: reject non-solid shape: " << nl; + } + + // The abaqus element side number (1-based) + const label sideNum = (sideName[1] - '0'); + + const label foamFaceNum = (*faceIdIter)[sideNum - 1]; + + const labelList& connect = connectivity_[index]; + + // Nodes for the derived shell element + labelList elemNodes; + + switch (elemTypes_[index]) + { + case shapeType::abaqusTet: + { + elemNodes = labelList(connect, tet.modelFaces()[foamFaceNum]); + break; + } + case shapeType::abaqusPrism: + { + elemNodes = labelList(connect, prism.modelFaces()[foamFaceNum]); + break; + } + case shapeType::abaqusHex: + { + elemNodes = labelList(connect, hex.modelFaces()[foamFaceNum]); + break; + } + default: + break; + } + + enum shapeType shape = shapeType::abaqusUnknownShape; + + if (elemNodes.size() == 3) + { + shape = shapeType::abaqusTria; + } + else if (elemNodes.size() == 4) + { + shape = shapeType::abaqusQuad; + } + else + { + // Cannot happen + FatalErrorInFunction + << "Could not map face side for " + << id << ", " << sideName << nl + << exit(FatalError); + } + + // Synthesize face Id from solid element Id and side Id + const label newElemId = ABAQUSCore::encodeSolidId(id, sideNum); + + // Further checks? + connectivity_.append(std::move(elemNodes)); + elemTypes_.append(shape); + elemIds_.append(newElemId); + elsetIds_.append(setId); + } + + return (elemTypes_.size() - initialCount); +} + + void Foam::fileFormats::ABAQUSCore::readHelper::read ( ISstream& is @@ -427,9 +551,11 @@ void Foam::fileFormats::ABAQUSCore::readHelper::read // Some abaqus files use upper-case or mixed-case for section names, // convert all to upper-case for ease. - string upperLine(stringOps::upper(line)); + const string upperLine(stringOps::upper(line)); + // // "*Nodes" section + // if (upperLine.starts_with("*NODE")) { // Ignore "NSET=...", we cannot do anything useful with it @@ -446,14 +572,16 @@ void Foam::fileFormats::ABAQUSCore::readHelper::read continue; } + // // "*Element" section + // if (upperLine.starts_with("*ELEMENT,")) { // Must have "TYPE=..." - auto elemTypeName = getIdentifier("TYPE", line); + const string elemTypeName(getIdentifier("TYPE", line)); // May have "ELSET=..." on the same line - string elsetName(getIdentifier("ELSET", line)); + const string elsetName(getIdentifier("ELSET", line)); const shapeType shape(getElementType(elemTypeName)); @@ -485,18 +613,42 @@ void Foam::fileFormats::ABAQUSCore::readHelper::read continue; } - + // // "*Surface" section + // if (upperLine.starts_with("*SURFACE,")) { - #ifdef Foam_readAbaqusSurface + // Require "NAME=..." on the same line + const string elsetName(getIdentifier("NAME", line)); + + // May have "TYPE=..." on the same line. + // If missing, default is ELEMENT. + const string surfTypeName(getIdentifier("TYPE", line)); + + if + ( + !surfTypeName.empty() + && stringOps::upper(surfTypeName) != "ELEMENT" + ) + { + Info<< "Reading abaqus surface type " + << surfTypeName << " is not implemented" << nl; + continue; + } + + // Treat like an element set + const label elsetId = addNewElset(elsetName); skipComments(is); - #else - Info<< "Reading of abaqus surfaces not implemented" << nl; - #endif + nread = readSurfaceElements(is, elsetId); + if (verbose_) + { + InfoErr + << "Read " << nread << " *SURFACE entries for " + << elsetName << nl; + } continue; } } @@ -623,6 +775,15 @@ void Foam::fileFormats::ABAQUSCore::readHelper::compact_nodes() } +void Foam::fileFormats::ABAQUSCore::readHelper::renumber_elements_1to0() +{ + for (label& elemId : elemIds_) + { + renumber0_elemId(elemId); + } +} + + void Foam::fileFormats::ABAQUSCore::writePoints ( Ostream& os, diff --git a/src/fileFormats/abaqus/ABAQUSCore.H b/src/fileFormats/abaqus/ABAQUSCore.H index 3f99516c85..4149d59621 100644 --- a/src/fileFormats/abaqus/ABAQUSCore.H +++ b/src/fileFormats/abaqus/ABAQUSCore.H @@ -90,7 +90,6 @@ SourceFiles namespace Foam { - namespace fileFormats { @@ -148,11 +147,62 @@ public: return (tag & 0x80); } + //- Is a combined (synthetic) face Id? + inline static bool isEncodedSolidId(const label combinedId) + { + return (combinedId < 0); + } + + //- Combine solid element Id and side Id into synthetic face Id + inline static label encodeSolidId(const label id, const label side) + { + return -(10 * id + side); + } + + //- Entangle solid element id from synthetic face Id + inline static label decodeSolidElementId(const label combinedId) + { + return + ( + isEncodedSolidId(combinedId) + ? (-combinedId / 10) + : combinedId + ); + } + + //- Entangle solid side id from synthetic face Id + //- Synthesize faceId from solid element Id and sideId + inline static label decodeSolidSideNum(const label combinedId) + { + return + ( + isEncodedSolidId(combinedId) + ? (-combinedId % 10) + : 0 + ); + } + protected: // Protected Member Functions + //- From 1-based to 0-based + inline static void renumber0_elemId(label& elemId) + { + if (isEncodedSolidId(elemId)) + { + // Eg, + // 1-based (solid 4, side 2) is -42 + // 0-based (solid 3, side 2) is -32 + elemId += 10; + } + else + { + --elemId; + } + } + //- Face addressing from ABAQUS faces to OpenFOAM faces. // For hex, prism, tet primitive shapes. static const Map& abaqusToFoamFaceAddr(); @@ -249,12 +299,26 @@ protected: const label setId = 0 ); + //- Read elements within an "*Surface" section. + // If the shape is known/supported, appends to + // connectivity, elemType, elemIds lists. + // + // \return the number of elements read + label readSurfaceElements + ( + ISstream& is, + const label setId = 0 + ); + //- Remove non-shell elements and compact the points void purge_solids(); //- Compact unused points and relabel connectivity void compact_nodes(); + + //- Renumber elements from 1-based to 0-based + void renumber_elements_1to0(); }; diff --git a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C index 72ebcfc65e..cfcd5357dc 100644 --- a/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C +++ b/src/surfMesh/surfaceFormats/abaqus/ABAQUSsurfaceFormat.C @@ -154,6 +154,7 @@ bool Foam::fileFormats::ABAQUSsurfaceFormat::read // No solids reader.purge_solids(); reader.compact_nodes(); + reader.renumber_elements_1to0(); // Convert 1-based -> 0-based // Convert connectivity to faces From f82af7cb2abdba8175ab5e270f76525bf761e6ad Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Mon, 14 Sep 2020 16:39:36 +0200 Subject: [PATCH 2/8] ENH: initial support for abaqus surface sampled output (#1600) - supports geometry and field-specific scaling, separate geometry and fields. Beta-feature for suppressing geometry output entirely. --- src/surfMesh/Make/files | 1 + .../writers/abaqus/abaqusSurfaceWriter.C | 355 ++++++++++++++++++ .../writers/abaqus/abaqusSurfaceWriter.H | 239 ++++++++++++ .../writers/abaqus/abaqusSurfaceWriterImpl.C | 308 +++++++++++++++ 4 files changed, 903 insertions(+) create mode 100644 src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C create mode 100644 src/surfMesh/writers/abaqus/abaqusSurfaceWriter.H create mode 100644 src/surfMesh/writers/abaqus/abaqusSurfaceWriterImpl.C diff --git a/src/surfMesh/Make/files b/src/surfMesh/Make/files index 7c68022eb8..eac20cc94a 100644 --- a/src/surfMesh/Make/files +++ b/src/surfMesh/Make/files @@ -63,6 +63,7 @@ writers = writers $(writers)/surfaceWriter.C $(writers)/caching/surfaceWriterCaching.C +$(writers)/abaqus/abaqusSurfaceWriter.C $(writers)/boundaryData/boundaryDataSurfaceWriter.C $(writers)/ensight/ensightSurfaceWriter.C $(writers)/foam/foamSurfaceWriter.C diff --git a/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C new file mode 100644 index 0000000000..8932d90684 --- /dev/null +++ b/src/surfMesh/writers/abaqus/abaqusSurfaceWriter.C @@ -0,0 +1,355 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / 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 . + +\*---------------------------------------------------------------------------*/ + +#include "abaqusSurfaceWriter.H" +#include "ABAQUSCore.H" +#include "IOmanip.H" +#include "ListOps.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& 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() + && !ListOps::found(elemIds, lessOp1