Skip to content

Commit f666ad3

Browse files
committed
some nastran fixes for continuation lines
1 parent 6fbc398 commit f666ad3

File tree

2 files changed

+68
-24
lines changed

2 files changed

+68
-24
lines changed

src/meshio/_helpers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ def write(filename, mesh: Mesh, file_format: str | None = None, **kwargs):
176176
if key in num_nodes_per_cell:
177177
if value.shape[1] != num_nodes_per_cell[key]:
178178
raise WriteError(
179-
f"Unexpected cells array shape {value.shape} for {key} cells."
179+
f"Unexpected cells array shape {value.shape} for {key} cells. "
180+
+ f"Expected shape [:, {num_nodes_per_cell[key]}]."
180181
)
181182
else:
182183
# we allow custom keys <https://github.com/nschloe/meshio/issues/501> and

src/meshio/nastran/_nastran.py

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
"""
22
I/O for Nastran bulk data.
33
"""
4+
from __future__ import annotations
5+
46
import numpy as np
7+
from rich import print
58

69
from ..__about__ import __version__
710
from .._common import num_nodes_per_cell, warn
@@ -63,13 +66,12 @@ def read_buffer(f):
6366
cells = []
6467
cells_id = []
6568
cell = None
66-
cell_type = None
6769
point_refs = []
6870
cell_refs = []
6971
cell_ref = None
7072

71-
def add_cell(nastran_type, cell, cell_type, cell_ref):
72-
cell_type = nastran_to_meshio_type[keyword]
73+
def add_cell(nastran_type, cell, cell_ref):
74+
cell_type = nastran_to_meshio_type[nastran_type]
7375
cell = list(map(int, cell))
7476

7577
# Treat 2nd order CTETRA, CPYRA, CPENTA, CHEXA elements
@@ -90,6 +92,7 @@ def add_cell(nastran_type, cell, cell_type, cell_ref):
9092

9193
cell = _convert_to_vtk_ordering(cell, nastran_type)
9294

95+
# decide if we should append cell or start a new cell block
9396
if len(cells) > 0 and cells[-1][0] == cell_type:
9497
cells[-1][1].append(cell)
9598
cells_id[-1].append(cell_id)
@@ -114,24 +117,51 @@ def add_cell(nastran_type, cell, cell_type, cell_ref):
114117
if next_line.startswith("ENDDATA"):
115118
break
116119

117-
# read line and merge with all continuation lines (starting with `+`)
118-
chunks = _chunk_line(next_line)
120+
# read line and merge with all continuation lines (starting with `+` or
121+
# `*` or automatic continuation lines in fixed format)
122+
chunks = []
123+
c, _ = _chunk_line(next_line)
124+
chunks.append(c)
119125
while True:
120126
next_line = f.readline()
121127
if not next_line:
122128
raise ReadError("Premature EOF")
123-
next_line = next_line.rstrip()
124129
# Blank lines or comments
125130
if len(next_line) < 4 or next_line.startswith(("$", "//", "#")):
126131
continue
127-
elif next_line[0] == "+":
128-
# skip the continuation chunk
129-
chunks += _chunk_line(next_line)[1:]
132+
elif next_line[0] in ["+", "*"]:
133+
# From
134+
# <https://docs.plm.automation.siemens.com/data_services/resources/nxnastran/10/help/en_US/tdocExt/pdf/User.pdf>:
135+
# You can manually specify a continuation by using a
136+
# continuation identifier. A continuation identifier is a
137+
# special character (+ or *) that indicates that the data
138+
# continues on another line.
139+
c, _ = _chunk_line(next_line)
140+
chunks.append(c[1:])
141+
elif len(chunks[-1]) == 10 and chunks[-1][9] == " ":
142+
# automatic continuation: last chunk of previous line and first
143+
# chunk of current line are spaces
144+
c, _ = _chunk_line(next_line)
145+
if c[0] == " ":
146+
chunks[-1] = chunks[-1][:9]
147+
chunks.append(c[1:])
148+
else:
149+
# not a continuation
150+
pass
130151
else:
131152
break
132153

