From 956fb4ca3a134d59c3afd88a091364874d870a80 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 | 72 ++++++++++++++----- .../surfaceFormats/nas/NASsurfaceFormat.C | 69 +++++++++++++----- .../writers/nastran/nastranSurfaceWriter.C | 20 ++---- .../writers/nastran/nastranSurfaceWriter.H | 25 ++++--- 6 files changed, 155 insertions(+), 87 deletions(-) diff --git a/src/fileFormats/nastran/NASCore.C b/src/fileFormats/nastran/NASCore.C index 26c056e391..21842a7723 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-2022 OpenCFD Ltd. + Copyright (C) 2017-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -122,7 +122,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; @@ -130,15 +131,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); } @@ -235,8 +244,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 0fd8e9b160..05a351cbc8 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-2022 OpenCFD Ltd. + Copyright (C) 2017-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -48,7 +48,6 @@ SourceFiles namespace Foam { - namespace fileFormats { @@ -74,18 +73,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 @@ -93,18 +92,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 9940ccf093..44b6290cf0 100644 --- a/src/meshTools/edgeMesh/edgeFormats/nastran/NASedgeFormat.C +++ b/src/meshTools/edgeMesh/edgeFormats/nastran/NASedgeFormat.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2017 OpenCFD Ltd. + Copyright (C) 2017-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -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,38 +98,66 @@ 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); + + label a = readLabel(nextNasField(line, linei, 8, freeFormat)); + label b = readLabel(nextNasField(line, linei, 8, freeFormat)); dynEdges.append(edge(a,b)); } else if (cmd == "PLOTEL") { - // discard elementId (8-16) - (void) nextNasField(line, linei, 8); // 8-16 + // Fixed format: + // 8-16 : element id + // 16-24 : vertex + // 24-32 : vertex + // 32-40 : vertex - label a = readLabel(nextNasField(line, linei, 8)); // 16-24 - label b = readLabel(nextNasField(line, linei, 8)); // 24-32 + // discard elementId (8-16) + (void) nextNasField(line, linei, 8, freeFormat); + + label a = readLabel(nextNasField(line, linei, 8, freeFormat)); + label b = readLabel(nextNasField(line, linei, 8, freeFormat)); dynEdges.append(edge(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) + + 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.append(index); dynPoints.append(point(x, y, z)); @@ -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 diff --git a/src/surfMesh/surfaceFormats/nas/NASsurfaceFormat.C b/src/surfMesh/surfaceFormats/nas/NASsurfaceFormat.C index 225cc3752a..00a84b84ba 100644 --- a/src/surfMesh/surfaceFormats/nas/NASsurfaceFormat.C +++ b/src/surfMesh/surfaceFormats/nas/NASsurfaceFormat.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2015 OpenFOAM Foundation - Copyright (C) 2017-2022 OpenCFD Ltd. + Copyright (C) 2017-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -149,7 +149,6 @@ bool Foam::fileFormats::NASsurfaceFormat::read string line; while (is.good()) { - string::size_type linei = 0; // Parsing position within current line is.getLine(line); // ANSA extension @@ -223,16 +222,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); @@ -261,12 +274,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); @@ -310,11 +331,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.append(index); dynPoints.append(point(x, y, z)); @@ -327,6 +358,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 diff --git a/src/surfMesh/writers/nastran/nastranSurfaceWriter.C b/src/surfMesh/writers/nastran/nastranSurfaceWriter.C index b1b2c7be57..ad84fb938f 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 15d51da629..50c3599296 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