ENH: limit vtk floatField range (fixes #1055)

- for space-savings the VTK fields are normally written as 'float'
  rather than double. When a double field contains very large values,
  these can result in a overflow when converted to float.

  Now trap these with the appropriate numeric limits.
  No warning when these values are clipped: it should be readily
  apparent from the output.

ENH: handle symmTensor component swapping directly on VTK output.

- use VTK output routines in vtkSurfaceWriter to benefit from the
  above changes
This commit is contained in:
Mark Olesen
2018-11-01 17:58:36 +00:00
parent 0ce7e364a4
commit 7cb51f5b98
8 changed files with 223 additions and 204 deletions

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2016-2017 OpenCFD Ltd. \\ / A nd | Copyright (C) 2016-2018 OpenCFD Ltd.
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -25,6 +25,7 @@ License
#include "foamVtkAppendRawFormatter.H" #include "foamVtkAppendRawFormatter.H"
#include "foamVtkOutputOptions.H" #include "foamVtkOutputOptions.H"
#include <limits>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -105,8 +106,21 @@ void Foam::vtk::appendRawFormatter::write(const float val)
void Foam::vtk::appendRawFormatter::write(const double val) void Foam::vtk::appendRawFormatter::write(const double val)
{ {
// std::cerr<<"double as float=" << val << '\n'; // std::cerr<<"double as float=" << val << '\n';
float copy(val);
write(copy); // Limit range of double to float conversion
if (val >= std::numeric_limits<float>::max())
{
write(std::numeric_limits<float>::max());
}
else if (val <= std::numeric_limits<float>::lowest())
{
write(std::numeric_limits<float>::lowest());
}
else
{
float copy(val);
write(copy);
}
} }

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2016-2017 OpenCFD Ltd. \\ / A nd | Copyright (C) 2016-2018 OpenCFD Ltd.
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -25,6 +25,7 @@ License
#include "foamVtkAsciiFormatter.H" #include "foamVtkAsciiFormatter.H"
#include "foamVtkOutputOptions.H" #include "foamVtkOutputOptions.H"
#include <limits>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -139,8 +140,20 @@ void Foam::vtk::asciiFormatter::write(const float val)
void Foam::vtk::asciiFormatter::write(const double val) void Foam::vtk::asciiFormatter::write(const double val)
{ {
next(); // Limit range of double to float conversion
os()<< float(val); if (val >= std::numeric_limits<float>::max())
{
write(std::numeric_limits<float>::max());
}
else if (val <= std::numeric_limits<float>::lowest())
{
write(std::numeric_limits<float>::lowest());
}
else
{
float copy(val);
write(copy);
}
} }

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd. \\ / A nd | Copyright (C) 2017-2018 OpenCFD Ltd.
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -24,6 +24,7 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "foamVtkBase64Layer.H" #include "foamVtkBase64Layer.H"
#include <limits>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -96,8 +97,21 @@ void Foam::vtk::foamVtkBase64Layer::write(const float val)
void Foam::vtk::foamVtkBase64Layer::write(const double val) void Foam::vtk::foamVtkBase64Layer::write(const double val)
{ {
// std::cerr<<"double as float=" << val << '\n'; // std::cerr<<"double as float=" << val << '\n';
float copy(val);
write(copy); // Limit range of double to float conversion
if (val >= std::numeric_limits<float>::max())
{
write(std::numeric_limits<float>::max());
}
else if (val <= std::numeric_limits<float>::lowest())
{
write(std::numeric_limits<float>::lowest());
}
else
{
float copy(val);
write(copy);
}
} }

View File

@ -2,7 +2,7 @@
========= | ========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2016-2017 OpenCFD Ltd. \\ / A nd | Copyright (C) 2016-2018 OpenCFD Ltd.
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
@ -26,6 +26,7 @@ License
#include "foamVtkLegacyRawFormatter.H" #include "foamVtkLegacyRawFormatter.H"
#include "foamVtkOutputOptions.H" #include "foamVtkOutputOptions.H"
#include "endian.H" #include "endian.H"
#include <limits>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -98,10 +99,7 @@ void Foam::vtk::legacyRawFormatter::write
} }
void Foam::vtk::legacyRawFormatter::write void Foam::vtk::legacyRawFormatter::write(const label val)
(
const label val
)
{ {
// std::cerr<<"label is:" << sizeof(val) << '\n'; // std::cerr<<"label is:" << sizeof(val) << '\n';
@ -121,10 +119,7 @@ void Foam::vtk::legacyRawFormatter::write
} }
void Foam::vtk::legacyRawFormatter::write void Foam::vtk::legacyRawFormatter::write(const float val)
(
const float val
)
{ {
// std::cerr<<"float is:" << sizeof(val) << '\n'; // std::cerr<<"float is:" << sizeof(val) << '\n';
@ -142,15 +137,25 @@ void Foam::vtk::legacyRawFormatter::write
} }
void Foam::vtk::legacyRawFormatter::write void Foam::vtk::legacyRawFormatter::write(const double val)
(
const double val
)
{ {
// Legacy cannot support Float64 anyhow. // Legacy cannot support Float64 anyhow.
// std::cerr<<"write double as float:" << val << '\n'; // std::cerr<<"write double as float:" << val << '\n';
float copy(val);
write(copy); // Limit range of double to float conversion
if (val >= std::numeric_limits<float>::max())
{
write(std::numeric_limits<float>::max());
}
else if (val <= std::numeric_limits<float>::lowest())
{
write(std::numeric_limits<float>::lowest());
}
else
{
float copy(val);
write(copy);
}
} }

