Skip to content

Fix parsing of Nastran small field reals in the modal structural solver - #2859

Open
gaoflow wants to merge 1 commit into
su2code:developfrom
gaoflow:fix-nastran-small-field-real-parsing
Open

Fix parsing of Nastran small field reals in the modal structural solver#2859
gaoflow wants to merge 1 commit into
su2code:developfrom
gaoflow:fix-nastran-small-field-real-parsing

Conversation

@gaoflow

@gaoflow gaoflow commented Aug 4, 2026

Copy link
Copy Markdown

Proposed Changes

SU2_PY/SU2_Nastran/pysu2_nastran.py reads the GRID and CORD2R entries of a Nastran sorted bulk data echo by slicing eight character fields and then interpreting each slice positionally. Two helpers do that interpretation, and both are only correct when the value happens to be left flush and to fill all eight columns:

def nastran_float(s):
    if s.find("E") == -1:
        s = s.replace("-", "e-")
        s = s.replace("+", "e+")
        if s[0] == "e":        # only removes the spurious "e" if it lands on index 0
            s = s[1:]
    return float(s)

def __checkBlankField(self, string):
    if string == " " * 8:      # only recognises a blank field exactly eight characters wide
        return int(0)
    return int(string)

Neither assumption is guaranteed. The Quick Reference Guide, Format of Bulk Data Entries -> Small Field Format, says:

Fields 1 and 10 must be left justified.
Fields 2 through 9 do not need to be either right or left justified, although aligning the data fields is good practice.

and on the same page:

Real numbers may be entered in a variety of ways. For example, the following are all acceptable versions of the real number seven:
7.0 .7E1 0.7+1 .70+1 7.E+0 70.-1

So three families of legal input abort the FSI run before it starts:

  1. Any negative real that is not left flush. nastran_float(" -5.2") builds " e-5.2" and raises ValueError. This does not need the E-less exponent form at all; a plain right justified negative coordinate is enough. pyNastran, for one, writes every small field with '%8s'/'%8d'/'%8.Nf', i.e. right justified.
  2. Lower case e and D exponents. "700.e-2" becomes "700.ee-2" and ".7D1" is never converted, in any justification. Both are accepted real formats (.1d-5 is in the OptiStruct bulk data guidelines, and the QRG notes that "a double precision specification requires a 'D' type exponent").
  3. A GRID entry without the optional CD field. Nastran trims the trailing blanks of each echo line, so the entry ends after X3 and line[48:56] is '' -> int('') raises. In the tutorial mesh (Tutorials/multiphysics/unsteady_fsi_python/Ma01/modal.f06) CD is written explicitly, which is why this has not shown up; one column shorter and it is an empty slice.

The three symptoms are one root cause, so the fix is at the two helpers rather than at the twelve GRID/CORD2R call sites, which are unchanged. nastran_float normalises the field first and then inserts the omitted exponent letter at the first sign after the mantissa sign, which is what the established readers do; __checkBlankField treats any whitespace-only or absent field as blank. nastran_float moved to module level so it can be tested — it was a closure inside __readNastranMesh.

Verification

I enumerated the QRG spellings of a real crossed with sign and with left/right/centre justification in an eight column field: 108 fields, 49 of them rejected or miscomputed before, 0 after. The failures group as:

family left right centre
QRG spellings of seven 0 8 8
lower case e / D exponent 9 13 11

SU2_PY/SU2_Nastran/test_pysu2_nastran.py is new: it drives that table through nastran_float, and then builds the same little model (a CORD2R plus three GRIDs with negative coordinates, an E-less exponent, a blank CP and an omitted CD) in each justification and reads it through __readNastranMesh, asserting the parsed geometry does not depend on how the fields were written. 10 tests, all 10 fail on develop and pass here.

There is no Python test harness in the repository at the moment, so this is plain unittest with no new dependency, run with:

python -m unittest discover -s SU2_PY/SU2_Nastran

Happy to drop the file if you would rather not start a Python test directory here.

As a no-regression check I read the real tutorial modal.f06 (124 grid points, 41 negative coordinate values) with develop and with this branch: the parsed coordinates, IDs, CP, CD and marker sets are bit-for-bit identical. pre-commit run is clean (black 22.6.0). I did not build the C++ side; nothing outside this Python module is touched.

Related Work

No related PR that I can find. #2313 is a different problem in the same file (page headers interrupting a SET1 continuation in the echo) and is not addressed here.

PR Checklist

  • I am submitting my contribution to the develop branch.
  • My contribution generates no new compiler warnings (try with --warnlevel=3 when using meson). Nothing compiled changes.
  • My contribution is commented and consistent with SU2 style (https://su2code.github.io/docs_v7/Style-Guide/).
  • I used the pre-commit hook to prevent dirty commits and used pre-commit run --all to format old commits.
  • I have added a test case that demonstrates my contribution, if necessary.
  • I have updated appropriate documentation (Tutorials, Docs Page, config_template.cpp), if necessary.

The mesh reader decodes the eight character fields of the GRID and CORD2R
entries positionally: nastran_float() rewrote every "-" into "e-" and then
dropped a leading "e" by looking at index 0, and __checkBlankField() only
recognised a blank field when it was exactly eight spaces long.

Both assumptions only hold when a field is left justified and fills all
eight columns. The Quick Reference Guide states that fields 2 through 9 do
not need to be either right or left justified, and Nastran trims trailing
blanks from the lines of the sorted bulk data echo, so:

  - any negative coordinate that is not left flush raised ValueError, e.g.
    "    -5.2" became "    e-5.2";
  - a lower case "e" or a "D" exponent was never recognised, so "700.e-2"
    became "700.ee-2" in any justification;
  - a GRID entry without the optional CD field ends the echo line after X3,
    leaving an empty slice that raised ValueError in int().

nastran_float() now normalises the field before interpreting it and inserts
the omitted exponent letter at the first sign after the mantissa sign, which
is what the reference readers do. It is defined at module level so it can be
tested; all twelve call sites use it unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant