/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | foam-extend: Open Source CFD
\\ / O peration |
\\ / A nd | For copyright notice see file Copyright
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of foam-extend.
foam-extend 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.
foam-extend 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 foam-extend. If not, see .
Description
Reader module for Fieldview9 to read FOAM mesh and data.
Creates new 'fvbin' type executable which needs to be installed in place
of bin/fvbin.
Implements a reader for combined mesh&results on an unstructured mesh.
See Fieldview Release 9 Reference Manual and coding in user/ directory
of the Fieldview release.
\*---------------------------------------------------------------------------*/
#include "fvCFD.H"
#include "IOobjectList.H"
#include "GeometricField.H"
#include "pointFields.H"
#include "volPointInterpolation.H"
#include "readerDatabase.H"
#include "wallPolyPatch.H"
#include "ListOps.H"
#include "cellModeller.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
extern "C"
{
// Define various Fieldview constants and prototypes
#include "fv_reader_tags.h"
static const int FVHEX = 2;
static const int FVPRISM = 3;
static const int FVPYRAMID = 4;
static const int FVTET = 1;
static const int ITYPE = 1;
unsigned int fv_encode_elem_header(int elem_type, int wall_info[]);
void time_step_get_value(float*, int, int*, float*, int*);
void fv_error_msg(const char*, const char*);
void reg_single_unstruct_reader
(
char *,
void
(
char*, int*, int*, int*, int*, int*, int*,
int[], int*, char[][80], int[], int*, char[][80], int*
),
void
(
int*, int*, int*, float[], int*, float[], int*
)
);
int create_tet(const int, const int[8], const int[]);
int create_pyramid(const int, const int[8], const int[]);
int create_prism(const int, const int[8], const int[]);
int create_hex(const int, const int[8], const int[]);
typedef unsigned char uChar;
extern uChar create_bndry_face_buffered
(
int bndry_type,
int num_verts,
int verts[],
int *normals_flags,
int num_grid_nodes
);
/*
* just define empty readers here for ease of linking.
* Comment out if you have doubly defined linking error on this symbol
*/
void ftn_register_data_readers()
{}
/*
* just define empty readers here for ease of linking.
* Comment out if you have doubly defined linking error on this symbol
*/
void ftn_register_functions()
{}
/*
* just define empty readers here for ease of linking.
* Comment out if you have doubly defined linking error on this symbol
*/
void register_functions()
{}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//
// Storage for all Foam state (mainly database & mesh)
//
static readerDatabase db_;
// Write fv error message.
static void errorMsg(const string& msg)
{
fv_error_msg("Foam Reader", msg.c_str());
}
// Simple check if directory is valid case directory.
static bool validCase(const fileName& rootAndCase)
{
//if (isDir(rootAndCase/"system") && isDir(rootAndCase/"constant"))
if (isDir(rootAndCase/"constant"))
{
return true;
}
else
{
return false;
}
}
// Check whether case has topo changes by looking back from last time
// to first directory with polyMesh/cells.
static bool hasTopoChange(const instantList& times)
{
label lastIndex = times.size()-1;
const Time& runTime = db_.runTime();
// Only set time; do not update mesh.
runTime.setTime(times[lastIndex], lastIndex);
fileName facesInst(runTime.findInstance(polyMesh::meshSubDir, "faces"));
// See if cellInst is constant directory. Note extra .name() is for
// parallel cases when runTime.constant is '../constant'
if (facesInst != runTime.constant().name())
{
Info<< "Found cells file in " << facesInst << " so case has "
<< "topological changes" << endl;
return true;
}
else
{
Info<< "Found cells file in " << facesInst << " so case has "
<< "no topological changes" << endl;
return false;
}
}
static bool selectTime(const instantList& times, int* iret)
{
List fvTimes(2*times.size());
forAll(times, timeI)
{
fvTimes[2*timeI] = float(timeI);
fvTimes[2*timeI+1] = float(times[timeI].value());
}
int istep;
float time;
*iret=0;
time_step_get_value(fvTimes.begin(), times.size(), &istep, &time, iret);
if (*iret == -5)
{
errorMsg("Out of memory.");
return false;
}
if (*iret == -15)
{
// Cancel action.
return false;
}
if (*iret != 0)
{
errorMsg("Unspecified error.");
return false;
}
Info<< "Selected timeStep:" << istep << " time:" << scalar(time) << endl;
// Set time and load mesh.
db_.setTime(times[istep], istep);
return true;
}
// Gets (names of) all fields in all timesteps.
static void createFieldNames
(
const Time& runTime,
const instantList& Times,
const word& setName
)
{
// From foamToFieldView9/getFieldNames.H:
HashSet volScalarHash;
HashSet volVectorHash;
HashSet surfScalarHash;
HashSet surfVectorHash;
if (setName.empty())
{
forAll(Times, timeI)
{
const word& timeName = Times[timeI].name();
// Add all fields to hashtable
IOobjectList objects(runTime, timeName);
wordList vsNames(objects.names(volScalarField::typeName));
forAll(vsNames, fieldI)
{
volScalarHash.insert(vsNames[fieldI]);
}
wordList vvNames(objects.names(volVectorField::typeName));
forAll(vvNames, fieldI)
{
volVectorHash.insert(vvNames[fieldI]);
}
}
}
db_.setFieldNames(volScalarHash.toc(), volVectorHash.toc());
}
// Appends interpolated values of fieldName to vars array.
static void storeScalarField
(
const volPointInterpolation& pInterp,
const word& fieldName,
float vars[],
label& pointI
)
{
label nPoints = db_.mesh().nPoints();
label nTotPoints = nPoints + db_.polys().size();
// Check if present
IOobject ioHeader
(
fieldName,
db_.runTime().timeName(),
db_.runTime(),
IOobject::MUST_READ,
IOobject::NO_WRITE
);
if (ioHeader.headerOk())
{
Info<< "Storing " << nTotPoints << " of interpolated " << fieldName
<< endl;
volScalarField field(ioHeader, db_.mesh());
pointScalarField psf(pInterp.interpolate(field));
forAll(psf, i)
{
vars[pointI++] = float(psf[i]);
}
const labelList& polys = db_.polys();
forAll(polys, i)
{
label cellI = polys[i];
vars[pointI++] = float(field[cellI]);
}
}
else
{
Info<< "Storing " << nTotPoints << " of dummy values of " << fieldName
<< endl;
for(label i = 0; i < nPoints; i++)
{
vars[pointI++] = 0.0;
}
const labelList& polys = db_.polys();
forAll(polys, i)
{
vars[pointI++] = 0.0;
}
}
}
// Appends interpolated values of fieldName to vars array.
static void storeVectorField
(
const volPointInterpolation& pInterp,
const word& fieldName,
float vars[],
label& pointI
)
{
label nPoints = db_.mesh().nPoints();
label nTotPoints = nPoints + db_.polys().size();
// Check if present
IOobject ioHeader
(
fieldName,
db_.runTime().timeName(),
db_.runTime(),
IOobject::MUST_READ,
IOobject::NO_WRITE
);
if (ioHeader.headerOk())
{
Info<< "Storing " << nTotPoints << " of interpolated " << fieldName
<< endl;
volVectorField field(ioHeader, db_.mesh());
for (direction d = 0; d < vector::nComponents; d++)
{
tmp tcomp = field.component(d);
const volScalarField& comp = tcomp();
pointScalarField psf(pInterp.interpolate(comp));
forAll(psf, i)
{
vars[pointI++] = float(psf[i]);
}
const labelList& polys = db_.polys();
forAll(polys, i)
{
label cellI = polys[i];
vars[pointI++] = float(comp[cellI]);
}
}
}
else
{
Info<< "Storing " << nTotPoints << " of dummy values of " << fieldName
<< endl;
for (direction d = 0; d < vector::nComponents; d++)
{
for(label i = 0; i < nPoints; i++)
{
vars[pointI++] = 0.0;
}
const labelList& polys = db_.polys();
forAll(polys, i)
{
vars[pointI++] = 0.0;
}
}
}
}
// Returns Fieldview face_type of mesh face faceI.
static label getFvType(const polyMesh& mesh, const label faceI)
{
return mesh.boundaryMesh().whichPatch(faceI) + 1;
}
// Returns Fieldview face_type of face f.
static label getFaceType
(
const polyMesh& mesh,
const labelList& faceLabels,
const face& f
)
{
// Search in subset faceLabels of faces for index of face f.
const faceList& faces = mesh.faces();
forAll(faceLabels, i)
{
label faceI = faceLabels[i];
if (f == faces[faceI])
{
// Convert patch to Fieldview face_type.
return getFvType(mesh, faceI);
}
}
FatalErrorIn("getFaceType")
<< "Cannot find face " << f << " in mesh face subset " << faceLabels
<< abort(FatalError);
return -1;
}
// Returns Fieldview face_types for set of faces
static labelList getFaceTypes
(
const polyMesh& mesh,
const labelList& cellFaces,
const faceList& cellShapeFaces
)
{
labelList faceLabels(cellShapeFaces.size());
forAll(cellShapeFaces, i)
{
faceLabels[i] = getFaceType(mesh, cellFaces, cellShapeFaces[i]);
}
return faceLabels;
}
/*
* Callback for querying file contents. Taken from user/user_unstruct_combined.f
*/
void user_query_file_function
(
/* input */
char* fname, /* filename */
int* lenf, /* length of fName */
int* iunit, /* fortran unit to use */
int* max_grids, /* maximum number of grids allowed */
int* max_face_types,/* maximum number of face types allowed */
int* max_vars, /* maximum number of result variables allowed per */
/* grid point*/
/* output */
int* num_grids, /* number of grids that will be read from data file */
int num_nodes[], /* (array of node counts for all grids) */
int* num_face_types, /* number of special face types */
char face_type_names[][80], /* array of face-type names */
int wall_flags[], /* array of flags for the "wall" behavior */
int* num_vars, /* number of result variables per grid point */
char var_names[][80], /* array of variable names */
int* iret /* return value */
)
{
fprintf(stderr, "\n** user_query_file_function\n");
string rootAndCaseString(fname, *lenf);
fileName rootAndCase(rootAndCaseString);
word setName("");
if (!validCase(rootAndCase))
{
setName = rootAndCase.name();
rootAndCase = rootAndCase.path();
word setDir = rootAndCase.name();
rootAndCase = rootAndCase.path();
word meshDir = rootAndCase.name();
rootAndCase = rootAndCase.path();
rootAndCase = rootAndCase.path();
if
(
setDir == "sets"
&& meshDir == polyMesh::typeName
&& validCase(rootAndCase)
)
{
// Valid set (hopefully - cannot check contents of setName yet).
}
else
{
errorMsg
(
"Could not find system/ and constant/ directory in\n"
+ rootAndCase
+ "\nPlease select a Foam case directory."
);
*iret = 1;
return;
}
}
fileName rootDir(rootAndCase.path());
fileName caseName(rootAndCase.name());
// handle trailing '/'
if (caseName.empty())
{
caseName = rootDir.name();
rootDir = rootDir.path();
}
Info<< "rootDir : " << rootDir << endl
<< "caseName : " << caseName << endl
<< "setName : " << setName << endl;
//
// Get/reuse database and mesh
//
bool caseChanged = db_.setRunTime(rootDir, caseName, setName);
//
// Select time
//
instantList Times = db_.runTime().times();
// If topo case set database time and update mesh.
if (hasTopoChange(Times))
{
if (!selectTime(Times, iret))
{
return;
}
}
else if (caseChanged)
{
// Load mesh (if case changed) to make sure we have nPoints etc.
db_.loadMesh();
}
//
// Set output variables
//
*num_grids = 1;
const fvMesh& mesh = db_.mesh();
label nTotPoints = mesh.nPoints() + db_.polys().size();
num_nodes[0] = nTotPoints;
Info<< "setting num_nodes:" << num_nodes[0] << endl;
Info<< "setting num_face_types:" << mesh.boundary().size() << endl;
*num_face_types = mesh.boundary().size();
if (*num_face_types > *max_face_types)
{
errorMsg("Too many patches. FieldView limit:" + name(*max_face_types));
*iret = 1;
return;
}
forAll(mesh.boundaryMesh(), patchI)
{
const polyPatch& patch = mesh.boundaryMesh()[patchI];
strcpy(face_type_names[patchI], patch.name().c_str());
if (isA(patch))
{
wall_flags[patchI] = 1;
}
else
{
wall_flags[patchI] = 0;
}
Info<< "Patch " << patch.name() << " is wall:"
<< wall_flags[patchI] << endl;
}
//- Find all volFields and add them to database
createFieldNames(db_.runTime(), Times, setName);
*num_vars = db_.volScalarNames().size() + 3*db_.volVectorNames().size();
if (*num_vars > *max_vars)
{
errorMsg("Too many variables. FieldView limit:" + name(*max_vars));
*iret = 1;
return;
}
int nameI = 0;
forAll(db_.volScalarNames(), i)
{
const word& fieldName = db_.volScalarNames()[i];
const word& fvName = db_.getFvName(fieldName);
strcpy(var_names[nameI++], fvName.c_str());
}
forAll(db_.volVectorNames(), i)
{
const word& fieldName = db_.volVectorNames()[i];
const word& fvName = db_.getFvName(fieldName);
strcpy(var_names[nameI++], (fvName + "x;" + fvName).c_str());
strcpy(var_names[nameI++], (fvName + "y").c_str());
strcpy(var_names[nameI++], (fvName + "z").c_str());
}
*iret = 0;
}
/*
* Callback for reading timestep. Taken from user/user_unstruct_combined.f
*/
void user_read_one_grid_function
(
int* iunit, /* in: fortran unit number */
int* igrid, /* in: grid number to read */
int* nodecnt, /* in: number of nodes to read */
float xyz[], /* out: coordinates of nodes: x1..xN y1..yN z1..zN */
int* num_vars, /* in: number of results per node */
float vars[], /* out: values per node */
int* iret /* out: return value */
)
{
fprintf(stderr, "\n** user_read_one_grid_function\n");
if (*igrid != 1)
{
errorMsg("Illegal grid number " + Foam::name(*igrid));
*iret = 1;
return;
}
// Get current time
instantList Times = db_.runTime().times();
// Set database time and update mesh.
// Note: this should not be nessecary here. We already have the correct
// time set and mesh loaded. This is only nessecary because Fieldview
// otherwise thinks the case is non-transient.
if (!selectTime(Times, iret))
{
return;
}
const fvMesh& mesh = db_.mesh();
// With mesh now loaded check for change in number of points.
label nTotPoints = mesh.nPoints() + db_.polys().size();
if (*nodecnt != nTotPoints)
{
errorMsg
(
"nodecnt differs from number of points in mesh.\nnodecnt:"
+ Foam::name(*nodecnt)
+ " mesh:"
+ Foam::name(nTotPoints)
);
*iret = 1;
return;
}
if
(
*num_vars
!= (db_.volScalarNames().size() + 3*db_.volVectorNames().size())
)
{
errorMsg("Illegal number of variables " + name(*num_vars));
*iret = 1;
return;
}
//
// Set coordinates
//
const pointField& points = mesh.points();
int xIndex = 0;
int yIndex = xIndex + nTotPoints;
int zIndex = yIndex + nTotPoints;
// Add mesh points first.
forAll(points, pointI)
{
xyz[xIndex++] = points[pointI].x();
xyz[yIndex++] = points[pointI].y();
xyz[zIndex++] = points[pointI].z();
}
// Add cell centres of polys
const pointField& ctrs = mesh.cellCentres();
const labelList& polys = db_.polys();
forAll(polys, i)
{
label cellI = polys[i];
xyz[xIndex++] = ctrs[cellI].x();
xyz[yIndex++] = ctrs[cellI].y();
xyz[zIndex++] = ctrs[cellI].z();
}
//
// Define elements by calling fv routines
//
static const cellModel& tet = *(cellModeller::lookup("tet"));
static const cellModel& tetWedge = *(cellModeller::lookup("tetWedge"));
static const cellModel& pyr = *(cellModeller::lookup("pyr"));
static const cellModel& prism = *(cellModeller::lookup("prism"));
static const cellModel& wedge = *(cellModeller::lookup("wedge"));
static const cellModel& hex = *(cellModeller::lookup("hex"));
//static const cellModel& splitHex = *(cellModeller::lookup("splitHex"));
int tetVerts[4];
int pyrVerts[5];
int prismVerts[6];
int hexVerts[8];
int tetFaces[4];
int pyrFaces[5];
int prismFaces[5];
int hexFaces[6];
const cellShapeList& cellShapes = mesh.cellShapes();
const faceList& faces = mesh.faces();
const cellList& cells = mesh.cells();
const labelList& owner = mesh.faceOwner();
label nPoints = mesh.nPoints();
// Fieldview face_types array with all faces marked as internal.
labelList internalFaces(6, 0);
// Mark all cells next to boundary so we don't have to calculate
// wall_types for internal cells and can just pass internalFaces.
boolList wallCell(mesh.nCells(), false);
label nWallCells = 0;
for (label faceI = mesh.nInternalFaces(); faceI < mesh.nFaces(); faceI++)
{
label cellI = owner[faceI];
if (!wallCell[cellI])
{
wallCell[cellI] = true;
nWallCells++;
}
}
label nPolys = 0;
forAll(cellShapes, cellI)
{
const cellShape& cellShape = cellShapes[cellI];
const cellModel& cellModel = cellShape.model();
const cell& cellFaces = cells[cellI];
int istat = 0;
if (cellModel == tet)
{
tetVerts[0] = cellShape[3] + 1;
tetVerts[1] = cellShape[0] + 1;
tetVerts[2] = cellShape[1] + 1;
tetVerts[3] = cellShape[2] + 1;
if (wallCell[cellI])
{
labelList faceTypes =
getFaceTypes(mesh, cellFaces, cellShape.faces());
tetFaces[0] = faceTypes[2];
tetFaces[1] = faceTypes[3];
tetFaces[2] = faceTypes[0];
tetFaces[3] = faceTypes[1];
istat = create_tet(ITYPE, tetVerts, tetFaces);
}
else
{
// All faces internal so use precalculated zero.
istat = create_tet(ITYPE, tetVerts, internalFaces.begin());
}
}
else if (cellModel == tetWedge)
{
prismVerts[0] = cellShape[0] + 1;
prismVerts[1] = cellShape[3] + 1;
prismVerts[2] = cellShape[4] + 1;
prismVerts[3] = cellShape[1] + 1;
prismVerts[4] = cellShape[4] + 1;
prismVerts[5] = cellShape[2] + 1;
if (wallCell[cellI])
{
labelList faceTypes =
getFaceTypes(mesh, cellFaces, cellShape.faces());
prismFaces[0] = faceTypes[1];
prismFaces[1] = faceTypes[2];
prismFaces[2] = faceTypes[3];
prismFaces[3] = faceTypes[0];
prismFaces[4] = faceTypes[3];
istat = create_prism(ITYPE, prismVerts, prismFaces);
}
else
{
istat = create_prism(ITYPE, prismVerts, internalFaces.begin());
}
}
else if (cellModel == pyr)
{
pyrVerts[0] = cellShape[0] + 1;
pyrVerts[1] = cellShape[1] + 1;
pyrVerts[2] = cellShape[2] + 1;
pyrVerts[3] = cellShape[3] + 1;
pyrVerts[4] = cellShape[4] + 1;
if (wallCell[cellI])
{
labelList faceTypes =
getFaceTypes(mesh, cellFaces, cellShape.faces());
pyrFaces[0] = faceTypes[0];
pyrFaces[1] = faceTypes[3];
pyrFaces[2] = faceTypes[2];
pyrFaces[3] = faceTypes[1];
pyrFaces[4] = faceTypes[4];
istat = create_pyramid(ITYPE, pyrVerts, pyrFaces);
}
else
{
istat = create_pyramid(ITYPE, pyrVerts, internalFaces.begin());
}
}
else if (cellModel == prism)
{
prismVerts[0] = cellShape[0] + 1;
prismVerts[1] = cellShape[3] + 1;
prismVerts[2] = cellShape[4] + 1;
prismVerts[3] = cellShape[1] + 1;
prismVerts[4] = cellShape[5] + 1;
prismVerts[5] = cellShape[2] + 1;
if (wallCell[cellI])
{
labelList faceTypes =
getFaceTypes(mesh, cellFaces, cellShape.faces());
prismFaces[0] = faceTypes[4];
prismFaces[1] = faceTypes[2];
prismFaces[2] = faceTypes[3];
prismFaces[3] = faceTypes[0];
prismFaces[4] = faceTypes[1];
istat = create_prism(ITYPE, prismVerts, prismFaces);
}
else
{
istat = create_prism(ITYPE, prismVerts, internalFaces.begin());
}
}
else if (cellModel == wedge)
{
hexVerts[0] = cellShape[0] + 1;
hexVerts[1] = cellShape[1] + 1;
hexVerts[2] = cellShape[0] + 1;
hexVerts[3] = cellShape[2] + 1;
hexVerts[4] = cellShape[3] + 1;
hexVerts[5] = cellShape[4] + 1;
hexVerts[6] = cellShape[6] + 1;
hexVerts[7] = cellShape[5] + 1;
if (wallCell[cellI])
{
labelList faceTypes =
getFaceTypes(mesh, cellFaces, cellShape.faces());
hexFaces[0] = faceTypes[2];
hexFaces[1] = faceTypes[3];
hexFaces[2] = faceTypes[0];
hexFaces[3] = faceTypes[1];
hexFaces[4] = faceTypes[4];
hexFaces[5] = faceTypes[5];
istat = create_hex(ITYPE, hexVerts, hexFaces);
}
else
{
istat = create_hex(ITYPE, hexVerts, internalFaces.begin());
}
}
else if (cellModel == hex)
{
hexVerts[0] = cellShape[0] + 1;
hexVerts[1] = cellShape[1] + 1;
hexVerts[2] = cellShape[3] + 1;
hexVerts[3] = cellShape[2] + 1;
hexVerts[4] = cellShape[4] + 1;
hexVerts[5] = cellShape[5] + 1;
hexVerts[6] = cellShape[7] + 1;
hexVerts[7] = cellShape[6] + 1;
if (wallCell[cellI])
{
labelList faceTypes =
getFaceTypes(mesh, cellFaces, cellShape.faces());
hexFaces[0] = faceTypes[0];
hexFaces[1] = faceTypes[1];
hexFaces[2] = faceTypes[4];
hexFaces[3] = faceTypes[5];
hexFaces[4] = faceTypes[2];
hexFaces[5] = faceTypes[3];
istat = create_hex(ITYPE, hexVerts, hexFaces);
}
else
{
istat = create_hex(ITYPE, hexVerts, internalFaces.begin());
}
}
else
{
forAll(cellFaces, cFaceI)
{
label faceI = cellFaces[cFaceI];
// Get Fieldview facetype (internal/on patch)
label fvFaceType = getFvType(mesh, faceI);
const face& f = faces[faceI];
// Indices into storage
label nQuads = 0;
label nTris = 0;
// Storage for triangles and quads created by face
// decomposition (sized for worst case)
faceList quadFaces((f.size() - 2)/2);
faceList triFaces(f.size() - 2);
f.trianglesQuads
(
points,
nTris,
nQuads,
triFaces,
quadFaces
);
// Label of cell centre in fv point list.
label polyCentrePoint = nPoints + nPolys;
for (label i=0; i