View File

@ -48,6 +48,7 @@ SourceFiles
#include "foamVtkCore.H" #include "foamVtkCore.H"
#include "foamVtkFormatter.H" #include "foamVtkFormatter.H"
#include "floatScalar.H" #include "floatScalar.H"
#include "symmTensor.H"
#include "IOstream.H" #include "IOstream.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -231,6 +232,22 @@ namespace legacy
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Template specialization for symmTensor ordering
template<>
inline void write(vtk::formatter& fmt, const symmTensor& val)
{
// symmTensor ( XX, XY, XZ, YY, YZ, ZZ )
// VTK order ( XX, YY, ZZ, XY, YZ, XZ ) -> (0, 3, 5, 1, 4, 2)
fmt.write(component(val, 0)); // XX
fmt.write(component(val, 3)); // YY
fmt.write(component(val, 5)); // ZZ
fmt.write(component(val, 1)); // XY
fmt.write(component(val, 4)); // YZ
fmt.write(component(val, 2)); // XZ
}
} // End namespace vtk } // End namespace vtk
} // End namespace Foam } // End namespace Foam

View File

@ -25,9 +25,8 @@ License
#include "vtkSurfaceWriter.H" #include "vtkSurfaceWriter.H"
#include "foamVtkOutputOptions.H" #include "foamVtkOutputOptions.H"
#include "OFstream.H"
#include "OSspecific.H" #include "OSspecific.H"
#include <fstream>
#include "makeSurfaceWriterMethods.H" #include "makeSurfaceWriterMethods.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -49,7 +48,7 @@ defineSurfaceWriterWriteFields(Foam::vtkSurfaceWriter);
void Foam::vtkSurfaceWriter::writeGeometry void Foam::vtkSurfaceWriter::writeGeometry
( (
Ostream& os, vtk::formatter& format,
const meshedSurf& surf, const meshedSurf& surf,
std::string title std::string title
) )
@ -62,153 +61,40 @@ void Foam::vtkSurfaceWriter::writeGeometry
title = "sampleSurface"; title = "sampleSurface";
} }
// header vtk::legacy::fileHeader
os (
<< "# vtk DataFile Version 2.0" << nl format,
<< title.c_str() << nl title,
<< "ASCII" << nl vtk::fileTag::POLY_DATA
<< "DATASET POLYDATA" << nl; );
// Write vertex coords vtk::legacy::beginPoints(format.os(), points.size());
os << "POINTS " << points.size() << " double" << nl;
for (const point& pt : points)
{
os << float(pt.x()) << ' '
<< float(pt.y()) << ' '
<< float(pt.z()) << nl;
}
os << nl;
vtk::writeList(format, points);
format.flush();
// Write faces // Write faces
label nNodes = 0; // connectivity count without additional storage (done internally)
label nConnectivity = 0;
for (const face& f : faces) for (const face& f : faces)
{ {
nNodes += f.size(); nConnectivity += f.size();
} }
os << "POLYGONS " << faces.size() << ' ' vtk::legacy::beginPolys
<< faces.size() + nNodes << nl; (
format.os(),
faces.size(),
nConnectivity
);
for (const face& f : faces) for (const face& f : faces)
{ {
os << f.size(); format.write(f.size()); // The size prefix
for (const label verti : f) vtk::writeList(format, f);
{
os << ' ' << verti;
}
os << nl;
}
}
namespace Foam
{
template<>
void Foam::vtkSurfaceWriter::writeData
(
Ostream& os,
const Field<scalar>& values
)
{
os << "1 " << values.size() << " double" << nl;
forAll(values, elemI)
{
if (elemI)
{
if (elemI % 10)
{
os << ' ';
}
else
{
os << nl;
}
}
os << float(values[elemI]);
}
os << nl;
} }
format.flush();
template<>
void Foam::vtkSurfaceWriter::writeData
(
Ostream& os,
const Field<vector>& values
)
{
os << "3 " << values.size() << " double" << nl;
for (const vector& v : values)
{
os << float(v[0]) << ' '
<< float(v[1]) << ' '
<< float(v[2]) << nl;
}
}
template<>
void Foam::vtkSurfaceWriter::writeData
(
Ostream& os,
const Field<sphericalTensor>& values
)
{
os << "1 " << values.size() << " double" << nl;
for (const sphericalTensor& v : values)
{
os << float(v[0]) << nl;
}
}
template<>
void Foam::vtkSurfaceWriter::writeData
(
Ostream& os,
const Field<symmTensor>& values
)
{
os << "6 " << values.size() << " double" << nl;
// symmTensor ( XX, XY, XZ, YY, YZ, ZZ )
// VTK order ( XX, YY, ZZ, XY, YZ, XZ ) -> (0, 3, 5, 1, 4, 2)
for (const symmTensor& v : values)
{
os << float(v[0]) << ' ' << float(v[3]) << ' ' << float(v[5])
<< ' '
<< float(v[1]) << ' ' << float(v[4]) << ' ' << float(v[2])
<< nl;
}
}
template<>
void Foam::vtkSurfaceWriter::writeData
(
Ostream& os,
const Field<tensor>& values
)
{
os << "9 " << values.size() << " double" << nl;
for (const tensor& v : values)
{
os << float(v[0]) << ' ' << float(v[1]) << ' ' << float(v[2])
<< ' '
<< float(v[3]) << ' ' << float(v[4]) << ' ' << float(v[5])
<< ' '
<< float(v[6]) << ' ' << float(v[7]) << ' ' << float(v[8])
<< nl;
}
}
} }
@ -235,11 +121,14 @@ Foam::vtkSurfaceWriter::vtkSurfaceWriter(const dictionary& options)
vtk::outputOptions opts(static_cast<vtk::formatType>(fmtType_)); vtk::outputOptions opts(static_cast<vtk::formatType>(fmtType_));
opts.ascii const word formatName = options.lookupOrDefault<word>("format", "");
( if (formatName.size())
options.found("format") {
&& (IOstream::formatEnum(options.get<word>("format")) == IOstream::ASCII) opts.ascii
); (
IOstream::formatEnum(formatName) == IOstream::ASCII
);
}
if (options.lookupOrDefault("legacy", false)) if (options.lookupOrDefault("legacy", false))
{ {
@ -272,22 +161,29 @@ Foam::fileName Foam::vtkSurfaceWriter::write
{ {
// geometry: rootdir/time/surfaceName.{vtk|vtp} // geometry: rootdir/time/surfaceName.{vtk|vtp}
fileName outputFile(outputDir/surfaceName + ".vtk");
if (!isDir(outputDir)) if (!isDir(outputDir))
{ {
mkDir(outputDir); mkDir(outputDir);
} }
OFstream os(outputDir/surfaceName + ".vtk");
os.precision(precision_);
if (verbose) if (verbose)
{ {
Info<< "Writing geometry to " << os.name() << endl; Info<< "Writing geometry to " << outputFile << endl;
} }
writeGeometry(os, surf, surfaceName); // As vtk::outputOptions
vtk::outputOptions opts(static_cast<vtk::formatType>(fmtType_));
opts.legacy(true);
opts.precision(precision_);
return os.name(); std::ofstream os(outputFile);
auto format = opts.newFormatter(os);
writeGeometry(*format, surf, surfaceName);
return outputFile;
} }

View File

@ -85,6 +85,12 @@ SourceFiles
namespace Foam namespace Foam
{ {
namespace vtk
{
// Forward declarations
class formatter;
}
/*---------------------------------------------------------------------------*\ /*---------------------------------------------------------------------------*\
Class vtkSurfaceWriter Declaration Class vtkSurfaceWriter Declaration
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
@ -107,13 +113,17 @@ class vtkSurfaceWriter
static void writeGeometry static void writeGeometry
( (
Ostream& os, vtk::formatter& format,
const meshedSurf& surf, const meshedSurf& surf,
std::string title = "" std::string title = ""
); );
template<class Type> template<class Type>
static void writeData(Ostream&, const Field<Type>&); static void writeField
(
vtk::formatter& format,
const Field<Type>& values
);
//- Templated write operation //- Templated write operation

View File

@ -23,28 +23,69 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "OFstream.H" #include "foamVtkOutputOptions.H"
#include "OSspecific.H" #include "OSspecific.H"
#include <fstream>
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
template<class Type>
static inline void beginFloatField
(
vtk::formatter& format,
const word& fieldName,
const Field<Type>& values
)
{
// Use 'double' instead of 'float' for ASCII output (issue #891)
if (format.opts().ascii())
{
format.os()
<< fieldName << ' '
<< int(pTraits<Type>::nComponents) << ' '
<< values.size() << " double" << nl;
}
else
{
format.os()
<< fieldName << ' '
<< int(pTraits<Type>::nComponents) << ' '
<< values.size() << " float" << nl;
}
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
// // Unspecialized (unknown) data type - map as zeros
// void Foam::vtkSurfaceWriter::writeZeros
// (
// vtk::formatter& format,
// const label count
// )
// {
// const float val(0);
//
// for (label i=0; i < count; ++i)
// {
// format.write(val);
// }
// format.flush();
// }
template<class Type> template<class Type>
void Foam::vtkSurfaceWriter::writeData void Foam::vtkSurfaceWriter::writeField
( (
Ostream& os, vtk::formatter& format,
const Field<Type>& values const Field<Type>& values
) )
{ {
// Unspecialized (unknown) data type - map as zeros vtk::writeList(format, values);
format.flush();
const label len = values.size();
os << "1 " << len << " double" << nl;
for (label i=0; i < len; ++i)
{
os << float(0) << nl;
}
} }
@ -62,39 +103,48 @@ Foam::fileName Foam::vtkSurfaceWriter::writeTemplate
{ {
// field: rootdir/time/<field>_surfaceName.{vtk|vtp} // field: rootdir/time/<field>_surfaceName.{vtk|vtp}
fileName outputFile(outputDir/fieldName + '_' + surfaceName + ".vtk");
if (!isDir(outputDir)) if (!isDir(outputDir))
{ {
mkDir(outputDir); mkDir(outputDir);
} }
OFstream os(outputDir/fieldName + '_' + surfaceName + ".vtk");
os.precision(precision_);
if (verbose) if (verbose)
{ {
Info<< "Writing field " << fieldName << " to " << os.name() << endl; Info<< "Writing field " << fieldName << " to "
<< outputFile << endl;
} }
writeGeometry(os, surf); // As vtk::outputOptions
vtk::outputOptions opts(static_cast<vtk::formatType>(fmtType_));
opts.legacy(true);
opts.precision(precision_);
std::ofstream os(outputFile);
auto format = opts.newFormatter(os);
writeGeometry(*format, surf);
// start writing data
if (isNodeValues) if (isNodeValues)
{ {
os << "POINT_DATA "; format->os()
<< "POINT_DATA "
<< values.size() << nl
<< "FIELD attributes 1" << nl;
} }
else else
{ {
os << "CELL_DATA "; format->os()
<< "CELL_DATA "
<< values.size() << nl
<< "FIELD attributes 1" << nl;
} }
os << values.size() << nl beginFloatField(*format, fieldName, values);
<< "FIELD attributes 1" << nl writeField(*format, values);
<< fieldName << " ";
// Write data return outputFile;
writeData(os, values);
return os.name();
} }