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.
This commit is contained in:
Mark Olesen 2024-06-25 14:20:17 +02:00
parent 905d63357c
commit 956fb4ca3a
6 changed files with 155 additions and 87 deletions

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2017-2022 OpenCFD Ltd. Copyright (C) 2017-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -122,7 +122,8 @@ std::string Foam::fileFormats::NASCore::nextNasField
( (
const std::string& str, const std::string& str,
std::string::size_type& pos, std::string::size_type& pos,
std::string::size_type len const std::string::size_type width,
const bool free_format
) )
{ {
const auto beg = pos; const auto beg = pos;
@ -130,15 +131,23 @@ std::string Foam::fileFormats::NASCore::nextNasField
if (end == std::string::npos) 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 else
{ {
len = (end - beg); // Efffective width // Free format - continue after comma
pos = end + 1; // 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 // 2 ID : point ID - requires starting index of 1
// 3 CP : coordinate system ID (blank) // 3 CP : coordinate system ID (blank)
// 4 X1 : point x coordinate // 4 X1 : point x coordinate
// 5 X2 : point x coordinate // 5 X2 : point y coordinate
// 6 X3 : point x coordinate // 6 X3 : point z coordinate
// 7 CD : coordinate system for displacements (blank) // 7 CD : coordinate system for displacements (blank)
// 8 PS : single point constraints (blank) // 8 PS : single point constraints (blank)
// 9 SEID : super-element ID // 9 SEID : super-element ID

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com \\ / A nd | www.openfoam.com
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2017-2022 OpenCFD Ltd. Copyright (C) 2017-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -48,7 +48,6 @@ SourceFiles
namespace Foam namespace Foam
{ {
namespace fileFormats namespace fileFormats
{ {
@ -74,18 +73,18 @@ public:
//- Output load format //- Output load format
enum loadFormat enum loadFormat
{ {
PLOAD2, PLOAD2, //!< Face load (eg, pressure)
PLOAD4 PLOAD4 //!< Vertex load
}; };
//- Selection names for the NASTRAN file field formats //- Selection names for the NASTRAN load formats
static const Enum<loadFormat> loadFormatNames; static const Enum<loadFormat> loadFormatNames;
// Constructors // Constructors
//- Default construct //- Default construct
NASCore() = default; NASCore() noexcept = default;
// Public Static Member Functions // Public Static Member Functions
@ -93,18 +92,20 @@ public:
//- Extract numbers from things like "-2.358-8" (same as "-2.358e-8") //- Extract numbers from things like "-2.358-8" (same as "-2.358e-8")
static scalar readNasScalar(const std::string& str); static scalar readNasScalar(const std::string& str);
//- A string::substr() to handle fixed-format and free-format NASTRAN. //- A std::string::substr() variant to handle fixed-format and
// Returns the substr to the next comma (if found) or the given length //- free-format NASTRAN.
// // Returns the substr until the next comma (if found)
// \param str The string to extract from // or the given fixed width
// \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.
static std::string nextNasField static std::string nextNasField
( (
//! The string to extract from
const std::string& str, const std::string& str,
//! [in,out] The parse position within \p str
std::string::size_type& pos, 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
); );

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2017 OpenCFD Ltd. Copyright (C) 2017-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -62,13 +62,17 @@ bool Foam::fileFormats::NASedgeFormat::read
while (is.good()) while (is.good())
{ {
string::size_type linei = 0; // parsing position within current line
string line; string line;
is.getLine(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 // 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) // First word (column 0-8)
const word cmd(word::validate(nextNasField(line, linei, 8))); const word cmd(word::validate(nextNasField(line, linei, 8)));
if (cmd == "CBEAM" || cmd == "CROD") if (cmd == "CBEAM" || cmd == "CROD")
{ {
// discard elementId (8-16) // Fixed format:
(void) nextNasField(line, linei, 8); // 8-16 // 8-16 : element id
// discard groupId (16-24) // 16-24 : group id
(void) nextNasField(line, linei, 8); // 16-24 // 24-32 : vertex
// 32-40 : vertex
label a = readLabel(nextNasField(line, linei, 8)); // 24-32 // discard elementId
label b = readLabel(nextNasField(line, linei, 8)); // 32-40 (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)); dynEdges.append(edge(a,b));
} }
else if (cmd == "PLOTEL") else if (cmd == "PLOTEL")
{ {
// discard elementId (8-16) // Fixed format:
(void) nextNasField(line, linei, 8); // 8-16 // 8-16 : element id
// 16-24 : vertex
// 24-32 : vertex
// 32-40 : vertex
label a = readLabel(nextNasField(line, linei, 8)); // 16-24 // discard elementId (8-16)
label b = readLabel(nextNasField(line, linei, 8)); // 24-32 (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)); dynEdges.append(edge(a,b));
} }
else if (cmd == "GRID") else if (cmd == "GRID")
{ {
label index = readLabel(nextNasField(line, linei, 8)); // 8-16 // Fixed (short) format:
(void) nextNasField(line, linei, 8); // 16-24 // 8-16 : point id
scalar x = readNasScalar(nextNasField(line, linei, 8)); // 24-32 // 16-24 : coordinate system (unsupported)
scalar y = readNasScalar(nextNasField(line, linei, 8)); // 32-40 // 24-32 : point x coordinate
scalar z = readNasScalar(nextNasField(line, linei, 8)); // 40-48 // 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); pointId.append(index);
dynPoints.append(point(x, y, z)); dynPoints.append(point(x, y, z));
@ -138,6 +170,8 @@ bool Foam::fileFormats::NASedgeFormat::read
// GRID* 126 0 -5.55999875E+02 -5.68730474E+02 // GRID* 126 0 -5.55999875E+02 -5.68730474E+02
// * 2.14897901E+02 // * 2.14897901E+02
// Cannot be long format and free format at the same time!
label index = readLabel(nextNasField(line, linei, 16)); // 8-24 label index = readLabel(nextNasField(line, linei, 16)); // 8-24
(void) nextNasField(line, linei, 16); // 24-40 (void) nextNasField(line, linei, 16); // 24-40
scalar x = readNasScalar(nextNasField(line, linei, 16)); // 40-56 scalar x = readNasScalar(nextNasField(line, linei, 16)); // 40-56

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2017-2022 OpenCFD Ltd. Copyright (C) 2017-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -149,7 +149,6 @@ bool Foam::fileFormats::NASsurfaceFormat<Face>::read
string line; string line;
while (is.good()) while (is.good())
{ {
string::size_type linei = 0; // Parsing position within current line
is.getLine(line); is.getLine(line);
// ANSA extension // ANSA extension
@ -223,16 +222,30 @@ bool Foam::fileFormats::NASsurfaceFormat<Face>::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) // First word (column 0-8)
const word cmd(word::validate(nextNasField(line, linei, 8))); const word cmd(word::validate(nextNasField(line, linei, 8)));
if (cmd == "CTRIA3") if (cmd == "CTRIA3")
{ {
label elemId = readLabel(nextNasField(line, linei, 8)); // 8-16 // Fixed format:
label groupId = readLabel(nextNasField(line, linei, 8)); // 16-24 // 8-16 : element id
const auto a = readLabel(nextNasField(line, linei, 8)); // 24-32 // 16-24 : group id
const auto b = readLabel(nextNasField(line, linei, 8)); // 32-40 // 24-32 : vertex
const auto c = readLabel(nextNasField(line, linei, 8)); // 40-48 // 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 // Convert groupId into zoneId
const auto iterZone = zoneLookup.cfind(groupId); const auto iterZone = zoneLookup.cfind(groupId);
@ -261,12 +274,20 @@ bool Foam::fileFormats::NASsurfaceFormat<Face>::read
} }
else if (cmd == "CQUAD4") else if (cmd == "CQUAD4")
{ {
label elemId = readLabel(nextNasField(line, linei, 8)); // 8-16 // Fixed format:
label groupId = readLabel(nextNasField(line, linei, 8)); // 16-24 // 8-16 : element id
const auto a = readLabel(nextNasField(line, linei, 8)); // 24-32 // 16-24 : group id
const auto b = readLabel(nextNasField(line, linei, 8)); // 32-40 // 24-32 : vertex
const auto c = readLabel(nextNasField(line, linei, 8)); // 40-48 // 32-40 : vertex
const auto d = readLabel(nextNasField(line, linei, 8)); // 48-56 // 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 // Convert groupId into zoneId
const auto iterZone = zoneLookup.cfind(groupId); const auto iterZone = zoneLookup.cfind(groupId);
@ -310,11 +331,21 @@ bool Foam::fileFormats::NASsurfaceFormat<Face>::read
} }
else if (cmd == "GRID") else if (cmd == "GRID")
{ {
label index = readLabel(nextNasField(line, linei, 8)); // 8-16 // Fixed (short) format:
(void) nextNasField(line, linei, 8); // 16-24 // 8-16 : point id
scalar x = readNasScalar(nextNasField(line, linei, 8)); // 24-32 // 16-24 : coordinate system (not supported)
scalar y = readNasScalar(nextNasField(line, linei, 8)); // 32-40 // 24-32 : point x coordinate
scalar z = readNasScalar(nextNasField(line, linei, 8)); // 40-48 // 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); pointId.append(index);
dynPoints.append(point(x, y, z)); dynPoints.append(point(x, y, z));
@ -327,6 +358,8 @@ bool Foam::fileFormats::NASsurfaceFormat<Face>::read
// GRID* 126 0 -5.55999875E+02 -5.68730474E+02 // GRID* 126 0 -5.55999875E+02 -5.68730474E+02
// * 2.14897901E+02 // * 2.14897901E+02
// Cannot be long format and free format at the same time!
label index = readLabel(nextNasField(line, linei, 16)); // 8-24 label index = readLabel(nextNasField(line, linei, 16)); // 8-24
(void) nextNasField(line, linei, 16); // 24-40 (void) nextNasField(line, linei, 16); // 24-40
scalar x = readNasScalar(nextNasField(line, linei, 16)); // 40-56 scalar x = readNasScalar(nextNasField(line, linei, 16)); // 40-56

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2012-2016 OpenFOAM Foundation Copyright (C) 2012-2016 OpenFOAM Foundation
Copyright (C) 2015-2022 OpenCFD Ltd. Copyright (C) 2015-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -307,16 +307,10 @@ void Foam::surfaceWriters::nastranWriter::writeGeometry
Foam::surfaceWriters::nastranWriter::nastranWriter() Foam::surfaceWriters::nastranWriter::nastranWriter()
: :
surfaceWriter(), surfaceWriter(),
writeFormat_(fieldFormat::SHORT), writeFormat_(fieldFormat::FREE),
fieldMap_(),
commonGeometry_(false), commonGeometry_(false),
separator_() separator_(",") // FREE format
{ {}
// if (writeFormat_ == fieldFormat::FREE)
// {
// separator_ = ",";
// }
}
Foam::surfaceWriters::nastranWriter::nastranWriter Foam::surfaceWriters::nastranWriter::nastranWriter
@ -331,12 +325,10 @@ Foam::surfaceWriters::nastranWriter::nastranWriter
( (
"format", "format",
options, options,
fieldFormat::LONG fieldFormat::FREE
) )
), ),
fieldMap_(), commonGeometry_(options.getOrDefault("commonGeometry", false))
commonGeometry_(options.getOrDefault("commonGeometry", false)),
separator_()
{ {
if (writeFormat_ == fieldFormat::FREE) if (writeFormat_ == fieldFormat::FREE)
{ {

View File

@ -6,7 +6,7 @@
\\/ M anipulation | \\/ M anipulation |
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Copyright (C) 2012-2016 OpenFOAM Foundation Copyright (C) 2012-2016 OpenFOAM Foundation
Copyright (C) 2015-2022 OpenCFD Ltd. Copyright (C) 2015-2024 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -33,13 +33,13 @@ Description
The formatOptions for nastran: The formatOptions for nastran:
\table \table
Property | Description | Reqd | Default Property | Description | Reqd | Default
fields | Field pairs for PLOAD2/PLOAD4 | yes | format | Nastran format (short/long/free) | no | free
format | Nastran format (short/long/free) | no | long
scale | Output geometry scaling | no | 1 scale | Output geometry scaling | no | 1
transform | Output coordinate transform | no | transform | Output coordinate transform | no |
fieldLevel | Subtract field level before scaling | no | empty dict fieldLevel | Subtract field level before scaling | no | empty dict
fieldScale | Output field scaling | no | empty dict fieldScale | Output field scaling | no | empty dict
commonGeometry | use separate geometry files | no | false commonGeometry | use separate geometry files | no | false
fields | Field pairs for PLOAD2/PLOAD4 | yes |
\endtable \endtable
For example, For example,
@ -48,13 +48,6 @@ Description
{ {
nastran nastran
{ {
// OpenFOAM field name to NASTRAN load types
fields
(
(pMean PLOAD2)
(p PLOAD4)
);
format free; // format type format free; // format type
scale 1000; // [m] -> [mm] scale 1000; // [m] -> [mm]
@ -62,6 +55,13 @@ Description
{ {
"p.*" 0.01; // [Pa] -> [mbar] "p.*" 0.01; // [Pa] -> [mbar]
} }
// OpenFOAM field name to NASTRAN load types
fields
(
(pMean PLOAD2)
(p PLOAD4)
);
} }
} }
\endverbatim \endverbatim
@ -93,7 +93,6 @@ Description
Note Note
Output variable scaling does not apply to integer types such as Ids. 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 SourceFiles
nastranSurfaceWriter.C nastranSurfaceWriter.C
@ -221,10 +220,10 @@ public:
// Constructors // Constructors
//- Default construct. Default SHORT format //- Default construct. Default FREE format
nastranWriter(); nastranWriter();
//- Construct with some output options. Default LONG format //- Construct with some output options. Default FREE format
explicit nastranWriter(const dictionary& options); explicit nastranWriter(const dictionary& options);
//- Construct from components //- Construct from components