154+
# flatten
155+
chunks = [item for sublist in chunks for item in sublist]
156+
157+
# strip chunks
133158
chunks = [chunk.strip() for chunk in chunks]
134159

160+
# remove empty chunks
161+
chunks = [c for c in chunks if c != ""]
162+
163+
print("c", chunks)
164+
135165
keyword = chunks[0]
136166

137167
# Points
@@ -148,7 +178,7 @@ def add_cell(nastran_type, cell, cell_type, cell_ref):
148178
if len(pref) > 0:
149179
point_refs.append(int(pref))
150180
points_id.append(point_id)
151-
chunks2 = _chunk_line(next_line)
181+
chunks2, _ = _chunk_line(next_line)
152182
next_line = f.readline()
153183
points.append(
154184
[
@@ -174,13 +204,16 @@ def add_cell(nastran_type, cell, cell_type, cell_ref):
174204
# This information is removed.
175205
cell = chunks[3:5]
176206
else:
207+
print(keyword)
208+
print(chunks)
209+
exit(1)
177210
cell = chunks[3:]
178211

179212
# remove empty chunks
180213
cell = [item for item in cell if item != ""]
181214

182215
if cell is not None:
183-
add_cell(keyword, cell, cell_type, cell_ref)
216+
add_cell(keyword, cell, cell_ref)
184217

185218
# Convert to numpy arrays
186219
points = np.array(points)
@@ -209,10 +242,20 @@ def add_cell(nastran_type, cell, cell_type, cell_ref):
209242

210243
# There are two basic categories of input data formats in NX Nastran:
211244
#
212-
# "Free" format data, in which the data fields are simply separated by commas. This type of data is known as free field data.
213-
# "Fixed" format data, in which your data must be aligned in columns of specific width. There are two subcategories of fixed format data that differ based on the size of the fixed column width:
214-
# Small field format, in which a single line of data is divided into 10 fields that can contain eight characters each.
215-
# Large field format, in which a single line of input is expanded into two lines The first and last fields on each line are eight columns wide, while the intermediate fields are sixteen columns wide. The large field format is useful when you need greater numerical accuracy.
245+
# - "Free" format data, in which the data fields are simply separated by
246+
# commas. This type of data is known as free field data.
247+
#
248+
# - "Fixed" format data, in which your data must be aligned in columns of
249+
# specific width. There are two subcategories of fixed format data that differ
250+
# based on the size of the fixed column width:
251+
#
252+
# - Small field format, in which a single line of data is divided into 10
253+
# fields that can contain eight characters each.
254+
#
255+
# - Large field format, in which a single line of input is expanded into
256+
# two lines The first and last fields on each line are eight columns wide,
257+
# while the intermediate fields are sixteen columns wide. The large field
258+
# format is useful when you need greater numerical accuracy.
216259
#
217260
# See: https://docs.plm.automation.siemens.com/data_services/resources/nxnastran/10/help/en_US/tdocExt/pdf/User.pdf
218261

@@ -356,14 +399,14 @@ def _nastran_string_to_float(string):
356399
return float(string[0] + string[1:].replace("+", "e+").replace("-", "e-"))
357400

358401

359-
def _chunk_line(line):
360-
if "," in line: # free format
361-
chunks = line.split(",")
362-
else: # fixed format
363-
CHUNK_SIZE = 8
364-
chunks = [line[i : CHUNK_SIZE + i] for i in range(0, 72, CHUNK_SIZE)]
365-
# everything after the 9th chunk is ignored
366-
return chunks[:9]
402+
def _chunk_line(line: str) -> tuple[list[str], bool]:
403+
if "," in line:
404+
# free format
405+
return line.split(","), True
406+
# fixed format
407+
CHUNK_SIZE = 8
408+
chunks = [line[i : CHUNK_SIZE + i] for i in range(0, 80, CHUNK_SIZE)]
409+
return chunks, False
367410

368411

369412
def _convert_to_vtk_ordering(cell, nastran_type):

0 commit comments

Comments
 (0)