/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022-2024 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 .
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
(
const bool writeTracks
)
{
// Collated?
// ========
// CaseFile: rootdir/NAME/NAME.case
// Geometry: rootdir/NAME/NAME.mesh
wroteGeom_ = true;
return fileName::null;
}
template
Foam::fileName Foam::coordSetWriters::ensightWriter::writeCollated
(
const word& fieldName,
const UPtrList>& fieldPtrs,
elemOutputType elemOutput
)
{
// Geometry changed since last output? Capture now
const bool geomChanged = (!upToDate_);
checkOpen();
const ensight::FileName baseName(outputPath_.name());
const ensight::VarName varName(fieldName);
// Collated
// ========
// CaseFile: rootdir/NAME/NAME.case
// Geometry: rootdir/NAME/data//geometry
// Field: rootdir/NAME/data//field
// Use geometry name as sub-directory for results. Eg,
// - NAME1/NAME1.case
// - NAME1/data/00000000/geometry
// - NAME1/data/00000000/VAR1
// - NAME1/data/00000000/VAR2
// Names "data" and "geometry" as per ensightCase:
const int maskWidth = 8;
const char* mask = "data/********/";
// Ignore the useTimeDir setting - manage ourselves
const fileName baseDir = outputPath_;
const word timeDir = timeName();
const scalar timeValue = currTime_.value();
const fileName outputFile = baseDir / baseName + ".case";
if (verbose_)
{
Info<< "Writing case file to " << outputFile << endl;
}
// Update geometry
merge();
{
if (!Foam::isDir(outputFile.path()))
{
Foam::mkDir(outputFile.path());
}
const bool stateChanged =
caching_.update
(
baseDir,
timeValue,
geomChanged,
fieldName,
ensightPTraits::typeName,
varName
);
// The most current time and geometry indices
const label timeIndex = caching_.latestTimeIndex();
const label geomIndex = caching_.latestGeomIndex();
// This will be used for the name of a static geometry,
// or just the masking part for moving geometries.
const fileName geometryName
(
"data"
/ ensightCase::padded(maskWidth, geomIndex)
/ ensightCase::geometryName
);
// Location for data (and possibly the geometry as well)
const fileName dataDir
(
baseDir/"data"/ensightCase::padded(maskWidth, timeIndex)
);
// As per mkdir -p "data/00000000"
Foam::mkDir(dataDir);
const fileName geomFile(baseDir/geometryName);
if (!Foam::exists(geomFile))
{
if (verbose_)
{
Info<< "Writing geometry to " << geomFile.name() << endl;
}
// Two-argument form for path-name to avoid validating base-dir
ensightGeoFile osGeom
(
geomFile.path(),
geomFile.name(),
caseOpts_.format()
);
osGeom.beginGeometry();
writeGeometry(osGeom, elemOutput);
}
// Write field
ensightFile osField
(
dataDir,
varName,
caseOpts_.format()
);
if (verbose_)
{
Info<< "Writing field file to " << osField.name() << endl;
}
// Write field (serial only)
writeTrackField(osField, fieldPtrs);
// Update case file
if (stateChanged)
{
OFstream osCase
(
IOstreamOption::ATOMIC,
outputFile,
IOstreamOption::ASCII
);
ensightCase::setTimeFormat(osCase, caseOpts_); // time-format
if (verbose_)
{
Info<< "Writing case file to " << osCase.name() << endl;
}
// The geometry can be any of the following:
// 0: constant/static
// 1: moving, with the same frequency as the data
// 2: moving, with different frequency as the data
const label tsGeom = caching_.geometryTimeset();
osCase
<< "FORMAT" << nl
<< "type: ensight gold" << nl
<< nl
<< "GEOMETRY" << nl;
if (tsGeom)
{
// moving
osCase
<< "model: " << tsGeom << " " // time-set (1|2)
<< mask << geometryName.name() << nl;
}
else
{
// steady
osCase
<< "model: "
<< geometryName.c_str() << nl;
}
osCase
<< nl
<< "VARIABLE" << nl;
for (const entry& dEntry : caching_.fieldsDict())
{
const dictionary& subDict = dEntry.dict();
const string varType(subDict.get("type"));
const word varName
(
subDict.getOrDefault
(
"name",
dEntry.keyword() // fieldName as fallback
)
);
osCase
<< varType.c_str()
<<
(
true // this->isPointData()
? " per node: 1 " // time-set 1
: " per element: 1 " // time-set 1
)
<< setw(15) << varName << ' '
<< mask << ensight::FileName(varName).c_str() << nl;
}
osCase
<< nl
<< "TIME" << nl;
ensightCase::printTimeset(osCase, 1, caching_.times());
if (tsGeom == 2)
{
ensightCase::printTimeset
(
osCase,
tsGeom,
caching_.times(),
caching_.geometries()
);
}
osCase << "# end" << nl;
}
// Timestamp in the directory for future reference
{
OFstream timeStamp(dataDir/"time");
timeStamp
<< "# timestep time" << nl
<< dataDir.name() << ' ' << timeValue << nl;
}
}
wroteGeom_ = true;
return outputFile;
}
// ************************************************************************* //