From 69b2a667c96e06a217a534884a8b5b96da467a6d Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Tue, 25 Jun 2024 14:20:17 +0200 Subject: [PATCH] BUG: Nastran reading of free format truncates last field (fixes #3189) - the old logic relied on the presence/absence of a comma to decide whether to parse as fixed or free format. This logic is faulty when handling the final (trailing) entry and will generally lead to the last field being truncated when read in. Now the caller decides on fixed vs free. FIX: inconsistent Nastran surface output format - use FREE format by default. Previously had an odd mix of SHORT format when created without options and LONG format (as default) when created with format options. --- src/fileFormats/nastran/NASCore.C | 27 ++++--- src/fileFormats/nastran/NASCore.H | 29 +++---- .../edgeFormats/nastran/NASedgeFormat.C | 80 +++++++++++++------ .../surfaceFormats/nas/NASsurfaceFormat.C | 73 ++++++++++++----- .../writers/nastran/nastranSurfaceWriter.C | 20 ++--- .../writers/nastran/nastranSurfaceWriter.H | 25 +++--- 6 files changed, 162 insertions(+), 92 deletions(-) diff --git a/src/fileFormats/nastran/NASCore.C b/src/fileFormats/nastran/NASCore.C index 80d8117bca..8713f243bd 100644 --- a/src/fileFormats/nastran/NASCore.C +++ b/src/fileFormats/nastran/NASCore.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2017-2023 OpenCFD Ltd. + Copyright (C) 2017-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -136,7 +136,8 @@ std::string Foam::fileFormats::NASCore::nextNasField ( const std::string& str, std::string::size_type& pos, - std::string::size_type len + const std::string::size_type width, + const bool free_format ) { const auto beg = pos; @@ -144,15 +145,23 @@ std::string Foam::fileFormats::NASCore::nextNasField if (end == std::string::npos) { - pos = beg + len; // Continue after field width + if (free_format) + { + // Nothing left + pos = str.size(); + return str.substr(beg); + } + + // Fixed format - continue after field width + pos = beg + width; + return str.substr(beg, width); } else { - len = (end - beg); // Efffective width - pos = end + 1; // Continue after comma + // Free format - continue after comma + pos = end + 1; + return str.substr(beg, (end - beg)); } - - return str.substr(beg, len); } @@ -249,8 +258,8 @@ void Foam::fileFormats::NASCore::writeCoord // 2 ID : point ID - requires starting index of 1 // 3 CP : coordinate system ID (blank) // 4 X1 : point x coordinate - // 5 X2 : point x coordinate - // 6 X3 : point x coordinate + // 5 X2 : point y coordinate + // 6 X3 : point z coordinate // 7 CD : coordinate system for displacements (blank) // 8 PS : single point constraints (blank) // 9 SEID : super-element ID diff --git a/src/fileFormats/nastran/NASCore.H b/src/fileFormats/nastran/NASCore.H index 422127fefe..fc72ede6fa 100644 --- a/src/fileFormats/nastran/NASCore.H +++ b/src/fileFormats/nastran/NASCore.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2017-2023 OpenCFD Ltd. + Copyright (C) 2017-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -47,7 +47,6 @@ SourceFiles namespace Foam { - namespace fileFormats { @@ -79,18 +78,18 @@ public: //- Output load format enum loadFormat { - PLOAD2, - PLOAD4 + PLOAD2, //!< Face load (eg, pressure) + PLOAD4 //!< Vertex load }; - //- Selection names for the NASTRAN file field formats + //- Selection names for the NASTRAN load formats static const Enum loadFormatNames; // Constructors //- Default construct - NASCore() = default; + NASCore() noexcept = default; // Public Static Member Functions @@ -98,18 +97,20 @@ public: //- Extract numbers from things like "-2.358-8" (same as "-2.358e-8") static scalar readNasScalar(const std::string& str); - //- A string::substr() to handle fixed-format and free-format NASTRAN. - // Returns the substr to the next comma (if found) or the given length - // - // \param str The string to extract from - // \param pos On input, the position of the first character of the - // substring. On output, advances to the next position to use. - // \param len The fixed-format length to use if a comma is not found. + //- A std::string::substr() variant to handle fixed-format and + //- free-format NASTRAN. + // Returns the substr until the next comma (if found) + // or the given fixed width static std::string nextNasField ( + //! The string to extract from const std::string& str, + //! [in,out] The parse position within \p str std::string::size_type& pos, - std::string::size_type len + //! The fixed-format width to use (if comma is not found) + const std::string::size_type width, + //! The input is known to be free-format + const bool free_format = false ); diff --git a/src/meshTools/edgeMesh/edgeFormats/nastran/NASedgeFormat.C b/src/meshTools/edgeMesh/edgeFormats/nastran/NASedgeFormat.C index 916d898cd6..62e1fe1b2b 100644 --- a/src/meshTools/edgeMesh/edgeFormats/nastran/NASedgeFormat.C +++ b/src/meshTools/edgeMesh/edgeFormats/nastran/NASedgeFormat.C @@ -62,13 +62,17 @@ bool Foam::fileFormats::NASedgeFormat::read while (is.good()) { - string::size_type linei = 0; // parsing position within current line string line; is.getLine(line); - if (line.empty() || line[0] == '$') + if (line.empty()) { - continue; // Skip empty or comment + continue; // Ignore empty + } + else if (line[0] == '$') + { + // Ignore comment + continue; } // Check if character 72 is continuation @@ -94,41 +98,69 @@ bool Foam::fileFormats::NASedgeFormat::read } + // Parsing position within current line + std::string::size_type linei = 0; + + // Is free format if line contains a comma + const bool freeFormat = line.contains(','); + // First word (column 0-8) const word cmd(word::validate(nextNasField(line, linei, 8))); if (cmd == "CBEAM" || cmd == "CROD") { - // discard elementId (8-16) - (void) nextNasField(line, linei, 8); // 8-16 - // discard groupId (16-24) - (void) nextNasField(line, linei, 8); // 16-24 + // Fixed format: + // 8-16 : element id + // 16-24 : group id + // 24-32 : vertex + // 32-40 : vertex - label a = readLabel(nextNasField(line, linei, 8)); // 24-32 - label b = readLabel(nextNasField(line, linei, 8)); // 32-40 + // discard elementId + (void) nextNasField(line, linei, 8, freeFormat); + // discard groupId + (void) nextNasField(line, linei, 8, freeFormat); - dynEdges.append(edge(a,b)); + label a = readLabel(nextNasField(line, linei, 8, freeFormat)); + label b = readLabel(nextNasField(line, linei, 8, freeFormat)); + + dynEdges.emplace_back(a,b); } else if (cmd == "PLOTEL") { + // Fixed format: + // 8-16 : element id + // 16-24 : vertex + // 24-32 : vertex + // 32-40 : vertex + // discard elementId (8-16) - (void) nextNasField(line, linei, 8); // 8-16 + (void) nextNasField(line, linei, 8, freeFormat); - label a = readLabel(nextNasField(line, linei, 8)); // 16-24 - label b = readLabel(nextNasField(line, linei, 8)); // 24-32 + label a = readLabel(nextNasField(line, linei, 8, freeFormat)); + label b = readLabel(nextNasField(line, linei, 8, freeFormat)); - dynEdges.append(edge(a,b)); + dynEdges.emplace_back(a,b); } else if (cmd == "GRID") { - label index = readLabel(nextNasField(line, linei, 8)); // 8-16 - (void) nextNasField(line, linei, 8); // 16-24 - scalar x = readNasScalar(nextNasField(line, linei, 8)); // 24-32 - scalar y = readNasScalar(nextNasField(line, linei, 8)); // 32-40 - scalar z = readNasScalar(nextNasField(line, linei, 8)); // 40-48 + // Fixed (short) format: + // 8-16 : point id + // 16-24 : coordinate system (unsupported) + // 24-32 : point x coordinate + // 32-40 : point y coordinate + // 40-48 : point z coordinate + // 48-56 : displacement coordinate system (optional, unsupported) + // 56-64 : single point constraints (optional, unsupported) + // 64-70 : super-element id (optional, unsupported) - pointId.append(index); - dynPoints.append(point(x, y, z)); + label index = readLabel(nextNasField(line, linei, 8, freeFormat)); + (void) nextNasField(line, linei, 8, freeFormat); + scalar x = readNasScalar(nextNasField(line, linei, 8, freeFormat)); + scalar y = readNasScalar(nextNasField(line, linei, 8, freeFormat)); + scalar z = readNasScalar(nextNasField(line, linei, 8, freeFormat)); + + pointId.push_back(index); + dynPoints.emplace_back(x, y, z); } else if (cmd == "GRID*") { @@ -138,6 +170,8 @@ bool Foam::fileFormats::NASedgeFormat::read // GRID* 126 0 -5.55999875E+02 -5.68730474E+02 // * 2.14897901E+02 + // Cannot be long format and free format at the same time! + label index = readLabel(nextNasField(line, linei, 16)); // 8-24 (void) nextNasField(line, linei, 16); // 24-40 scalar x = readNasScalar(nextNasField(line, linei, 16)); // 40-56 @@ -157,8 +191,8 @@ bool Foam::fileFormats::NASedgeFormat::read (void) nextNasField(line, linei, 8); // 0-8 scalar z = readNasScalar(nextNasField(line, linei, 16)); // 8-16 - pointId.append(index); - dynPoints.append(point(x, y, z)); + pointId.push_back(index); + dynPoints.emplace_back(x, y, z); } } diff --git a/src/surfMesh/surfaceFormats/nas/NASsurfaceFormat.C b/src/surfMesh/surfaceFormats/nas/NASsurfaceFormat.C index a836708018..0e19dbc55a 100644 --- a/src/surfMesh/surfaceFormats/nas/NASsurfaceFormat.C +++ b/src/surfMesh/surfaceFormats/nas/NASsurfaceFormat.C @@ -183,8 +183,6 @@ bool Foam::fileFormats::NASsurfaceFormat::read while (is.good()) { - // Parsing position within current line - std::string::size_type linei = 0; is.getLine(line); if (NASCore::debug > 1) Info<< "Process: " << line << nl; @@ -320,16 +318,30 @@ bool Foam::fileFormats::NASsurfaceFormat::read } } + + // Parsing position within current line + std::string::size_type linei = 0; + + // Is free format if line contains a comma + const bool freeFormat = line.contains(','); + // First word (column 0-8) const word cmd(word::validate(nextNasField(line, linei, 8))); if (cmd == "CTRIA3") { - label elemId = readLabel(nextNasField(line, linei, 8)); // 8-16 - label groupId = readLabel(nextNasField(line, linei, 8)); // 16-24 - const auto a = readLabel(nextNasField(line, linei, 8)); // 24-32 - const auto b = readLabel(nextNasField(line, linei, 8)); // 32-40 - const auto c = readLabel(nextNasField(line, linei, 8)); // 40-48 + // Fixed format: + // 8-16 : element id + // 16-24 : group id + // 24-32 : vertex + // 32-40 : vertex + // 40-48 : vertex + + label elemId = readLabel(nextNasField(line, linei, 8, freeFormat)); + label groupId = readLabel(nextNasField(line, linei, 8, freeFormat)); + const auto a = readLabel(nextNasField(line, linei, 8, freeFormat)); + const auto b = readLabel(nextNasField(line, linei, 8, freeFormat)); + const auto c = readLabel(nextNasField(line, linei, 8, freeFormat)); // Convert groupId into zoneId const auto iterZone = zoneLookup.cfind(groupId); @@ -358,12 +370,20 @@ bool Foam::fileFormats::NASsurfaceFormat::read } else if (cmd == "CQUAD4") { - label elemId = readLabel(nextNasField(line, linei, 8)); // 8-16 - label groupId = readLabel(nextNasField(line, linei, 8)); // 16-24 - const auto a = readLabel(nextNasField(line, linei, 8)); // 24-32 - const auto b = readLabel(nextNasField(line, linei, 8)); // 32-40 - const auto c = readLabel(nextNasField(line, linei, 8)); // 40-48 - const auto d = readLabel(nextNasField(line, linei, 8)); // 48-56 + // Fixed format: + // 8-16 : element id + // 16-24 : group id + // 24-32 : vertex + // 32-40 : vertex + // 40-48 : vertex + // 48-56 : vertex + + label elemId = readLabel(nextNasField(line, linei, 8, freeFormat)); + label groupId = readLabel(nextNasField(line, linei, 8, freeFormat)); + const auto a = readLabel(nextNasField(line, linei, 8, freeFormat)); + const auto b = readLabel(nextNasField(line, linei, 8, freeFormat)); + const auto c = readLabel(nextNasField(line, linei, 8, freeFormat)); + const auto d = readLabel(nextNasField(line, linei, 8, freeFormat)); // Convert groupId into zoneId const auto iterZone = zoneLookup.cfind(groupId); @@ -407,11 +427,21 @@ bool Foam::fileFormats::NASsurfaceFormat::read } else if (cmd == "GRID") { - label index = readLabel(nextNasField(line, linei, 8)); // 8-16 - (void) nextNasField(line, linei, 8); // 16-24 - scalar x = readNasScalar(nextNasField(line, linei, 8)); // 24-32 - scalar y = readNasScalar(nextNasField(line, linei, 8)); // 32-40 - scalar z = readNasScalar(nextNasField(line, linei, 8)); // 40-48 + // Fixed (short) format: + // 8-16 : point id + // 16-24 : coordinate system (not supported) + // 24-32 : point x coordinate + // 32-40 : point y coordinate + // 40-48 : point z coordinate + // 48-56 : displacement coordinate system (optional, unsupported) + // 56-64 : single point constraints (optional, unsupported) + // 64-70 : super-element id (optional, unsupported) + + label index = readLabel(nextNasField(line, linei, 8, freeFormat)); + (void) nextNasField(line, linei, 8, freeFormat); + scalar x = readNasScalar(nextNasField(line, linei, 8, freeFormat)); + scalar y = readNasScalar(nextNasField(line, linei, 8, freeFormat)); + scalar z = readNasScalar(nextNasField(line, linei, 8, freeFormat)); pointId.push_back(index); dynPoints.emplace_back(x, y, z); @@ -424,6 +454,8 @@ bool Foam::fileFormats::NASsurfaceFormat::read // GRID* 126 0 -5.55999875E+02 -5.68730474E+02 // * 2.14897901E+02 + // Cannot be long format and free format at the same time! + label index = readLabel(nextNasField(line, linei, 16)); // 8-24 (void) nextNasField(line, linei, 16); // 24-40 scalar x = readNasScalar(nextNasField(line, linei, 16)); // 40-56 @@ -453,7 +485,10 @@ bool Foam::fileFormats::NASsurfaceFormat::read // have the 'weird' format where the immediately preceeding // comment contains the information. - label groupId = readLabel(nextNasField(line, linei, 8)); // 8-16 + // Fixed format: + // 8-16 : pshell id + + label groupId = readLabel(nextNasField(line, linei, 8, freeFormat)); if (lastComment.size() > 1 && !nameLookup.contains(groupId)) { diff --git a/src/surfMesh/writers/nastran/nastranSurfaceWriter.C b/src/surfMesh/writers/nastran/nastranSurfaceWriter.C index e2e0534264..b818af6f04 100644 --- a/src/surfMesh/writers/nastran/nastranSurfaceWriter.C +++ b/src/surfMesh/writers/nastran/nastranSurfaceWriter.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2012-2016 OpenFOAM Foundation - Copyright (C) 2015-2022 OpenCFD Ltd. + Copyright (C) 2015-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -307,16 +307,10 @@ void Foam::surfaceWriters::nastranWriter::writeGeometry Foam::surfaceWriters::nastranWriter::nastranWriter() : surfaceWriter(), - writeFormat_(fieldFormat::SHORT), - fieldMap_(), + writeFormat_(fieldFormat::FREE), commonGeometry_(false), - separator_() -{ - // if (writeFormat_ == fieldFormat::FREE) - // { - // separator_ = ","; - // } -} + separator_(",") // FREE format +{} Foam::surfaceWriters::nastranWriter::nastranWriter @@ -331,12 +325,10 @@ Foam::surfaceWriters::nastranWriter::nastranWriter ( "format", options, - fieldFormat::LONG + fieldFormat::FREE ) ), - fieldMap_(), - commonGeometry_(options.getOrDefault("commonGeometry", false)), - separator_() + commonGeometry_(options.getOrDefault("commonGeometry", false)) { if (writeFormat_ == fieldFormat::FREE) { diff --git a/src/surfMesh/writers/nastran/nastranSurfaceWriter.H b/src/surfMesh/writers/nastran/nastranSurfaceWriter.H index 1cc4a4d8d2..8f8aeba8c1 100644 --- a/src/surfMesh/writers/nastran/nastranSurfaceWriter.H +++ b/src/surfMesh/writers/nastran/nastranSurfaceWriter.H @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2012-2016 OpenFOAM Foundation - Copyright (C) 2015-2022 OpenCFD Ltd. + Copyright (C) 2015-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -33,13 +33,13 @@ Description The formatOptions for nastran: \table Property | Description | Reqd | Default - fields | Field pairs for PLOAD2/PLOAD4 | yes | - format | Nastran format (short/long/free) | no | long + format | Nastran format (short/long/free) | no | free scale | Output geometry scaling | no | 1 transform | Output coordinate transform | no | fieldLevel | Subtract field level before scaling | no | empty dict fieldScale | Output field scaling | no | empty dict commonGeometry | use separate geometry files | no | false + fields | Field pairs for PLOAD2/PLOAD4 | yes | \endtable For example, @@ -48,13 +48,6 @@ Description { nastran { - // OpenFOAM field name to NASTRAN load types - fields - ( - (pMean PLOAD2) - (p PLOAD4) - ); - format free; // format type scale 1000; // [m] -> [mm] @@ -62,6 +55,13 @@ Description { "p.*" 0.01; // [Pa] -> [mbar] } + + // OpenFOAM field name to NASTRAN load types + fields + ( + (pMean PLOAD2) + (p PLOAD4) + ); } } \endverbatim @@ -93,7 +93,6 @@ Description Note Output variable scaling does not apply to integer types such as Ids. - Field pairs default to PLOAD2 for scalars and PLOAD4 for vectors etc. SourceFiles nastranSurfaceWriter.C @@ -221,10 +220,10 @@ public: // Constructors - //- Default construct. Default SHORT format + //- Default construct. Default FREE format nastranWriter(); - //- Construct with some output options. Default LONG format + //- Construct with some output options. Default FREE format explicit nastranWriter(const dictionary& options); //- Construct from components