diff --git a/applications/utilities/mesh/conversion/fireToFoam/Make/files b/applications/utilities/mesh/conversion/fireToFoam/Make/files new file mode 100644 index 0000000000..a8734855d0 --- /dev/null +++ b/applications/utilities/mesh/conversion/fireToFoam/Make/files @@ -0,0 +1,3 @@ +fireToFoam.C + +EXE = $(FOAM_APPBIN)/fireToFoam diff --git a/applications/utilities/mesh/conversion/fireToFoam/Make/options b/applications/utilities/mesh/conversion/fireToFoam/Make/options new file mode 100644 index 0000000000..e3af2fe661 --- /dev/null +++ b/applications/utilities/mesh/conversion/fireToFoam/Make/options @@ -0,0 +1,7 @@ +EXE_INC = \ + -I$(LIB_SRC)/meshTools/lnInclude \ + -I$(LIB_SRC)/conversion/lnInclude \ + -I$(LIB_SRC)/fileFormats/lnInclude + +EXE_LIBS = \ + -lconversion diff --git a/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C b/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C new file mode 100644 index 0000000000..0f7c32c701 --- /dev/null +++ b/applications/utilities/mesh/conversion/fireToFoam/fireToFoam.C @@ -0,0 +1,123 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +Application + fireToFoam + +Group + grpMeshConversionUtilities + +Description + Converts an AVL/FIRE polyhedral mesh to OPENFOAM + +Usage + \b fireToFoam [OPTION] firePolyMesh + + Options: + + - \param -ascii + Write in ASCII format instead of binary + + - \par -check + Perform edge checking + + - \par -scale \ + Specify an alternative geometry scaling factor. + The default is \b 1 (no scaling). + +\*---------------------------------------------------------------------------*/ + +#include "argList.H" +#include "Time.H" +#include "FIREMeshReader.H" +#include "checkFireEdges.H" + +using namespace Foam; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +int main(int argc, char *argv[]) +{ + argList::addNote + ( + "Convert AVL/FIRE polyhedral mesh to OPENFOAM format" + ); + + argList::noParallel(); + argList::validArgs.append("firePolyMesh"); + argList::addBoolOption + ( + "ascii", + "write in ASCII format instead of binary" + ); + argList::addBoolOption + ( + "check", + "perform edge checking as well" + ); + argList::addOption + ( + "scale", + "scale", + "geometry scaling factor - default is 1 (no scaling)" + ); + + + argList args(argc, argv); + Time runTime(args.rootPath(), args.caseName()); + + + // Binary output, unless otherwise specified + const IOstream::streamFormat format = + ( + args.optionFound("ascii") + ? IOstream::ASCII + : IOstream::BINARY + ); + + // increase the precision of the points data + IOstream::defaultPrecision(max(10u, IOstream::defaultPrecision())); + + + fileFormats::FIREMeshReader reader + ( + args[1], + // Default no scaling + args.optionLookupOrDefault("scale", 1.0) + ); + + + autoPtr mesh = reader.mesh(runTime); + reader.writeMesh(mesh(), format); + + + if (args.optionFound("check")) + { + checkFireEdges(mesh()); + } + + Info<< "\nEnd\n" << endl; + return 0; +} + +// ************************************************************************* // diff --git a/applications/utilities/mesh/conversion/foamToFireMesh/Make/files b/applications/utilities/mesh/conversion/foamToFireMesh/Make/files new file mode 100644 index 0000000000..0cc67f4f55 --- /dev/null +++ b/applications/utilities/mesh/conversion/foamToFireMesh/Make/files @@ -0,0 +1,3 @@ +foamToFireMesh.C + +EXE = $(FOAM_APPBIN)/foamToFireMesh diff --git a/applications/utilities/mesh/conversion/foamToFireMesh/Make/options b/applications/utilities/mesh/conversion/foamToFireMesh/Make/options new file mode 100644 index 0000000000..e3af2fe661 --- /dev/null +++ b/applications/utilities/mesh/conversion/foamToFireMesh/Make/options @@ -0,0 +1,7 @@ +EXE_INC = \ + -I$(LIB_SRC)/meshTools/lnInclude \ + -I$(LIB_SRC)/conversion/lnInclude \ + -I$(LIB_SRC)/fileFormats/lnInclude + +EXE_LIBS = \ + -lconversion diff --git a/applications/utilities/mesh/conversion/foamToFireMesh/foamToFireMesh.C b/applications/utilities/mesh/conversion/foamToFireMesh/foamToFireMesh.C new file mode 100644 index 0000000000..87ddd4dff9 --- /dev/null +++ b/applications/utilities/mesh/conversion/foamToFireMesh/foamToFireMesh.C @@ -0,0 +1,136 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +Application + foamToFireMesh + +Description + Reads an OpenFOAM mesh and writes an AVL/FIRE fpma format + +Usage + \b foamToFireMesh [OPTION] + + Options: + - \par -ascii + Write in ASCII format instead of binary + + - \par -scale \ + Specify an alternative geometry scaling factor. + The default is \b 1 (ie, no scaling). + +See also + Foam::cellTable, Foam::meshWriter and Foam::fileFormats::FIREMeshWriter + +\*---------------------------------------------------------------------------*/ + +#include "argList.H" +#include "timeSelector.H" +#include "Time.H" +#include "polyMesh.H" +#include "FIREMeshWriter.H" + +using namespace Foam; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// Main program: + +int main(int argc, char *argv[]) +{ + argList::addNote + ( + "read OpenFOAM mesh and write an AVL/FIRE fpma format" + ); + argList::noParallel(); + timeSelector::addOptions(); + + argList::addBoolOption + ( + "ascii", + "write in ASCII format instead of binary" + ); + argList::addOption + ( + "scale", + "factor", + "geometry scaling factor - default is 1 (none)" + ); + + #include "setRootCase.H" + #include "createTime.H" + + instantList timeDirs = timeSelector::select0(runTime, args); + + fileName exportName = meshWriter::defaultMeshName; + if (args.optionFound("case")) + { + exportName += '-' + args.globalCaseName(); + } + + + // write control options + // ~~~~~~~~~~~~~~~~~~~~~ + fileFormats::FIREMeshWriter::binary = !args.optionFound("ascii"); + + // default: rescale from [m] to [mm] + scalar scaleFactor = 1; + if (args.optionReadIfPresent("scale", scaleFactor)) + { + if (scaleFactor <= 0) + { + scaleFactor = 1; + } + } + + #include "createPolyMesh.H" + + forAll(timeDirs, timeI) + { + runTime.setTime(timeDirs[timeI], timeI); + + #include "getTimeIndex.H" + + polyMesh::readUpdateState state = mesh.readUpdate(); + + if (!timeI || state != polyMesh::UNCHANGED) + { + fileFormats::FIREMeshWriter writer(mesh, scaleFactor); + + fileName meshName(exportName); + if (state != polyMesh::UNCHANGED) + { + meshName += '_' + runTime.timeName(); + } + + writer.write(meshName); + } + + Info<< nl << endl; + } + + Info<< "End\n" << endl; + + return 0; +} + + +// ************************************************************************* // diff --git a/applications/utilities/mesh/conversion/foamToFireMesh/getTimeIndex.H b/applications/utilities/mesh/conversion/foamToFireMesh/getTimeIndex.H new file mode 100644 index 0000000000..11b8993f28 --- /dev/null +++ b/applications/utilities/mesh/conversion/foamToFireMesh/getTimeIndex.H @@ -0,0 +1,51 @@ +// Read time index from */uniform/time, but treat 0 and constant specially + + word timeName = "0"; + + if + ( + runTime.timeName() != runTime.constant() + && runTime.timeName() != "0" + ) + { + IOobject io + ( + "time", + runTime.timeName(), + "uniform", + runTime, + IOobject::READ_IF_PRESENT, + IOobject::NO_WRITE, + false + ); + + if (io.typeHeaderOk(true)) + { + IOdictionary timeObject + ( + IOobject + ( + "time", + runTime.timeName(), + "uniform", + runTime, + IOobject::MUST_READ_IF_MODIFIED, + IOobject::NO_WRITE, + false + ) + ); + + label index; + timeObject.lookup("index") >> index; + timeName = Foam::name(index); + } + else + { + timeName = runTime.timeName(); + // Info<< "skip ... missing entry " << io.objectPath() << endl; + // continue; + } + } + + Info<< "\nTime [" << timeName << "] = " << runTime.timeName() << nl; + diff --git a/src/conversion/Make/files b/src/conversion/Make/files index 19af831aeb..0266423e35 100644 --- a/src/conversion/Make/files +++ b/src/conversion/Make/files @@ -15,6 +15,10 @@ ensight/part/ensightPartCells.C ensight/part/ensightPartFaces.C ensight/part/ensightParts.C +fire/FIREMeshReader.C +fire/FIREMeshWriter.C +fire/checkFireEdges.C + starcd/STARCDMeshReader.C starcd/STARCDMeshWriter.C diff --git a/src/conversion/fire/FIREMeshReader.C b/src/conversion/fire/FIREMeshReader.C new file mode 100644 index 0000000000..ef8576cf5f --- /dev/null +++ b/src/conversion/fire/FIREMeshReader.C @@ -0,0 +1,549 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +\*---------------------------------------------------------------------------*/ + +#include "FIREMeshReader.H" +#include "wallPolyPatch.H" +#include "ListOps.H" +#include "IFstream.H" + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +Foam::word Foam::fileFormats::FIREMeshReader::validateWord +( + const std::string& str +) +{ + std::string::size_type ngood = 0; + bool prefix = false; + bool first = true; + + for + ( + std::string::const_iterator iter = str.begin(); + iter != str.end(); + ++iter + ) + { + if (word::valid(*iter)) + { + ++ngood; + if (first) + { + first = false; + + // start with a digit? need to prefix with '_' + if (isdigit(*iter)) + { + prefix = true; + } + } + } + } + + if (prefix) + { + ++ngood; + } + else if (ngood == str.size()) + { + return str; + } + + Foam::word out; + out.resize(ngood); + ngood = 0; + + Foam::word::iterator iter2 = out.begin(); + for + ( + std::string::const_iterator iter1 = str.begin(); + iter1 != str.end(); + ++iter1 + ) + { + register char c = *iter1; + + if (Foam::word::valid(c)) + { + if (prefix) + { + prefix = false; + *(iter2++) = '_'; + ++ngood; + } + *(iter2++) = c; + ++ngood; + } + } + + out.resize(ngood); + + return out; +} + + +void Foam::fileFormats::FIREMeshReader::readPoints +( + ISstream& is, + const scalar scaleFactor +) +{ + const label n = FIRECore::readPoints(is, points_); + // the above has FatalError if there are no points + + Info<< "Number of points = " << n << endl; + if (scaleFactor > 1.0 + SMALL || scaleFactor < 1.0 - SMALL) + { + points_ *= scaleFactor; + } +} + + +void Foam::fileFormats::FIREMeshReader::readFaces(ISstream& is) +{ + const label nFaces = getFireLabel(is); + Info<< "Number of faces = " << nFaces << endl; + meshFaces_.setSize(nFaces); + + if (nFaces > 0) + { + forAll(meshFaces_, faceI) + { + const label size = getFireLabel(is); + + face& f = meshFaces_[faceI]; + f.setSize(size); + forAll(f, fp) + { + f[fp] = getFireLabel(is); + } + + // flip in-place + f.flip(); + } + } + else + { + FatalErrorInFunction + << "no faces in file " << is.name() + << abort(FatalError); + } +} + + +void Foam::fileFormats::FIREMeshReader::readCells(ISstream& is) +{ + const label nCells = getFireLabel(is); + Info<< "Number of cells = " << nCells << endl; + + owner_.setSize(meshFaces_.size()); + neigh_.setSize(meshFaces_.size()); + + owner_ = -1; + neigh_ = -1; + + if (nCells > 0) + { + for (label cellI = 0; cellI < nCells; ++cellI) + { + const label nface = getFireLabel(is); + + for (label i = 0; i < nface; ++i) + { + const label faceI = getFireLabel(is); + + if (owner_[faceI] == -1) + { + owner_[faceI] = cellI; + } + else if (neigh_[faceI] == -1) + { + neigh_[faceI] = cellI; + } + else + { + Warning + << "bad cell connectivity for face " << faceI + << " on cell " << cellI + << endl; + } + } + } + } + else + { + FatalErrorInFunction + << "no cells in file " << is.name() + << abort(FatalError); + } + + cellTableId_.setSize(nCells); + cellTableId_ = -1; +} + + +void Foam::fileFormats::FIREMeshReader::readSelections(ISstream& is) +{ + const label nSelect = getFireLabel(is); + Info<< "Number of select = " << nSelect << endl; + + label nCellSelections = 0; + label nFaceSelections = 0; + + faceZoneId_.setSize(meshFaces_.size()); + faceZoneId_ = -1; + + DynamicList faceNames(32); + + for (label selI = 0; selI < nSelect; ++selI) + { + std::string name = getFireString(is); + const label selType = getFireLabel(is); + const label count = getFireLabel(is); + + if (selType == FIRECore::cellSelection) + { + // index starting at 1 + const label selId = ++nCellSelections; + + cellTable_.setName(selId, validateWord(name)); + cellTable_.setMaterial(selId, "fluid"); + + for (label i = 0; i < count; ++i) + { + const label cellId = getFireLabel(is); + + cellTableId_[cellId] = selId; + } + } + else if (selType == FIRECore::faceSelection) + { + // index starting at 0 + const label selId = nFaceSelections++; + + faceNames.append(validateWord(name)); + + for (label i = 0; i < count; ++i) + { + const label faceId = getFireLabel(is); + + faceZoneId_[faceId] = selId; + } + } + else + { + // discard other selection types (eg, nodes) + for (label i = 0; i < count; ++i) + { + getFireLabel(is); + } + } + } + + Info<< nFaceSelections << " face selections" << endl; + Info<< nCellSelections << " cell selections" << endl; + + // add extra for missed boundary faces + faceNames.append("__MISSED_FACES__"); + faceNames_.transfer(faceNames); +} + + +void Foam::fileFormats::FIREMeshReader::reorganize() +{ + nInternalFaces_ = 0; + + // pass 1: + // count internal faces and also swap owner <-> neigh as required + forAll(meshFaces_, faceI) + { + if (neigh_[faceI] != -1) + { + ++nInternalFaces_; + + if (owner_[faceI] > neigh_[faceI]) + { + Swap(owner_[faceI], neigh_[faceI]); + } + } + } + + label posInternal = 0; + label posExternal = nInternalFaces_; + + labelList oldToNew(meshFaces_.size(), -1); + + // pass 2: + // mapping to ensure proper division of internal / external + forAll(meshFaces_, faceI) + { + if (neigh_[faceI] == -1) + { + oldToNew[faceI] = posExternal++; + } + else + { + oldToNew[faceI] = posInternal++; + } + } + + inplaceReorder(oldToNew, meshFaces_); + inplaceReorder(oldToNew, owner_); + inplaceReorder(oldToNew, neigh_); + inplaceReorder(oldToNew, faceZoneId_); + + // determine the patch sizes - faceNames_ already has extra place for missed faces + const label zoneMissed = faceNames_.size() - 1; + patchSizes_.setSize(faceNames_.size()); + patchSizes_ = 0; + + patchStarts_.setSize(patchSizes_.size()); + patchStarts_ = 0; + + for (label faceI = nInternalFaces_; faceI < meshFaces_.size(); ++faceI) + { + label zoneI = faceZoneId_[faceI]; + if (zoneI == -1) + { + ++patchSizes_[zoneMissed]; + } + else + { + ++patchSizes_[zoneI]; + } + } + + if (patchSizes_[zoneMissed]) + { + Info<<"collecting " << patchSizes_[zoneMissed] + << " missed boundary faces to final patch" << endl; + } + + oldToNew = -1; + + // calculate the patch starts + { + label pos = nInternalFaces_; + + forAll(patchStarts_, patchI) + { + patchStarts_[patchI] = pos; + pos += patchSizes_[patchI]; + } + + forAll(patchSizes_, patchI) + { + patchSizes_[patchI] = 0; + } + } + + // reordering + for (label faceI = nInternalFaces_; faceI < meshFaces_.size(); ++faceI) + { + label patchI = faceZoneId_[faceI]; + if (patchI == -1) + { + oldToNew[faceI] = patchStarts_[zoneMissed] + patchSizes_[zoneMissed]; + ++patchSizes_[zoneMissed]; + } + else + { + oldToNew[faceI] = patchStarts_[patchI] + patchSizes_[patchI]; + ++patchSizes_[patchI]; + } + } + + // discard old information + faceZoneId_.clear(); + + inplaceReorder(oldToNew, meshFaces_); + inplaceReorder(oldToNew, owner_); + inplaceReorder(oldToNew, neigh_); + + //--- neigh_.setSize(nInternalFaces_); + + // finally reduce to the number of patches actually used + patchNames_.setSize(patchSizes_.size()); + oldToNew = -1; + + label nPatches = 0; + forAll(patchSizes_, patchI) + { + if (patchSizes_[patchI]) + { + patchNames_[nPatches] = faceNames_[patchI]; + + oldToNew[patchI] = nPatches; + ++nPatches; + } + } + + inplaceReorder(oldToNew, patchStarts_); + inplaceReorder(oldToNew, patchSizes_); + + patchStarts_.setSize(nPatches); + patchSizes_.setSize(nPatches); + patchNames_.setSize(nPatches); +} + + +void Foam::fileFormats::FIREMeshReader::addPatches(polyMesh& mesh) const +{ + // create patches + List newPatches(patchSizes_.size()); + + label meshFaceI = nInternalFaces_; + + forAll(patchStarts_, patchI) + { + Info<< "patch " << patchI + << " (start: " << meshFaceI << " size: " << patchSizes_[patchI] + << ") name: " << patchNames_[patchI] + << endl; + + // don't know anything better - just make it a wall + newPatches[patchI] = new polyPatch + ( + patchNames_[patchI], + patchSizes_[patchI], + meshFaceI, + patchI, + mesh.boundaryMesh(), + word::null + ); + + meshFaceI += patchSizes_[patchI]; + } + + mesh.addPatches(newPatches); +} + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +bool Foam::fileFormats::FIREMeshReader::readGeometry(const scalar scaleFactor) +{ + IOstream::streamFormat fmt = IOstream::ASCII; + + const word ext = geometryFile_.ext(); + bool supported = FIRECore::file3dExtensions.found(ext); + if (supported) + { + FIRECore::fileExt3d fireFileType = FIRECore::file3dExtensions[ext]; + if (fireFileType == FIRECore::POLY_ASCII) + { + fmt = IOstream::ASCII; + } + else if (fireFileType == FIRECore::POLY_BINARY) + { + fmt = IOstream::BINARY; + } + else + { + // compressed or something + supported = false; + } + } + + if (!supported) + { + FatalErrorInFunction + << "File-type '" << ext + << "' is not supported for reading as a FIRE mesh." << nl + << "If it is a compressed file, use gunzip first." + << abort(FatalError); + } + + IFstream is(geometryFile_, fmt, false); + + readPoints(is, scaleFactor); + readFaces(is); + readCells(is); + readSelections(is); + + return true; +} + +Foam::autoPtr Foam::fileFormats::FIREMeshReader::mesh +( + const objectRegistry& registry +) +{ + readGeometry(scaleFactor_); + reorganize(); + + Info<< "Creating a polyMesh" << endl; + + autoPtr mesh + ( + new polyMesh + ( + IOobject + ( + polyMesh::defaultRegion, + "constant", + registry + ), + xferMove(points_), + xferMove(meshFaces_), + xferMove(owner_), + xferMove(neigh_) + ) + ); + + // adding patches also checks the mesh + addPatches(mesh()); + + cellTable_.addCellZones(mesh(), cellTableId_); + + // addFaceZones(mesh()); + + return mesh; +} + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::fileFormats::FIREMeshReader::FIREMeshReader +( + const fileName& name, + const scalar scaleFactor +) +: + meshReader(name, scaleFactor), + owner_(0), + neigh_(0), + faceZoneId_(0), + faceNames_() +{} + + +// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // + +Foam::fileFormats::FIREMeshReader::~FIREMeshReader() +{} + + +// ************************************************************************* // diff --git a/src/conversion/fire/FIREMeshReader.H b/src/conversion/fire/FIREMeshReader.H new file mode 100644 index 0000000000..0c95cb5fa2 --- /dev/null +++ b/src/conversion/fire/FIREMeshReader.H @@ -0,0 +1,140 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +Class + Foam::fileFormats::FIREMeshReader + +Description + Read AVL/FIRE fpma, fpmb files. + +Note + Does not handle compressed versions (fpmaz, fpmbz) of these files. + +SourceFiles + FIREMeshReader.C + +\*---------------------------------------------------------------------------*/ + +#ifndef FIREMeshReader_H +#define FIREMeshReader_H + +#include "meshReader.H" +#include "FIRECore.H" +#include "labelList.H" +#include "IFstream.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +// forward declarations +class polyMesh; + +namespace fileFormats +{ + +/*---------------------------------------------------------------------------*\ + Class fileFormats::FIREMeshReader Declaration +\*---------------------------------------------------------------------------*/ + +class FIREMeshReader +: + public meshReader, + public FIRECore +{ + +protected: + + // Protected Data + + labelList owner_; + labelList neigh_; + + labelList faceZoneId_; + wordList faceNames_; + + + // Protected Member Functions + + //- Disallow default bitwise copy construct + FIREMeshReader(const FIREMeshReader&) = delete; + + //- Disallow default bitwise assignment + void operator=(const FIREMeshReader&) = delete; + + + //- Validate word (eg, avoid leading digits) + static word validateWord(const std::string&); + + + //- Read the mesh from the file(s) + virtual bool readGeometry(const scalar scaleFactor = 1.0); + + //- Read points from file + void readPoints(ISstream&, const scalar scaleFactor = 1.0); + + //- Read points from file + void readFaces(ISstream&); + + //- Read cell connectivities from file + void readCells(ISstream&); + + //- Read cell/face selections from file + void readSelections(ISstream&); + + //- + void reorganize(); + + void addPatches(polyMesh&) const; + + +public: + + // Constructors + + //- Construct by reading file, optionally with scaling + FIREMeshReader(const fileName&, const scalar scaleFactor = 1.0); + + + //- Destructor + virtual ~FIREMeshReader(); + + + // Member Functions + + //- Create and return polyMesh + virtual autoPtr mesh(const objectRegistry&); + +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace fileFormats +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/conversion/fire/FIREMeshWriter.C b/src/conversion/fire/FIREMeshWriter.C new file mode 100644 index 0000000000..29746005d0 --- /dev/null +++ b/src/conversion/fire/FIREMeshWriter.C @@ -0,0 +1,364 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +\*---------------------------------------------------------------------------*/ + +#include "FIREMeshWriter.H" +#include "Time.H" +#include "HashTable.H" +#include "OFstream.H" +#include "processorPolyPatch.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +bool Foam::fileFormats::FIREMeshWriter::binary = false; +bool Foam::fileFormats::FIREMeshWriter::compress = false; +bool Foam::fileFormats::FIREMeshWriter::prefixBoundary = true; + + +//! \cond fileScope +//- Output newline in ascii mode, no-op in binary mode +inline static void newline(Foam::OSstream& os) +{ + if (os.format() == Foam::IOstream::ASCII) + { + os << Foam::endl; + } +} + +//! \endcond + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + + +bool Foam::fileFormats::FIREMeshWriter::writeGeometry(OSstream& os) const +{ + const faceList& faces = mesh_.faces(); + const pointField& points = mesh_.points(); + const cellList& cells = mesh_.cells(); + + // Points + // ~~~~~~ + + // Set the precision of the points data to 10 + os.precision(10); + + Info<< "points: " << points.size() << endl; + putFireLabel(os, points.size()); + newline(os); + + forAll(points, ptI) + { + // scaling is normally 1 (ie, none) + putFirePoint(os, scaleFactor_ * points[ptI]); + } + newline(os); // readability + + + // Faces + // ~~~~~ + // OPENFOAM - faces normals are outward-facing. + // FIRE - faces normals are inward-facing. + + Info<< "faces: " << faces.size() << endl; + putFireLabel(os, faces.size()); + newline(os); + + forAll(faces, faceI) + { + // flip face + putFireLabels(os, faces[faceI].reverseFace()); + } + newline(os); // readability + + + // Cells + // ~~~~~ + + Info<< "cells: " << cells.size() << endl; + putFireLabel(os, cells.size()); + newline(os); + + forAll(cells, cellId) + { + putFireLabels(os, cells[cellId]); + } + newline(os); // readability + + return os.good(); +} + + +bool Foam::fileFormats::FIREMeshWriter::writeSelections(OSstream& os) const +{ + label nZones = 0; + label nPatches = 0; + + // remap name between patches and cell-zones conflicts! + + HashTable patchNames; + HashTable zoneNames; + + wordHashSet usedPatchNames; + wordHashSet usedZoneNames; + + // boundaries, skipping empty and processor patches + forAll(mesh_.boundaryMesh(), patchI) + { + const polyPatch& patch = mesh_.boundaryMesh()[patchI]; + if (patch.size() && !isA(patch)) + { + ++nPatches; + + const word oldName = patch.name(); + word newName; + if (prefixBoundary) + { + newName = "BND_" + oldName; + + if (usedPatchNames.found(newName)) + { + newName = "BND_patch" + ::Foam::name(patchI); + } + } + else + { + newName = oldName; + + if (usedPatchNames.found(newName)) + { + newName = "patch" + ::Foam::name(patchI); + } + } + + usedPatchNames.set(newName); + patchNames.set(patchI, newName); + } + } + + + // cellzones, skipping empty zones + forAll(mesh_.cellZones(), zoneI) + { + const cellZone& cZone = mesh_.cellZones()[zoneI]; + if (cZone.size()) + { + ++nZones; + + const word oldName = cZone.name(); + word newName = oldName; + + if (usedPatchNames.found(newName) || usedZoneNames.found(newName)) + { + newName = "CEL_zone" + ::Foam::name(zoneI); + } + + usedZoneNames.set(newName); + zoneNames.set(zoneI, newName); + } + } + + + // + // actually write things + // + + putFireLabel(os, (nZones + nPatches)); + newline(os); + + // do cell zones + forAll(mesh_.cellZones(), zoneI) + { + const cellZone& cZone = mesh_.cellZones()[zoneI]; + + if (cZone.size()) + { + Info<< "cellZone " << zoneI + << " (size: " << cZone.size() + << ") name: " << zoneNames[zoneI] << nl; + + putFireString(os, zoneNames[zoneI]); + putFireLabel(os, static_cast(FIRECore::cellSelection)); + newline(os); + + putFireLabels(os, cZone); + newline(os); // readability + } + } + + // do boundaries, skipping empty and processor patches + forAll(mesh_.boundaryMesh(), patchI) + { + const polyPatch& patch = mesh_.boundaryMesh()[patchI]; + if (patch.size() && !isA(patch)) + { + Info<< "patch " << patchI + << " (start: " << patch.start() << " size: " << patch.size() + << ") name: " << patchNames[patchI] + << endl; + + putFireString(os, patchNames[patchI]); + putFireLabel(os, static_cast(FIRECore::faceSelection)); + newline(os); + + putFireLabels(os, patch.size(), patch.start()); + newline(os); // readability + } + + newline(os); // readability + } + + return os.good(); +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::fileFormats::FIREMeshWriter::FIREMeshWriter +( + const polyMesh& mesh, + const scalar scaleFactor +) +: + meshWriter(mesh, scaleFactor) +{} + + +// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // + +Foam::fileFormats::FIREMeshWriter::~FIREMeshWriter() +{} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +bool Foam::fileFormats::FIREMeshWriter::write(const fileName& meshName) const +{ + bool useBinary = binary; + bool useCompress = compress; + + fileName baseName(meshName); + if (baseName.empty()) + { + baseName = meshWriter::defaultMeshName; + + const Time& t = mesh_.time(); + + if + ( + t.timeName() != "0" + && t.timeName() != t.constant() + ) + { + baseName += "_" + t.timeName(); + } + } + else + { + const word ext = baseName.ext(); + + if (FIRECore::file3dExtensions.found(ext)) + { + FIRECore::fileExt3d fireFileType = FIRECore::file3dExtensions[ext]; + if (fireFileType == FIRECore::POLY_ASCII) + { + useBinary = false; + useCompress = false; + } + else if (fireFileType == FIRECore::POLY_BINARY) + { + useBinary = true; + useCompress = false; + } + else if (fireFileType == FIRECore::POLY_ASCII_COMPRESSED) + { + useBinary = false; + useCompress = true; + } + else if (fireFileType == FIRECore::POLY_BINARY_COMPRESSED) + { + useBinary = true; + useCompress = true; + } + } + + baseName = baseName.lessExt(); + } + + + // A slight hack. Cannot generate compressed files with the desired ending + // So create and rename later + const fileName filename = FIRECore::fireFileName + ( + baseName, + useBinary ? FIRECore::POLY_BINARY : FIRECore::POLY_ASCII + ); + + autoPtr osPtr + ( + new OFstream + ( + filename, + (useBinary ? IOstream::BINARY : IOstream::ASCII), + IOstream::currentVersion, + (useCompress ? IOstream::COMPRESSED : IOstream::UNCOMPRESSED) + ) + ); + + if (osPtr->good()) + { + Info<< "Writing output to "; + if (useCompress) + { + // output .fpmaz instead of .fpma + Info<< '"' << osPtr().name().c_str() << "z\"" << endl; + } + else + { + Info<< osPtr().name() << endl; + } + + writeGeometry(osPtr()); + writeSelections(osPtr()); + + osPtr.clear(); // implicitly close the file + + if (useCompress) + { + // rename .fpma.gz -> .fpmaz + // The '.gz' is automatically added by OFstream in compression mode + Foam::mv(filename + ".gz", filename + "z"); + } + } + else + { + Info<<"could not open file for writing " << filename << endl; + return false; + } + + return true; +} + + +// ************************************************************************* // diff --git a/src/conversion/fire/FIREMeshWriter.H b/src/conversion/fire/FIREMeshWriter.H new file mode 100644 index 0000000000..7d61a0461e --- /dev/null +++ b/src/conversion/fire/FIREMeshWriter.H @@ -0,0 +1,130 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +Class + Foam::fileFormats::FIREMeshWriter + +Description + Writes polyMesh in AVL/FIRE polyhedra format (fpma, fpmb) + + It is also possible to write compressed formats (fpmaz, fpmbz) + +Note + The fpma, fpmb formats are relatively poorly documented, but are manageable + to read and write. It is, however, not recommended to import them directly + into AVL/FIRE (the GUI) since it is generally not robust enough. + Instead use their file-convertor to reconvert them into their native format. + + In the AVL/FIRE polyhedra format, the faces normals point inwards, whereas + the OpenFOAM face normals always point outwards. + +SourceFiles + FIREMeshWriter.C + +\*---------------------------------------------------------------------------*/ + +#ifndef FIREMeshWriter_H +#define FIREMeshWriter_H + +#include "meshWriter.H" +#include "FIRECore.H" +#include "IOstream.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +namespace fileFormats +{ + +/*---------------------------------------------------------------------------*\ + Class fileFormats::FIREMeshWriter Declaration +\*---------------------------------------------------------------------------*/ + +class FIREMeshWriter +: + public meshWriter, + public FIRECore +{ + // Private Member Functions + + //- Disallow default bitwise copy construct + FIREMeshWriter(const FIREMeshWriter&) = delete; + + //- Disallow default bitwise assignment + void operator=(const FIREMeshWriter&) = delete; + + //- Write points, faces, cells + bool writeGeometry(OSstream&) const; + + //- Write selections + bool writeSelections(OSstream&) const; + +public: + + // Static data members + + //- Write binary (default ascii) + static bool binary; + + //- Write with compression (default false) + static bool compress; + + //- Prefix patches with 'BND_' before writing (default true) + static bool prefixBoundary; + + + // Constructors + + //- Prepare for writing, optionally with scaling + FIREMeshWriter(const polyMesh&, const scalar scaleFactor = 1.0); + + + //- Destructor + virtual ~FIREMeshWriter(); + + + // Member Functions + + // Write + + //- Write volume mesh + virtual bool write + ( + const fileName& meshName = fileName::null + ) const; + +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace fileFormats +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/conversion/fire/checkFireEdges.C b/src/conversion/fire/checkFireEdges.C new file mode 100644 index 0000000000..eb7c3bf9da --- /dev/null +++ b/src/conversion/fire/checkFireEdges.C @@ -0,0 +1,299 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +\*---------------------------------------------------------------------------*/ + +#include "checkFireEdges.H" +#include "polyMesh.H" +#include "edge.H" +#include "HashSet.H" +#include "ListOps.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + //! \cond fileScope + //- Print face information for debugging purposes + static inline void printFace + ( + const face& f, + label faceI, + const edge& currEdge + ) + { + Info<< "face " << faceI << ':'; + forAll(f, fpI) + { + Info<< ' '; + if (f[fpI] == currEdge[0] || f[fpI] == currEdge[1]) + { + Info<< '_'; // highlight the node + } + Info<< f[fpI]; + } + Info<< endl; + } + //! \endcond +} + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +Foam::label Foam::checkFireEdges +( + const faceList& faces, + const labelListList& pointFaces, + const UList& points +) +{ + label nFailedEdges = 0; + const bool fullCheck = true; + typedef HashSet> edgeHashSet; + + Info<< "Checking edges according to AVL/FIRE on-the-fly methodology..." + << endl; + + labelHashSet strayPoints(100); + edgeHashSet failedEdges(100); + + forAll(faces, faceI) + { + const face& faceA = faces[faceI]; + + forAll(faceA, edgeI) + { + const edge currEdge = faceA.faceEdge(edgeI); + + // all faces attached to the first point + const labelList& otherFaceIds = pointFaces[currEdge[0]]; + + forAll(otherFaceIds, otherI) + { + const int otherFaceI = otherFaceIds[otherI]; + const face& faceB = faces[otherFaceI]; + + // only check once + if (otherFaceI <= faceI && !fullCheck) + { + continue; + } + + // get local edges on the second face + int other_p0 = -1; + int other_p1 = -1; + int size_m1 = faceB.size() - 1; + + forAll(faceB, ptI) + { + if (faceB[ptI] == currEdge[0]) + { + other_p0 = ptI; + } + + if (faceB[ptI] == currEdge[1]) + { + other_p1 = ptI; + } + } + + if + ( + // did not have both points - can skip + other_p0 == -1 + || other_p1 == -1 + // a normal edge + || abs(other_p0 - other_p1) == 1 + // handle wrapping + || (other_p0 == 0 && other_p1 == size_m1) + || (other_p1 == 0 && other_p0 == size_m1) + ) + { + continue; + } + + // find the "stray" point + int stray = -1; + if (abs(other_p0 - other_p1) == 2) + { + // a normal case + stray = (other_p0 + other_p1) / 2; + } + else if + ( + (other_p0 == 0 && other_p1+1 == size_m1) + || (other_p1 == 0 && other_p0+1 == size_m1) + ) + { + stray = size_m1; + } + + if (stray > 0) + { + strayPoints.set(faceB[stray]); + } + + failedEdges.set(currEdge); + + ++nFailedEdges; + + Info<< nl + << "Broken edge calculated between points " + << currEdge[0] << " " << currEdge[1] << endl; + + printFace(faceA, faceI, currEdge); + printFace(faceB, otherFaceI, currEdge); + } + } + } + + if (nFailedEdges) + { + Info<< endl; + } + Info<< "detected " << nFailedEdges << " edge failures"; + + // reduce to the actual number of edges + nFailedEdges = failedEdges.size(); + + // report the locations + if (nFailedEdges) + { + Info<< " over " << nFailedEdges << " edges" << endl; + + Info<< nl + << "edge points" << nl + << "~~~~~~~~~~~" << endl; + + + forAllConstIter(edgeHashSet, failedEdges, citer) + { + edge thisEdge = citer.key(); + if (thisEdge.start() > thisEdge.end()) + { + thisEdge.flip(); + } + + if (&points) + { + forAll(thisEdge, keyI) + { + const label ptI = thisEdge[keyI]; + Info<< "point " << ptI << ": " << points[ptI] << endl; + } + } + else + { + forAll(thisEdge, keyI) + { + const label ptI = thisEdge[keyI]; + Info<< "point " << ptI << endl; + } + } + Info<< endl; + } + + Info<< nl + << "stray points" << nl + << "~~~~~~~~~~~~" << endl; + + { + labelList keys = strayPoints.sortedToc(); + + if (&points) + { + forAll(keys, keyI) + { + const label ptI = keys[keyI]; + Info<< "stray " << ptI << ": " << points[ptI] << endl; + } + } + else + { + forAll(keys, keyI) + { + const label ptI = keys[keyI]; + Info<< "stray " << ptI << endl; + } + } + + } + Info<< endl; + } + else + { + Info<< endl; + } + + return nFailedEdges; +} + + +Foam::label Foam::checkFireEdges +( + const faceList& faces, + const UList& points +) +{ + label nPoints = -1; + + if (&points) + { + nPoints = points.size(); + + } + else + { + // get the max point addressed + forAll(faces, faceI) + { + const face& f = faces[faceI]; + forAll(f, fp) + { + if (nPoints < f[fp]) + { + nPoints = f[fp]; + } + } + } + + ++nPoints; + } + + labelListList pointFaces(nPoints); + invertManyToMany(nPoints, faces, pointFaces); + + return checkFireEdges(faces, pointFaces, points); +} + + +Foam::label Foam::checkFireEdges +( + const polyMesh& mesh +) +{ + return checkFireEdges(mesh.faces(), mesh.pointFaces(), mesh.points()); +} + + +// ************************************************************************* // diff --git a/src/conversion/fire/checkFireEdges.H b/src/conversion/fire/checkFireEdges.H new file mode 100644 index 0000000000..0f0bffe2c8 --- /dev/null +++ b/src/conversion/fire/checkFireEdges.H @@ -0,0 +1,79 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +Description + Checks the mesh for edge connectivity as expected by the AVL/FIRE + on-the-fly calculations. + Errors flagged here are not necessarily topological errors at all. + +SourceFiles + checkFireEdges.C + +\*---------------------------------------------------------------------------*/ + +#ifndef checkFireEdges_H +#define checkFireEdges_H + +#include "faceList.H" +#include "labelList.H" +#include "point.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +class polyMesh; + +//- check edge connectivity +label checkFireEdges +( + const faceList&, + const labelListList& pointFaces, + const UList& = UList::null() +); + + +//- check edge connectivity with pointFaces mapping calculated automatically +label checkFireEdges +( + const faceList&, + const UList& = UList::null() +); + + +//- check edge connectivity +label checkFireEdges(const polyMesh&); + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/fileFormats/Make/files b/src/fileFormats/Make/files index c972fde32e..4be2605bd8 100644 --- a/src/fileFormats/Make/files +++ b/src/fileFormats/Make/files @@ -7,10 +7,12 @@ ensight/part/ensightFaces.C ensight/read/ensightReadFile.C ensight/type/ensightPTraits.C -vtk/vtkUnstructuredReader.C nas/NASCore.C +fire/FIRECore.C starcd/STARCDCore.C +vtk/vtkUnstructuredReader.C + coordSet/coordSet.C setWriters = sampledSetWriters diff --git a/src/fileFormats/fire/FIRECore.C b/src/fileFormats/fire/FIRECore.C new file mode 100644 index 0000000000..ab57611634 --- /dev/null +++ b/src/fileFormats/fire/FIRECore.C @@ -0,0 +1,370 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +\*---------------------------------------------------------------------------*/ + +#include "FIRECore.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +const Foam::NamedEnum + Foam::fileFormats::FIRECore::file3dExtensions; + +namespace Foam +{ + template<> + const char* Foam::NamedEnum + < + Foam::fileFormats::FIRECore::fileExt3d, + 4 + >::names[] = + { + "fpma", + "fpmb", + "fpmaz", + "fpmbz" + }; +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::fileFormats::FIRECore::FIRECore() +{} + + +// * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * * // + +Foam::label Foam::fileFormats::FIRECore::readPoints +( + ISstream& is, + pointField& points +) +{ + const label n = getFireLabel(is); + + if (n > 0) + { + points.setSize(n); + + // read the coordinates + forAll(points, pointI) + { + points[pointI] = getFirePoint(is); + } + } + else + { + FatalErrorInFunction + << "no points in file " << is.name() + << abort(FatalError); + } + + return n; +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +Foam::fileName Foam::fileFormats::FIRECore::fireFileName +( + const fileName& base, + const enum fileExt3d ext +) +{ + return base + '.' + file3dExtensions[ext]; +} + + +Foam::label Foam::fileFormats::FIRECore::getFireLabel(ISstream& is) +{ + if (is.format() == IOstream::BINARY) + { + fireInt_t ivalue; + + is.stdStream().read + ( + reinterpret_cast(&ivalue), + sizeof(ivalue) + ); + + return ivalue; + } + else + { + return readLabel(is); + } +} + + +Foam::point Foam::fileFormats::FIRECore::getFirePoint(ISstream& is) +{ + point pt; + + if (is.format() == IOstream::BINARY) + { + fireReal_t coord[3]; + + is.stdStream().read + ( + reinterpret_cast(&coord), + sizeof(coord) + ); + + pt.x() = coord[0]; + pt.y() = coord[1]; + pt.z() = coord[2]; + } + else + { + pt.x() = readScalar(is); + pt.y() = readScalar(is); + pt.z() = readScalar(is); + } + + return pt; +} + + +std::string Foam::fileFormats::FIRECore::getFireString(ISstream& is) +{ + std::string str; + + if (is.format() == IOstream::BINARY) + { + long len; + + is.stdStream().read + ( + reinterpret_cast(&len), + sizeof(len) + ); + + str.resize(len); + + for (std::size_t pos = 0; pos < str.size(); ++pos) + { + is.stdStream().read(&(str[pos]), sizeof(char)); + } + } + else + { + const std::string whitespace(" \t\f\v\n\r"); + + string s; + + // use a low-level getline, but this means we must handle + // blank lines manually + while (s.empty()) + { + is.getLine(s); + if (!s.empty()) + { + // remove prefix whitespace + size_t pos = s.find_first_not_of(whitespace); + + if (pos != std::string::npos) + { + s.erase(0, pos); + + // remove suffix whitespace + pos = s.find_last_not_of(whitespace); + if (pos != std::string::npos) + { + s.erase(pos + 1); + } + } + + if (pos == std::string::npos) + { + s.clear(); + } + } + } + + str.swap(s); + } + + return str; +} + + +void Foam::fileFormats::FIRECore::putFireLabel +( + OSstream& os, + const label value +) +{ + if (os.format() == Foam::IOstream::BINARY) + { + fireInt_t ivalue(value); + + os.stdStream().write + ( + reinterpret_cast(&ivalue), + sizeof(ivalue) + ); + } + else + { + os << value; + } +} + + +void Foam::fileFormats::FIRECore::putFireLabels +( + OSstream& os, + const labelUList& lst +) +{ + if (os.format() == IOstream::BINARY) + { + fireInt_t ivalue(lst.size()); + + os.stdStream().write + ( + reinterpret_cast(&ivalue), + sizeof(ivalue) + ); + + forAll(lst, i) + { + ivalue = lst[i]; + + os.stdStream().write + ( + reinterpret_cast(&ivalue), + sizeof(ivalue) + ); + } + } + else + { + os << ' ' << lst.size(); + forAll(lst, i) + { + os << ' ' << lst[i]; + } + os << '\n'; + } +} + + +void Foam::fileFormats::FIRECore::putFireLabels +( + OSstream& os, + const label count, + const label start +) +{ + if (os.format() == IOstream::BINARY) + { + fireInt_t ivalue(count); + + os.stdStream().write + ( + reinterpret_cast(&ivalue), + sizeof(ivalue) + ); + + ivalue = start; + for (label i=0; i < count; ++i, ++ivalue) + { + os.stdStream().write + ( + reinterpret_cast(&ivalue), + sizeof(ivalue) + ); + } + } + else + { + os << ' ' << count; + + label ivalue = start; + for (label i = 0; i < count; ++i, ++ivalue) + { + os << ' ' << ivalue; + } + os << '\n'; + } +} + + +void Foam::fileFormats::FIRECore::putFirePoint +( + OSstream& os, + const point& value +) +{ + if (os.format() == IOstream::BINARY) + { + fireReal_t fvalue[3]; + fvalue[0] = value.x(); + fvalue[1] = value.y(); + fvalue[2] = value.z(); + + os.stdStream().write + ( + reinterpret_cast(&fvalue), + sizeof(fvalue) + ); + } + else + { + os << ' ' + << value.x() << ' ' + << value.y() << ' ' + << value.z() << '\n'; + } +} + + +void Foam::fileFormats::FIRECore::putFireString +( + OSstream& os, + const std::string& value +) +{ + if (os.format() == IOstream::BINARY) + { + long len(value.size()); + + os.stdStream().write + ( + reinterpret_cast(&len), + sizeof(len) + ); + + os.stdStream().write(value.data(), len); + } + else + { + // output without surrounding quotes + os.stdStream() << value << '\n'; + } +} + + +// ************************************************************************* // diff --git a/src/fileFormats/fire/FIRECore.H b/src/fileFormats/fire/FIRECore.H new file mode 100644 index 0000000000..3f9c654cc1 --- /dev/null +++ b/src/fileFormats/fire/FIRECore.H @@ -0,0 +1,179 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +Class + Foam::fileFormats::FIRECore + +Description + Core routines used when reading/writing AVL/FIRE files. + +SourceFiles + FIRECore.C + +\*---------------------------------------------------------------------------*/ + +#ifndef FIRECore_H +#define FIRECore_H + +#include "point.H" +#include "string.H" +#include "labelList.H" +#include "pointField.H" +#include "IOstreams.H" +#include "NamedEnum.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +// forward declarations +class polyMesh; + +namespace fileFormats +{ + +/*---------------------------------------------------------------------------*\ + Class fileFormats::FIRECore Declaration +\*---------------------------------------------------------------------------*/ + +class FIRECore +{ +public: + + // Public Data, Declarations + + //- Selection Types + enum selectionType + { + cellSelection = 2, + faceSelection = 3 + }; + + //- Shape-Type for FIRE (FLMA) files + enum shapeType + { + fireLine = 1, + fireTri = 2, + fireQuad = 3, + fireTet = 4, + fireHex = 5, + firePyr = 6, + firePrism = 8 + }; + + //- Enumeration defining the file extensions for 3D types + enum fileExt3d + { + POLY_ASCII, + POLY_BINARY, + POLY_ASCII_COMPRESSED, + POLY_BINARY_COMPRESSED + }; + + + //- Integer type (binary format) + typedef int32_t fireInt_t; + + //- Float type (binary format) + typedef double fireReal_t; + +protected: + + // Protected Data + + static const NamedEnum file3dExtensions; + + + // Protected Member Functions + + //- Construct null + FIRECore(); + + + //- Read points. + // This is the first thing to do when reading FPMA,FPMB,FLMA files. + // Return the number of points read. + // + // The file format is as follows: + // \verbatim + // NUMBER_OF_VERTICES + // x0 y0 z0 x1 y1 z1 ... xN-1 yN-1 zN-1 + // \endverbatim + static label readPoints(ISstream&, pointField&); + +public: + + // Public Member Functions + + //- Resolve base file-name for the given file-type + static fileName fireFileName + ( + const fileName& baseName, + const enum fileExt3d + ); + + + //- Get an integer (ascii or binary) + static label getFireLabel(ISstream&); + + //- Get an point x/y/z (ascii or binary) + static point getFirePoint(ISstream&); + + //- Extract a string (ascii or binary) + static std::string getFireString(ISstream&); + + + //- Write an integer (ascii or binary) + static void putFireLabel(OSstream&, const label); + + //- Write multiple integers (ascii or binary) + static void putFireLabels(OSstream&, const labelUList&); + + //- Write an on-the-fly list of integers (ascii or binary) + static void putFireLabels + ( + OSstream&, + const label count, + const label start + ); + + + //- Write a point x/y/z (ascii or binary) + static void putFirePoint(OSstream&, const point&); + + //- Write a string (ascii or binary) + static void putFireString(OSstream&, const std::string&); + +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace fileFormats +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/fileFormats/fire/README b/src/fileFormats/fire/README new file mode 100644 index 0000000000..1cf217d220 --- /dev/null +++ b/src/fileFormats/fire/README @@ -0,0 +1,124 @@ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + AVL/FIRE fpma/fpmb +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +fpma/fpmb are polyhedral formats (ASCII and BINARY). +The fpmaz/fpmbz are the same, but in compressed format. + +The fpma/fpmb format can exported directly from within the FIRE GUI. +However, it is not recommended to import them directly. +Instead use the utility fire_wm_convert_file, fire_wm_convert_mesh +(or equivalents) +are used to rewrite the fpma/fpmb into the internal FIRE format. + + +~~~~~~ +FORMAT +~~~~~~ + +NUMBER_OF_VERTICES +x0 y0 z0 x1 y1 z1 ... xN-1 yN-1 zN-1 + + +NUMBER_OF_FACES +face-stream0 +... +face-streamN-1 + + +NUMBER_OF_CELLS +cell-face-stream0 +.. +cell-face-streamN-1 + + +NUMBER_OF_SELECTIONS +selectionName[0] +selectionType[0] (2 = cells, 3 = faces) +numberOfIDs +id0 .. indN-1 + + + + +The face-stream: + nvert vert0 .. vertN + +The cell-face-stream: + nface faceId0 .. faceIdN + + +CAUTION: + The FIRE faces have inward-facing normals. + The OPENFOAM faces have outward-facing normals! + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + AVL/FIRE flma +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The AVL/FIRE flma format is very simple, but can only be used for 1D/2D shapes +and for 3D primitive shapes. + +it is fairly easy to write and to parse. +For 3D polyhedra, the cell-types are non-obvious and not easily determined +- the fpma/fpmb format is preferred for that. + + +~~~~~~ +FORMAT +~~~~~~ +NUMBER_OF_VERTICES +x0 y0 z0 x1 y1 z1 ... xN-1 yN-1 zN-1 + +NUMBER_OF_CELLS +nv[0] +verts[0] +nv[N-1] +... +verts[N-1] + +NUMBER_OF_CELLS +type0 type1 ... type2 + +NUMBER_OF_SELECTIONS +selectionName[0] +selectionType[0] (2 = cells, 3 = faces) +numberOfIDs +id0 .. indN-1 + +... +selectionName[N-1] +selectionType[N-1] (2 = cells, 3 = faces) +numberOfIDs +id0 .. indN-1 + + + +-- NOTES (flma) -- +# vertex/cells are zero-based + + cell-types: + 1 = line + 2 = tri + 3 = quad + 4 = tet + 5 = hex + 6 = pyr + 8 = prism + + +---------------+---------------+ + | shape | face-order | + +---------------+---------------+ + + Tet(FIRE) | 0 1 2 3 | + + Tet(FOAM) | 3 2 0 1 | + +---------------+---------------+ + + Hex(FIRE) | 0 1 2 3 4 5 | + + Hex(FOAM) | 4 5 0 1 2 3 | + +---------------+---------------+ + + Pyr(FIRE) | 0 1 2 3 4 | + + Pyr(FOAM) | 0 4 3 2 1 | + +---------------+---------------+ + + Prism(FIRE) | 0 1 2 3 4 | + + Prism(FOAM) | 0 1 4 3 2 | + +---------------+---------------+ + diff --git a/src/surfMesh/Make/files b/src/surfMesh/Make/files index 5e19af8613..f7beae2f7c 100644 --- a/src/surfMesh/Make/files +++ b/src/surfMesh/Make/files @@ -24,6 +24,7 @@ $(surfaceFormats)/surfaceFormatsCore.C $(surfaceFormats)/ac3d/AC3DsurfaceFormatCore.C $(surfaceFormats)/ac3d/AC3DsurfaceFormatRunTime.C +$(surfaceFormats)/fire/FLMAsurfaceFormatRunTime.C $(surfaceFormats)/gts/GTSsurfaceFormatRunTime.C $(surfaceFormats)/nas/NASsurfaceFormatRunTime.C $(surfaceFormats)/obj/OBJsurfaceFormatRunTime.C diff --git a/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormat.C b/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormat.C new file mode 100644 index 0000000000..4cbfb60d56 --- /dev/null +++ b/src/surfMesh/surfaceFormats/fire/FLMAsurfaceFormat.C @@ -0,0 +1,390 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2016 OpenCFD Ltd. + \\/ 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 . + +\*---------------------------------------------------------------------------*/ + +#include "FLMAsurfaceFormat.H" + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +//! \cond fileScope +//- Output newline in ascii mode, no-op in binary mode +inline static void newline(Foam::OSstream& os) +{ + if (os.format() == Foam::IOstream::ASCII) + { + os << Foam::endl; + } +} + + +template +inline static int countFaces(const Face& f) +{ + int n = (f.size() - 2); // number triangles can be determined directly + return n == 2 ? 1 : n; // quads don't need triangulation +} + +//! \endcond + + +template +inline void Foam::fileFormats::FLMAsurfaceFormat::writeShell +( + OSstream& os, + const Face& f +) +{ + if (os.format() == IOstream::BINARY) + { + if (f.size() == 3 || f.size() == 4) + { + putFireLabel(os, f.size()); + forAll(f, fp) + { + putFireLabel(os, f[fp]); + } + } + else + { + // simple triangulation about f[0]. + // better triangulation should have been done before + for (label fp1 = 1; fp1 < f.size() - 1; ++fp1) + { + label fp2 = f.fcIndex(fp1); + + putFireLabel(os, 3); + putFireLabel(os, f[0]); + putFireLabel(os, f[fp1]); + putFireLabel(os, f[fp2]); + } + } + } + else + { + // ASCII + if (f.size() == 3 || f.size() == 4) + { + os << ' ' << f.size(); + forAll(f, fp) + { + os << ' ' << f[fp]; + } + os << nl; + } + else + { + for (label fp1 = 1; fp1 < f.size() - 1; ++fp1) + { + label fp2 = f.fcIndex(fp1); + os << ' ' << 3 << ' ' + << f[0] << ' ' << f[fp1] << ' ' << f[fp2] + << nl; + } + } + } +} + + +template +inline void Foam::fileFormats::FLMAsurfaceFormat::writeType +( + OSstream& os, + const Face& f +) +{ + if (os.format() == IOstream::BINARY) + { + if (f.size() == 4) + { + putFireLabel(os, fireQuad); + } + else + { + const label n = countFaces(f); + for (label i=0; i < n; ++i) + { + putFireLabel(os, fireTri); + } + } + } + else + { + // ASCII + if (f.size() == 4) + { + os << ' ' << fireQuad; + } + else + { + const label n = countFaces(f); + for (label i=0; i < n; ++i) + { + os << ' ' << fireTri; + } + } + } +} + + +// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // + +template +void Foam::fileFormats::FLMAsurfaceFormat::write +( + OSstream& os, + const MeshedSurfaceProxy& surf +) +{ + if (!os.good()) + { + FatalErrorIn + ( + "fileFormats::FLMAsurfaceFormat::write" + "(OSstream&, const MeshedSurfaceProxy&)" + ) + << "bad output state " + << exit(FatalError); + } + + const pointField& pointLst = surf.points(); + const List& faceLst = surf.surfFaces(); + const List