ENH: vtk::fileWriter base class for geometry/field writers (issue #926)

- Output formats such as vtp, vtu follow a particular internal data
  structure (HEAD, FIELD_DATA, PIECE, CELL_DATA/POINT_DATA) and other
  output conventions. This writer base tracks these expected output
  states internally to help avoid logic errors in the callers.
This commit is contained in:
Mark Olesen
2018-09-29 13:14:50 +02:00
parent d837524860
commit ee0947b693
4 changed files with 872 additions and 0 deletions

View File

@ -16,6 +16,7 @@ stl/STLAsciiParseFlex.L
stl/STLAsciiParseManual.C
stl/STLAsciiParseRagel.C
vtk/file/foamVtkFileWriter.C
vtk/core/foamVtkCore.C
vtk/core/foamVtkPTraits.C

View File

@ -0,0 +1,474 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "foamVtkFileWriter.H"
#include "globalIndex.H"
#include "OSspecific.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
const Foam::Enum
<
Foam::vtk::fileWriter::outputState
>
Foam::vtk::fileWriter::stateNames
{
{ outputState::CLOSED, "closed" },
{ outputState::OPENED, "opened" },
{ outputState::DECLARED, "declared" },
{ outputState::FIELD_DATA, "FieldData" },
{ outputState::PIECE, "Piece" },
{ outputState::CELL_DATA, "CellData" },
{ outputState::POINT_DATA, "PointData" },
};
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
bool Foam::vtk::fileWriter::enter_Piece()
{
// Finish other output
endFieldData();
if (isState(outputState::OPENED))
{
beginFile();
}
if (notState(outputState::DECLARED))
{
FatalErrorInFunction
<< "Bad writer state (" << stateNames[state_]
<< ") - should be (" << stateNames[outputState::DECLARED] << ')'
<< exit(FatalError);
}
state_ = outputState::PIECE;
nCellData_ = nPointData_ = 0;
return true;
}
bool Foam::vtk::fileWriter::endPiece()
{
// Finish other output
endCellData();
endPointData();
if (notState(outputState::PIECE))
{
// Skip if not in Piece
return false;
}
state_ = outputState::DECLARED; // Mark as having been flushed
if (format_)
{
format().endPiece();
}
return true;
}
bool Foam::vtk::fileWriter::enter_CellData(label nEntries, label nFields)
{
// Already in CellData?
if (isState(outputState::CELL_DATA)) return false;
// Finish other output
endPointData();
if (notState(outputState::PIECE))
{
FatalErrorInFunction
<< "Bad writer state (" << stateNames[state_]
<< ") - should be (" << stateNames[outputState::PIECE] << ')'
<< exit(FatalError);
}
nCellData_ = 0;
// Do nothing for legacy when nFields == 0
if (legacy() && !nFields) return false;
state_ = outputState::CELL_DATA;
if (format_)
{
if (legacy())
{
legacy::beginCellData(format(), nEntries, nFields);
}
else
{
format().beginCellData();
}
}
return true;
}
bool Foam::vtk::fileWriter::enter_PointData(label nEntries, label nFields)
{
// Already in PointData?
if (isState(outputState::POINT_DATA)) return false;
// Finish other output
endCellData();
if (notState(outputState::PIECE))
{
FatalErrorInFunction
<< "Bad writer state (" << stateNames[state_]
<< ") - should be (" << stateNames[outputState::PIECE] << ')'
<< exit(FatalError);
}
nPointData_ = 0;
// Do nothing for legacy when nFields == 0
if (legacy() && !nFields) return false;
state_ = outputState::POINT_DATA;
if (format_)
{
if (legacy())
{
legacy::beginPointData(format(), nEntries, nFields);
}
else
{
format().beginPointData();
}
}
return true;
}
bool Foam::vtk::fileWriter::exit_File()
{
// Finish other output
endFieldData();
endPiece();
if (isState(outputState::DECLARED))
{
if (format_ && !legacy())
{
format().endTag(contentType_).endVTKFile();
}
state_ = outputState::OPENED; // Mark as having been flushed
}
// Must now be in CLOSED or OPENED states only
if (isState(outputState::CLOSED) || isState(outputState::OPENED))
{
return true;
}
WarningInFunction
<< "Bad writer state (" << stateNames[state_]
<< ") - should be (" << stateNames[outputState::CLOSED]
<< ") or (" << stateNames[outputState::OPENED]
<< ") for contentType (" << vtk::fileTagNames[contentType_]
<< nl << endl;
return false;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::vtk::fileWriter::fileWriter
(
const vtk::fileTag contentType,
const vtk::outputOptions opts
)
:
contentType_(contentType),
opts_(opts),
parallel_(false),
state_(outputState::CLOSED),
nCellData_(0),
nPointData_(0),
outputFile_(),
format_(),
os_()
{
// We do not currently support append mode at all
opts_.append(false);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::vtk::fileWriter::~fileWriter()
{
close();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::vtk::fileWriter::open(const fileName& file, bool parallel)
{
if (notState(outputState::CLOSED))
{
FatalErrorInFunction
<< "Bad writer state (" << stateNames[state_]
<< ") - should be (" << stateNames[outputState::CLOSED] << ')'
<< exit(FatalError);
}
if (format_)
{
format_.clear();
os_.close();
}
nCellData_ = nPointData_ = 0;
outputFile_ = file;
if
(
legacy()
? outputFile_.hasExt(vtk::fileExtension[contentType_])
: outputFile_.hasExt(vtk::legacy::fileExtension)
)
{
// Inappropriate extension. Legacy instead of xml, or vice versa.
outputFile_.removeExt();
}
if (!outputFile_.hasExt(ext()))
{
// Add extension if required
outputFile_.ext(ext());
}
// Only set parallel flag if really is a parallel run.
parallel_ = parallel && Pstream::parRun();
// Open a file and attach a formatter
// - on master (always)
// - on slave if not parallel
//
// This means we can always check if format_ is defined to know if output
// is desired on any particular process.
if (Pstream::master() || !parallel_)
{
mkDir(outputFile_.path());
os_.open(outputFile_);
format_ = opts_.newFormatter(os_);
}
state_ = outputState::OPENED;
return true;
}
void Foam::vtk::fileWriter::close()
{
exit_File();
if (format_)
{
format_.clear();
os_.close();
}
state_ = outputState::CLOSED;
outputFile_.clear();
nCellData_ = nPointData_ = 0;
}
bool Foam::vtk::fileWriter::beginFile(std::string title)
{
if (isState(outputState::DECLARED))
{
// Skip if already emitted
return false;
}
if (notState(outputState::OPENED))
{
FatalErrorInFunction
<< "Bad writer state (" << stateNames[state_]
<< ") - should be (" << stateNames[outputState::OPENED] << ')'
<< exit(FatalError);
}
state_ = outputState::DECLARED;
if (format_)
{
if (legacy())
{
legacy::fileHeader(format(), title, contentType_);
}
else
{
// XML (inline)
format().xmlHeader();
if (title.size())
{
format().xmlComment(title);
}
format().beginVTKFile(contentType_);
}
}
return true;
}
bool Foam::vtk::fileWriter::beginFieldData(label nFields)
{
// Do nothing for legacy when nFields == 0
if (legacy() && !nFields) return false;
if (isState(outputState::OPENED))
{
beginFile();
}
if (notState(outputState::DECLARED))
{
FatalErrorInFunction
<< "Bad writer state (" << stateNames[state_]
<< ") - should be (" << stateNames[outputState::DECLARED] << ')'
<< exit(FatalError);
}
state_ = outputState::FIELD_DATA;
if (format_)
{
if (legacy())
{
legacy::beginFieldData(format(), nFields);
}
else
{
format().beginFieldData();
}
}
return true;
}
bool Foam::vtk::fileWriter::endFieldData()
{
if (notState(outputState::FIELD_DATA))
{
// Skip if not in FieldData
return false;
}
state_ = outputState::DECLARED; // Toggle back to DECLARED
if (format_ && !legacy())
{
format().endFieldData();
}
return true;
}
bool Foam::vtk::fileWriter::endCellData()
{
if (notState(outputState::CELL_DATA))
{
// Skip if not in CellData
return false;
}
state_ = outputState::PIECE; // Toggle back to PIECE
if (format_ && !legacy())
{
format().endCellData();
}
return true;
}
bool Foam::vtk::fileWriter::endPointData()
{
if (notState(outputState::POINT_DATA))
{
// Skip if not in PointData
return false;
}
state_ = outputState::PIECE; // Toggle back to PIECE
if (format_ && !legacy())
{
format().endPointData();
}
return true;
}
void Foam::vtk::fileWriter::writeTimeValue(scalar timeValue)
{
// Convenience - switch to FieldData
if (isState(outputState::OPENED) || isState(outputState::DECLARED))
{
beginFieldData(1);
}
if (notState(outputState::FIELD_DATA))
{
FatalErrorInFunction
<< "Bad writer state (" << stateNames[state_]
<< ") - should be (" << stateNames[outputState::FIELD_DATA] << ')'
<< exit(FatalError);
}
// No collectives - can skip on slave processors
if (!format_) return;
if (legacy())
{
legacy::writeTimeValue(format(), timeValue);
}
else
{
format().writeTimeValue(timeValue);
}
}
// ************************************************************************* //

View File

@ -0,0 +1,289 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
Class
Foam::vtk::fileWriter
Description
Base class for VTK output writers that handle geometry and fields
(eg, vtp, vtu data).
These output formats are structured as DECLARED, FIELD_DATA, PIECE
followed by any CELL_DATA or POINT_DATA.
This writer base tracks these expected output states internally
to help avoid logic errors in the callers.
The FieldData element must be placed prior to writing any geometry
Piece. This moves the information to the front of the output file
for visibility and simplifies the logic when creating
multi-piece geometries.
SourceFiles
foamVtkFileWriter.C
\*---------------------------------------------------------------------------*/
#ifndef foamVtkFileWriter_H
#define foamVtkFileWriter_H
#include <fstream>
#include "Enum.H"
#include "UPstream.H"
#include "foamVtkOutputOptions.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace vtk
{
/*---------------------------------------------------------------------------*\
Class vtk::fileWriter Declaration
\*---------------------------------------------------------------------------*/
class fileWriter
{
protected:
// Protected Member Data
//- Internal tracking of the output state.
enum outputState
{
CLOSED = 0, //!< File is closed
OPENED, //!< File is opened
DECLARED, //!< File contents declared (VTKFile header written)
FIELD_DATA, //!< Inside FieldData
PIECE, //!< Inside Piece (after geometry write)
CELL_DATA, //!< Inside CellData
POINT_DATA //!< Inside PointData
};
//- Names for the output state (for messages, not for file output).
static const Enum<outputState> stateNames;
//- The content type
vtk::fileTag contentType_;
//- The requested output options
outputOptions opts_;
//- Writing in parallel (via master)
bool parallel_;
//- The output state
outputState state_;
//- The number of CellData written for the Piece thus far.
label nCellData_;
//- The number of PointData written for the Piece thus far.
label nPointData_;
//- The output file name
fileName outputFile_;
//- The VTK formatter in use (master process)
autoPtr<vtk::formatter> format_;
//- The backend ostream in use (master process)
std::ofstream os_;
// Protected Member Functions
//- The backend ostream in use
inline std::ofstream& os();
//- The VTK formatter in use
inline vtk::formatter& format();
//- True if the output state corresponds to the test state.
inline bool isState(outputState test) const;
//- True if the output state does not correspond to the test state.
inline bool notState(outputState test) const;
//- Trigger change state to Piece. Resets nCellData_, nPointData_.
bool enter_Piece();
//- Explicitly end Piece output and switch to DECLARED state
// Ignored (no-op) if not currently in the PIECE state.
bool endPiece();
//- Trigger change state to CellData.
// Legacy requires both parameters. XML doesn't require either.
//
// \return True if the state changed
bool enter_CellData(label nEntries, label nFields);
//- Trigger change state to PointData
// Legacy requires both parameters. XML doesn't require either.
//
// \return True if the state changed
bool enter_PointData(label nEntries, label nFields);
//- Emit file footer (end data, end piece, end file)
bool exit_File();
//- No copy construct
fileWriter(const fileWriter&) = delete;
//- No copy assignment
void operator=(const fileWriter&) = delete;
public:
// Constructors
//- Construct from components
fileWriter
(
const vtk::fileTag contentType,
const vtk::outputOptions opts
);
//- Destructor
virtual ~fileWriter();
// Member Functions
//- The content type
inline vtk::fileTag contentType() const;
//- The output options in use
inline vtk::outputOptions opts() const;
//- File extension for current format type.
inline word ext() const;
//- Commonly used query
inline bool legacy() const;
//- Parallel output requested?
inline bool parallel() const;
//- The output state in printable format
inline const word& state() const;
//- The current output file name
inline const fileName& output() const;
//- Open file for writing (creates parent directory).
// The file name is normally without an extension, this will be added
// according to the content-type and the output format (legacy/xml).
// If the file name has an extension, it will be used where if
// appropriate or changed to suit the format (legacy/xml) type.
// \note Expected calling states: (CLOSED).
bool open(const fileName& file, bool parallel=Pstream::parRun());
//- End the file contents and close the file after writing.
// \note Expected calling states: (PIECE | CELL_DATA | POINT_DATA).
void close();
//- Write file header (non-collective)
// \note Expected calling states: (OPENED)
virtual bool beginFile(std::string title = "");
//- Begin FieldData output section for specified number of fields.
// \param nFields is for legacy format only.
// When nFields=0, this a no-op for legacy format.
// \note Expected calling states: (OPENED | DECLARED).
bool beginFieldData(label nFields = 0);
//- Write mesh topology.
// Also writes the file header if not previously written.
// \note Must be called prior to writing CellData or PointData
virtual bool writeGeometry() = 0;
//- Begin CellData output section for specified number of fields.
// Must be called prior to writing any cell data fields.
// \param nFields is for legacy format only.
// When nFields=0, this a no-op for legacy format.
// \note Expected calling states: (PIECE | POINT_DATA).
//
// \return True if the state changed
virtual bool beginCellData(label nFields = 0) = 0;
//- Begin PointData for specified number of fields.
// Must be called prior to writing any point data fields.
// \param nFields is for legacy format only.
// When nFields=0, this a no-op for legacy format.
// \note Expected calling states: (PIECE | CELL_DATA).
//
// \return True if the state changed
virtual bool beginPointData(label nFields = 0) = 0;
//- Return the number of CellData written for the Piece thus far.
inline label nCellData() const;
//- Return the number of PointData written for the Piece thus far.
inline label nPointData() const;
//- Explicitly end FieldData output and switch to DECLARED state
// Ignored (no-op) if not currently in the FIELD_DATA state.
bool endFieldData();
//- Explicitly end CellData output and switch to PIECE state
// Ignored (no-op) if not currently in the CELL_DATA state.
bool endCellData();
//- Explicitly end PointData output and switch to PIECE state
// Ignored (no-op) if not currently in the POINT_DATA state.
bool endPointData();
//- Write "TimeValue" FieldData (name as per Catalyst output)
// Must be called within the FIELD_DATA state.
// \note As a convenience this can also be called from
// (OPENED | DECLARED) states, in which case it invokes
// beginFieldData(1) internally.
void writeTimeValue(scalar timeValue);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace vtk
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "foamVtkFileWriterI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,108 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
inline std::ofstream& Foam::vtk::fileWriter::os()
{
return os_;
}
inline Foam::vtk::formatter& Foam::vtk::fileWriter::format()
{
return *format_;
}
inline bool Foam::vtk::fileWriter::isState(outputState test) const
{
return (test == state_);
}
inline bool Foam::vtk::fileWriter::notState(outputState test) const
{
return (test != state_);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline Foam::vtk::fileTag Foam::vtk::fileWriter::contentType() const
{
return contentType_;
}
inline Foam::vtk::outputOptions Foam::vtk::fileWriter::opts() const
{
return opts_;
}
inline Foam::word Foam::vtk::fileWriter::ext() const
{
return opts_.ext(contentType_);
}
inline bool Foam::vtk::fileWriter::legacy() const
{
return opts_.legacy();
}
inline bool Foam::vtk::fileWriter::parallel() const
{
return parallel_;
}
inline const Foam::word& Foam::vtk::fileWriter::state() const
{
return stateNames[state_];
}
inline const Foam::fileName& Foam::vtk::fileWriter::output() const
{
return outputFile_;
}
inline Foam::label Foam::vtk::fileWriter::nCellData() const
{
return nCellData_;
}
inline Foam::label Foam::vtk::fileWriter::nPointData() const
{
return nPointData_;
}
// ************************************************************************* //