Skip to content

Add support for PBEAM, PBEAML and CBEAM Nastran cards#390

Draft
A-CGray wants to merge 65 commits into
masterfrom
NastranCompat
Draft

Add support for PBEAM, PBEAML and CBEAM Nastran cards#390
A-CGray wants to merge 65 commits into
masterfrom
NastranCompat

Conversation

@A-CGray

@A-CGray A-CGray commented Sep 30, 2025

Copy link
Copy Markdown
Contributor

As per title.

PBEAM and PBEAML cards can be used to define beam elements whose cross section varies over their length. Since TACS doesn't support this I simply use average properties.

I have added an example modal analysis of a tapered beam defined using either PBEAM or PBEAML cards, the TACS mode frequencies match NASTRAN pretty closely:

image image PBEAML-ModeComparison_000002 PBEAML-ModeComparison_000003 PBEAML-ModeComparison_000004

ToDo:

  • Support extraction of non-structural mass, offsets for centre of mass, centroid, and center of mass
    • Implement extraction from PBEAM cards
    • Implement extraction from PBEAML cards
    • Expand cython interfaces of beam constitutive classes to support these arguments
  • Add tests:
    • PBEAM Cards
    • PBEAML with "BAR" section
    • PBEAML with "TUBE" section
    • PBEAML with other section

Comment thread tacs/pytacs.py Outdated
Comment thread tacs/pytacs.py Outdated
A-CGray and others added 30 commits June 2, 2026 15:51
The tube cross-section is parametrised by the inner diameter and wall
thickness. Seven routines computed the outer diameter as
d0 = inner + wall, which is only correct for radii. The correct formula
is d0 = inner + 2*wall.

This corrupted the cross-sectional area A = pi/4*(d0^2 - d1^2) and
bending inertia Ia = pi/64*(d0^4 - d1^4) used for mass, stiffness, and
stress, and also produced inconsistent geometry relative to the failure
routines which already used the correct outer radius
r0 = 0.5*inner + wall.

Fix all seven d0 sites and correct the dependent DV sensitivity
coefficients:
  dA/d(inner): pi*wall/2 -> pi*wall
  dA/d(wall):  pi*d0/2   -> pi*d0
  dIa/d(wall): pi*d0^3/16 -> pi*d0^3/8

The failure routines (evalFailure, evalFailureStrainSens,
addFailureDVSens) use r0 = 0.5*inner + wall throughout and are unchanged.

Update integration test golden values in test_tube_beam.py and
test_tube_beam_pbarl.py to reflect the corrected cross-section geometry
(outer diameter 0.11 m -> 0.12 m for the d=0.1, t=0.01 test case).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mark every local variable that is initialised once and never mutated as
TacsScalar const (right-const per project style). Applies to rho, d0,
d1, A, Ia, G, kcorr, r0, fail, dA, dIa, and dr0 across all methods.
Variables that cannot be const are index (incremented), E/nu (written
by getIsotropicProperties out-param), and the stack arrays e0/s0/s0d/
e0d (elements assigned after declaration).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tiffness

The TACS beam element computes the e[2] bending strain as -kappa_z (the axial
derivative of director d1), not the textbook +kappa_z. The stiffness
derivation used the textbook convention, which flipped the sign of the
centroid-offset axial coupling (EA*xc2) and the EI23 off-diagonal in
populateMats.

Rewrite BeamSectionEqns.py in the element convention so the generated C matrix
matches the element, and update populateMats accordingly: EI23 is now positive
and the ad-hoc xc2 negation is removed. Symmetric, unoffset sections (I23=0)
are numerically unchanged; the fix matters for offset and asymmetric sections.
Add the sympy generator BeamSectionEqns.py to the repo for reproducibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PBARL ROD (a solid circle) was translated to IsoTubeBeamConstitutive, whose
transverse-shear correction factor is the thin-walled-tube limit
2(1+nu)/(4+3nu) ~ 0.53. For a solid circle the correct (Cowper 1966) value is
6(1+nu)/(7+6nu) ~ 0.89, so the beam was ~1.6x too soft in shear and
transverse-load tip deflections were ~6% high vs Nastran.

Route circular sections through BasicBeamConstitutive with kcorr computed from
Cowper's hollow-circular formula (m = inner/outer diameter): it gives the solid
value as m->0 and the thin-wall value as m->1. pyNastran does not expose a
shear factor for PBARL, so it is computed here.

Also fix a latent bug in the circular-offset branch: BasicBeamConstitutive
takes I33/I22/I23, not Iy/Iz/Iyz (the old kwargs would have raised on any
offset ROD/TUBE).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rsolve

For symmetric circular sections the bending modes are degenerate pairs. Asked
for exactly the reference mode count, the eigensolver could drop a twin and
return a non-contiguous set, misaligning the index-by-index frequency
comparison. Solve for MODAL_NUM_EIGS (20) and compare only the reference modes.

The eigenvector orientation within a degenerate pair is arbitrary, so TACS and
Nastran can pick different in-plane bending axes and the individual cross-MACs
split even though the shapes are identical. Group reference modes by frequency
and require, per TACS mode, that its best match lies within its group and that
the MAC summed over the group clears the threshold. For non-degenerate modes
this reduces to the previous argmax==i / mac[i,i] check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nces

Uncomment the PBARL/PBEAM/PBEAML rows in the case matrix and add the generated
Nastran input decks and extracted reference results (bar and rod families).
Tube/tube2 and CBEAM cases are enabled but remain skipped until their
references are generated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… MAC

The degeneracy-aware modal check summed the individual MAC values of a TACS
mode against its degenerate reference twins. That sum equals the true
projection of the shape onto the twins' subspace only when the twins are
orthogonal in the plain inner product. A CBAR wa/wb offset makes the mass
matrix non-diagonal, so the (still mass-orthonormal) twins acquire a small
mutual dot product and the group sum splits above/below 1 (e.g. 1.06 / 0.94),
tripping the threshold on the lower twin even though the subspaces coincide
(confirmed: canonical correlations = 1.0 between TACS and Nastran bending
subspaces, and the eigenfrequencies are offset-invariant to ~1e-9).

Replace the group sum with _subspace_projection_mac = ||Q Qᵀa||²/||a||², the
projection onto an orthonormalised basis of the reference group. It is
rotation-invariant within the group, assumes nothing about twin orthogonality,
and reduces exactly to mac[ii, ii] for a non-degenerate (singleton) group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Frequencies and mode shapes are independent comparisons, so split test_modal
into test_modal_frequencies and test_modal_shapes. A failure now identifies
which of the two broke without reading the assertion. A shared _solveModal
helper does the eigensolve, loads the references, and returns aligned arrays;
the solve is memoised per class so the two tests do not solve twice when they
share a process.

Also simplify the single modal problem from a one-entry dict (modal_problems)
to a plain attribute (modal_problem), dropping the next(iter(...)) accessors,
and bump the docstring count to 8 analyses per configuration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Circular TUBE/TUBE2 sections fail static_fy/static_fz against Nastran by up
to ~1.5% near the beam root. Root cause is a transverse-shear-correction-factor
convention difference, not a TACS error: TACS's IsoTubeBeamConstitutive uses the
Cowper (FSDT) factor k = 2(1+nu)/(4+3nu) ~ 0.53 (its effective k matches Cowper
to 4 significant figures), while Nastran's auto-computed PBARL/PBEAML TUBE factor
is the elementary thin-walled-tube value k ~ 0.50. Both are standard textbook
coefficients. The effect is confined to transverse tip-force cases (where shear
flexibility contributes) and is largest near the root where shear dominates
bending; bending-moment, torsion, and axial cases are unaffected.

Relax the static tolerance to 2e-2 only for TUBE/TUBE2 transverse-force cases,
gated on a new SECTION class attribute, with the rationale documented inline.
All other cases keep the tight 1e-3 default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e MAC

The modal mode-shape check compared each TACS mode against the span of its
reference degenerate group. When a degenerate bending pair straddles the
10-mode Nastran reference cutoff its upper twin is absent from the references,
so the surviving boundary mode's reference span is incomplete and the (correct)
TACS shape cannot project onto it — a guaranteed false failure, e.g.
cbar_pbarl_tube2_baseline mode 10.

Build the degenerate groups from the oversolved TACS spectrum rather than the
references. For a complete group, project the TACS shape onto the reference
span as before. For a truncated-twin group, reverse the projection: project the
orphaned reference shape onto the complete oversolved TACS span (which holds the
whole pair), with a mirror-image swap guard. _solveModal now returns the full
oversolved TACS spectrum and shapes.

Lower MAC_THRESHOLD 0.99 -> 0.98: the projection MAC degrades monotonically with
bending-mode order (1.0000/0.9992/0.9966 for the 1st-3rd pairs, 0.9861 for the
truncated 4th), mirroring the high-mode frequency divergence the 5% frequency
tolerance already allows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

2 participants