3. Generate a Section Shell Card from Thickness and Integration Rules

Why *SECTION_SHELL formatting is unforgiving

LS-DYNA reads *SECTION_SHELL in fixed-field mode by default. Each of the eight data fields on the card occupies exactly 10 characters, and the parser does not care about whitespace alignment — it slices the line at column boundaries. A value that drifts one character to the left silently shifts every subsequent field, so a thickness of 2.0 can end up interpreted as an integration point count. The generator therefore has two jobs: compute the correct values, and place them at the correct columns.

The card layout for the standard (non-composite) shell section is:

FieldNameMeaningTypical value
1SECIDSection ID referenced by *PARTinteger, unique
2ELFORMShell element formulation2 (Belytschko-Tsay), 16 (fully integrated)
3SHRFShear correction factor0.8333 (5/6) default
4NIPThrough-thickness integration points2–5 for elastic, 3–5 for plasticity
5QRQuadrature rule0 (default), 1 (Gauss), 2 (Lobatto)
6ICOMPComposite flag0 for homogeneous shell
7SETYPShell type1 (default), 2, 3
8T1Thickness at node 1e.g. 2.0

Fields 9–12 (T2, T3, T4) continue on the same line for variable-thickness shells; for a constant-thickness part you set T1 and leave the rest blank or repeat T1. The generator below handles the constant-thickness case and optionally emits the four nodal thicknesses.

Building the generator

The core function takes a parameter dictionary and returns a single formatted line. The critical helper is a fixed-width formatter that right-justifies each value into a 10-character slot, matching LS-DYNA's column convention.

def _f10(value):
    """Format a value into a 10-character LS-DYNA field."""
    if value is None or value == "":
        return " " * 10
    if isinstance(value, float):
        # LS-DYNA accepts E-notation; keep 6 significant digits
        text = f"{value:.6g}"
    else:
        text = str(value)
    if len(text) > 10:
        raise ValueError(f"value {text!r} exceeds 10-character field")
    return text.rjust(10)

def section_shell_card(params):
    """Emit a *SECTION_SHELL keyword block from a parameter dict.

    Required keys: secid, elform, nip, t1
    Optional keys: shrf (0.8333), qr (0.0), icomp (0), setyp (1),
                   t2, t3, t4 (default to t1)
    """
    secid  = params["secid"]
    elform = params["elform"]
    nip    = params["nip"]
    t1     = params["t1"]
    shrf   = params.get("shrf", 0.8333)
    qr     = params.get("qr", 0.0)
    icomp  = params.get("icomp", 0)
    setyp  = params.get("setyp", 1)
    t2     = params.get("t2", t1)
    t3     = params.get("t3", t1)
    t4     = params.get("t4", t1)

    line1 = "".join(_f10(v) for v in
                    (secid, elform, shrf, nip, qr, icomp, setyp, t1))
    line2 = "".join(_f10(v) for v in (t2, t3, t4))

    return (
        "*SECTION_SHELL\n"
        f"{line1}\n"
        f"{line2}\n"
    )

Two details matter here. First, _f10 rejects any value longer than 10 characters rather than truncating — silent truncation is exactly the failure mode that produces a corrupt deck. Second, floats are rendered with :.6g, which keeps 0.8333 as 0.8333 and 2.0 as 2, both of which LS-DYNA parses correctly while staying inside the field width.

Integration-point and formulation rules

The generator should not blindly accept any NIP/ELFORM combination. Two rules are worth encoding as validation before emission:

  • NIP vs. ELFORM. For ELFORM 2 (Belytschko-Tsay) and ELFORM 16 (fully integrated), NIP controls through-thickness integration only. NIP=1 is legal but gives no bending response; NIP≥2 is required for any part that carries moment. For elastic-only parts NIP=2 is sufficient; for plasticity NIP=3 or 5 is the usual choice.
  • QR interaction. QR=0 lets LS-DYNA pick the default rule for the given NIP. Setting QR=1 (Gauss) or QR=2 (Lobatto) explicitly is only meaningful when NIP matches the rule's supported point count; Lobatto with NIP=2 places points at the surfaces, which is useful for contact-heavy shells but changes the stress output locations.
Keep SHRF at 5/6 (0.8333) unless you have a specific reason to change it. It is the shear correction factor for a rectangular cross-section and is the LS-DYNA default; altering it without justification is a common source of unexpected stiffness in benchmark comparisons.

Worked example

Suppose a 2.0 mm steel panel uses Belytschko-Tsay with 5 through-thickness points and default shear correction. The parameter dictionary and resulting card are:

params = {
    "secid": 101,
    "elform": 2,
    "nip": 5,
    "t1": 2.0,
    "shrf": 0.8333,
    "qr": 0.0,
    "icomp": 0,
    "setyp": 1,
}
print(section_shell_card(params))

Expected output (each field right-justified in 10 columns):

*SECTION_SHELL
       101         2    0.8333         5         0         0         1         2
         2         2         2

Reading the first data line column by column: 101 is SECID, 2 is ELFORM, 0.8333 is SHRF, 5 is NIP, then three zeros for QR/ICOMP/SETYP, and 2 as T1. The second line carries T2, T3, T4 — all equal to T1 because the part is constant-thickness.

*SECTION_SHELL fixed-field column map col 1 11 21 31 41 51 61 SECID 101 ELFORM 2 SHRF 0.8333 NIP 5 QR 0 ICOMP 0 SETYP 1 Data line 2 T1 2 T2 2 T3 2 T4 2 Misalignment failure mode 101,2,0.8333,5,0,0,1,2 → comma-separated: parser reads 8 fields, OK 101 2 0.8333 5 0 0 1 2 → space-separated, ragged: field 3 shifts, NIP misread

Verification before submission

After generating the card, run three checks that catch the most common defects:

  1. Field count. Split each data line into 10-character slices and confirm the number of non-blank slices matches the expected count (8 on line 1, up to 4 on line 2). A slice that is entirely blank in the middle of a line means a value was omitted and everything after it shifted.
  2. Round-trip parse. Re-read the emitted line with the same fixed-width slicer and confirm each recovered value equals the input. This catches float formatting that changed the value (e.g. 0.833333 truncated to 0.8333 is acceptable; 1e-05 rendered as 1e-05 is fine, but 1234567.0 would overflow the field and must raise).
  3. Cross-reference. Confirm the SECID does not collide with an existing section in the include file, and that every *PART referencing this SECID uses a compatible ELFORM for its element type.
Do not rely on visual inspection of the card in a text editor. Proportional fonts and tab expansion hide column drift. Always validate with a fixed-width slicer or by importing the deck into LS-PrePost and checking the section summary, where a misread NIP or ELFORM shows up immediately as an unexpected integration count or formulation.

The generator above is deliberately small so it can be embedded in a larger automation pipeline: feed it a parameter dictionary from a spreadsheet or YAML file, emit one card per part, and concatenate the results into an include file. The fixed-width formatter and the field-count check are the two pieces that make the output safe to hand to LS-DYNA without manual cleanup.