/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2015 OpenFOAM Foundation Copyright (C) 2016-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 . \*---------------------------------------------------------------------------*/ #include "ensightFile.H" #include "ensightReadFile.H" #include "error.H" #include "List.H" #include #include // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // bool Foam::ensightFile::allowUndef_ = false; float Foam::ensightFile::undefValue_ = Foam::floatScalarVGREAT; const char* const Foam::ensightFile::coordinates = "coordinates"; // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // namespace Foam { // Put integers, floats etc in binary or ascii. template static inline void putPrimitive ( const Type& value, OFstream& os, const int fieldWidth ) { auto& oss = os.stdStream(); if (os.format() == IOstreamOption::BINARY) { oss.write(reinterpret_cast(&value), sizeof(Type)); } else { oss.width(fieldWidth); oss << value; } os.syncState(); } } // End namespace Foam // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // bool Foam::ensightFile::hasUndef(const UList& field) { for (const float val : field) { if (std::isnan(val)) { return true; } } return true; } bool Foam::ensightFile::hasUndef(const UList& field) { for (const double val : field) { if (std::isnan(val)) { return true; } } return true; } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void Foam::ensightFile::init() { // The ASCII formatting specs for ensight files setf ( std::ios_base::scientific, std::ios_base::floatfield ); precision(5); // Handle transient single-file timestep information auto& oss = OFstream::stdStream(); if (OFstream::is_appending()) { // Already positioned at the EOF (in append mode), but be certain oss.seekp(0, std::ios_base::end); origFileSize_ = oss.tellp(); } else { origFileSize_ = 0; } int64_t begin_footer(-1); List offsets; if (OFstream::is_appending()) { // Temporarily open for reading as well. // No race condition since no writing is done concurrently with the // reading IFstream is(OFstream::name(), OFstream::format()); begin_footer = ensightReadFile::getTimeStepFooter ( is, offsets ); } timeStepOffsets_ = std::move(offsets); if (OFstream::is_appending() && begin_footer > 0) { oss.seekp(begin_footer); OFstream::syncState(); } // InfoErr << "output at: " << label(begin_footer) << nl; // InfoErr // << "footer: " << label(begin_footer) // << " time-steps: " << offsets.size() << nl; } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::ensightFile::ensightFile ( std::nullptr_t, // dispatch tag IOstreamOption::appendType append, const fileName& pathname, IOstreamOption::streamFormat fmt ) : OFstream ( ( // Only use atomic when not appending (append == IOstreamOption::NO_APPEND) ? IOstreamOption::ATOMIC : IOstreamOption::NON_ATOMIC ), pathname, fmt, ( // Change APPEND_APP -> APPEND_ATE (file rewriting) (append == IOstreamOption::APPEND_APP) ? IOstreamOption::APPEND_ATE : append ) ), origFileSize_(0) { init(); } Foam::ensightFile::ensightFile ( IOstreamOption::appendType append, const fileName& pathname, IOstreamOption::streamFormat fmt ) : ensightFile ( nullptr, append, ensight::FileName(pathname), fmt ) {} Foam::ensightFile::ensightFile ( IOstreamOption::appendType append, const fileName& path, const fileName& name, IOstreamOption::streamFormat fmt ) : ensightFile ( nullptr, append, path/ensight::FileName(name), fmt ) {} // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::ensightFile::~ensightFile() { (void) writeTimeStepFooter(); } // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // bool Foam::ensightFile::allowUndef() noexcept { return allowUndef_; } // float Foam::ensightFile::undefValue() noexcept // { // return undefValue_; // } bool Foam::ensightFile::allowUndef(bool on) noexcept { bool old = allowUndef_; allowUndef_ = on; return old; } float Foam::ensightFile::undefValue(float value) noexcept { // enable its use too allowUndef_ = true; float old = undefValue_; undefValue_ = value; return old; } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void Foam::ensightFile::writeString(const char* str, size_t len) { // Output 79 chars (ASCII) or 80 chars (BINARY) char buf[80]; if (len > 80) len = 80; // TBD: truncate at newline? (shouldn't really occur anyhow) std::copy_n(str, len, buf); std::fill_n(buf + len, (80 - len), '\0'); // Pad trailing with nul auto& oss = stdStream(); if (format() == IOstreamOption::BINARY) { oss.write(buf, 80); } else { buf[79] = 0; // Max 79 in ASCII // TBD: Extra safety - trap newline in ASCII? // char* p = ::strchr(buf, '\n'); // if (p) *p = 0; oss << buf; } syncState(); } void Foam::ensightFile::writeString(const char* str) { writeString(str, strlen(str)); } void Foam::ensightFile::writeString(const std::string& str) { writeString(str.data(), str.size()); } Foam::Ostream& Foam::ensightFile::write(const char* str) { writeString(str, strlen(str)); return *this; } Foam::Ostream& Foam::ensightFile::write(const word& str) { writeString(str.data(), str.size()); return *this; } Foam::Ostream& Foam::ensightFile::write(const std::string& str) { writeString(str.data(), str.size()); return *this; } // Same as OFstream::writeRaw(buf, count) Foam::Ostream& Foam::ensightFile::write ( const char* buf, std::streamsize count ) { stdStream().write(buf, count); syncState(); return *this; } void Foam::ensightFile::writeInt(const int32_t val, const int fieldWidth) { putPrimitive(val, *this, fieldWidth); } void Foam::ensightFile::writeInt(const int64_t val, const int fieldWidth) { putPrimitive(narrowInt32(val), *this, fieldWidth); } void Foam::ensightFile::writeFloat(const float val, const int fieldWidth) { putPrimitive(val, *this, fieldWidth); } void Foam::ensightFile::writeFloat(const double val, const int fieldWidth) { putPrimitive(narrowFloat(val), *this, fieldWidth); } Foam::Ostream& Foam::ensightFile::write(const int32_t val) { putPrimitive(val, *this, 10); return *this; } Foam::Ostream& Foam::ensightFile::write(const int64_t val) { putPrimitive(narrowInt32(val), *this, 10); return *this; } Foam::Ostream& Foam::ensightFile::write(const float val) { putPrimitive(val, *this, 12); return *this; } Foam::Ostream& Foam::ensightFile::write(const double val) { putPrimitive(narrowFloat(val), *this, 12); return *this; } void Foam::ensightFile::newline() { if (format() == IOstreamOption::ASCII) { OFstream::write('\n'); } } void Foam::ensightFile::writeUndef() { write(undefValue_); } Foam::Ostream& Foam::ensightFile::writeKeyword(const keyType& key) { if (allowUndef_) { writeString(key + " undef"); newline(); write(undefValue_); newline(); } else { writeString(key); newline(); } return *this; } void Foam::ensightFile::writeBinaryHeader() { if (format() == IOstreamOption::BINARY) { writeString("C Binary"); // newline(); // A no-op in binary } } // Footer information looks like this // /* |---------------|---------------|-----------------------| * | ASCII | BINARY | element | * |---------------|---------------|-----------------------| * | "%20lld\n" | int32 | nSteps | * | "%20lld\n" | int64 | offset step 1 | * | "%20lld\n" | int64 | offset step 2 | * | "%20lld\n" | .. | | * | "%20lld\n" | int64 | offset step n | * | "%20lld\n" | int32 | flag (unused) | * | "%20lld\n" | int64 | offset to nSteps | * | "%s\n" | char[80] | 'FILE_INDEX' | * |---------------|---------------|-----------------------| */ int64_t Foam::ensightFile::writeTimeStepFooter() { if (timeStepOffsets_.empty()) { return -1; } auto& oss = OFstream::stdStream(); // The footer begin, which is also the current position const int64_t footer_begin(oss.tellp()); // nSteps putPrimitive(int32_t(timeStepOffsets_.size()), *this, 20); newline(); // offset step 1, 2, ... N for (int64_t off : timeStepOffsets_) { putPrimitive(off, *this, 20); newline(); } // flag (unused) putPrimitive(0, *this, 20); newline(); // The footer begin == position of nSteps putPrimitive(footer_begin, *this, 20); newline(); // FILE_INDEX is "%s\n", not "%79s\n" // but our ASCII strings are truncated (nul-padded) anyhow writeString("FILE_INDEX"); newline(); // Reposition to begin of footer so that any subsequent output // will overwrite the footer too oss.seekp(footer_begin); return footer_begin; } // // Convenience Output Methods // int64_t Foam::ensightFile::beginTimeStep() { writeString("BEGIN TIME STEP"); newline(); auto& oss = OFstream::stdStream(); const int64_t curr_pos(oss.tellp()); timeStepOffsets_.push_back(curr_pos); // To avoid partly incomplete/incorrect footer information, // overwrite original footer if needed. if (curr_pos >= 0 && curr_pos < origFileSize_) { const char fill[] = "deadbeef"; for ( int64_t pos = curr_pos; pos < origFileSize_ && bool(oss); pos += 8 ) { // Overwrite with specified "junk" to avoid/detect corrupt // files etc. Don't worry about slightly increasing the // file size (ie, max 7 bytes) - it's unimportant oss.write(fill, 8); } // Maintain the original output position oss.seekp(curr_pos); OFstream::syncState(); } return curr_pos; } int64_t Foam::ensightFile::endTimeStep() { writeString("END TIME STEP"); newline(); return int64_t(stdStream().tellp()); } void Foam::ensightFile::beginPart(const label index) { writeString("part"); newline(); write(index+1); // Ensight starts with 1 newline(); } void Foam::ensightFile::beginPart ( const label index, const std::string& description ) { beginPart(index); writeString(description); newline(); } void Foam::ensightFile::beginCoordinates(const label npoints) { writeString("coordinates"); newline(); write(npoints); newline(); } void Foam::ensightFile::beginParticleCoordinates(const label nparticles) { writeString("particle coordinates"); newline(); writeInt(nparticles, 8); // Warning: unusual width newline(); } void Foam::ensightFile::writeLabels(const UList