Repository navigation
Conversation
… read as missing data
A channel exported to Abaqus went out roughly five times too stiff about its
major axis, and the adapy model was left corrupted for every export after it.
`eval_general_properties` fills in section properties a model does not carry, and
it decided "missing" by testing `<= 0.0`. But every field of GeneralProperties
defaults to None, so None is the only marker for missing -- and zero is a
computed answer. Iyz is exactly 0 for every section symmetric about an axis: box,
tubular, I-profile, circular, flatbar, channel. Only calc_angular returns a
non-zero one. So the function treated the correct answer as absent for nearly
every profile that reaches it.
The substituted value made it worse. (Iy + Iz) / 2 is the *largest* a product of
inertia may legally be, so the fabricated Iyz then failed the positive-definiteness
test immediately below, and that branch inflated Iy as well. Measured:
UNP200x10 Iy 1.927e-05 -> 9.212e-05 (x 4.78) Iyz 0 -> 1.049e-05
UNP300x15 Iy 8.063e-05 -> 4.515e-04 (x 5.60) Iyz 0 -> 4.314e-05
Both numbers reached the *Beam General Section* data line behind a log line whose
own text admits "this is not a validated method of solving this issue". A section
five times too stiff in the wrong plane is not something a reader of the deck
would notice.
Section.properties caches into _genprops, and the function bound that cached
object and assigned to its fields. So writing an Abaqus deck permanently changed
the model: a Sesam or IFC export later in the same process inherited the inflated
Iy. Cross-format contamination with nothing to see.
Three changes:
missing means None. A computed zero is kept. Where Iyz genuinely is unknown the
substitute is 0.0 -- symmetric -- rather than the extreme of its legal range.
Ix, Iy and Iz keep the `or <= 0.0` test, because those cannot legitimately be
zero for a real cross-section; that asymmetry is the point rather than an
oversight, and a test pins it.
a copy is returned. dataclasses.replace, so the model is never written to.
impossible properties raise. Adjusting Iy by 10% until the inequality holds is
how a section becomes quietly stiffer; with a real Iyz the inequality cannot
fail for a physically possible section, so a failure means the input is
inconsistent and the error says which numbers.
11 tests. Restoring the original behaviour in full fails 7 of them, including an
end-to-end assertion on the number that reaches the deck. Each half of the defect
was also mutated separately and is caught separately -- either one alone is a
no-op for a channel, which is why the pair had to be tested together.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nej3bWxJevs9GkH6tg1WKv
PR Review👋 I checked your PR and found no issues. Thanks!
|
🚀 Profiling Results (Top 20 most expensive calls)
|
|
Correction to the test evidence in the description above. It says the suite shows "only the pre-existing environmental failures". That was wrong, and the error was mine: I ran the test suite with Run correctly (the environment's
So the baseline is zero failures, not eight or twelve, and this branch adds none. The comparisons in the description remain valid — clean 🤖 Generated with Claude Code |
…still reaches GENERAL The "Update branch" merge brought in Krande#386, whose Abaqus writer traces a channel as `section=ARBITRARY` -- three walls by centreline, exact -- instead of sending it down the GENERAL path. So the one end-to-end test here, which used a channel *because* it was the profile that reached `*Beam General Section`, now fails on its own precondition, on all three CI platforms and in the conda build. The defect this PR fixes is untouched by that: it lives in `eval_general_properties`, which every `*Beam General Section` still goes through. The test moves to the case that reaches it today, a section declared GENERAL carrying a real channel's properties with its computed zero `Iyz`, and additionally asserts that the zero survives to the data line. Same numbers the measurement was made on; the same file as on Krande#398, where it has been green. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nej3bWxJevs9GkH6tg1WKv
|
Folded into #407. This branch was merged in as-is, with its own merge commit, so every commit and its authorship #407 collects the six format-level FEA PRs (#394, #395, #396, #399, #400, #404) into one, since |
…nd verify plates through decks (#407) Folds the six format-level FEA PRs into one, drops the Abaqus/CAE stack, and gives the verification report a plate case built through the deck writers. Folded: #394 (*Tie -> BLDEP facet interpolation), #395 (Sesam super element number), #396 (a symmetric section's zero product of inertia), #399 (.gnx as a first-class format), #400 (Sesam settlement, one BLDEP term per dof pair, instance-qualified *MPC), #404 (surface pressure as BEUSLO). Dropped: #398, #401, #402, #403, #405 -- src/ada/cadit/cae, Part.to_abaqus_cae_script, its tests and verification/genie_vs_abaqus, which drives `abaqus cae noGUI=`. adapy supports formats, not APIs into proprietary software, and a verification entry only a CAE licence can reproduce verifies nothing anyone can check. Salvaged from it, because it is format-level: a T-profile now reaches an Abaqus *Beam Section as section=I with the bottom flange zeroed (it had no mapping at all), and Calculix converts a T to a general section as it already did an I. New: the plate strip case -- an ada.Plate meshed by the meshing module (gmsh), written as a deck per solver, checked against 5 q L^4 / (384 D) and f_n = n^2 pi / (2 L^2) sqrt(D / (rho t)). Code_Aster lands on the eigen closed form to six digits. Writer gaps it exposed, each found by running it rather than reading: * Calculix had no pressure load at all; it now writes *DLOAD ... P. Measured on ccx 2.23 that P, P1 and P2 are bit-identical on a shell, so the label carries no face and only the sign can. * Code_Aster's pressure had never run: FORCE_FACE with FY is a traction along global Y, not a normal pressure, and GROUP_MA named the surface, which has no MED counterpart -- every attempt stopped at <EXCEPTION> <MODELISA7_77>. Now FORCE_COQUE=_F(PRES=...) over the element sets, via the new shared ada.fem.surfaces.pressure_elsets. * Code_Aster refused any eigenvalue analysis with more than one Bc; ASSEMBLAGE's CHARGE takes a tuple. * The Sesam BEUSLO pressure sign was inverted relative to Abaqus, Calculix and Code_Aster. The reference it was tuned against applied its load along global -z on the grounds that -z was "against the plate's +z normal"; the strip's element normals are all -z (measured, all 128), so the reference ran along the normal and the test that pinned it never checked which face was named. Code_Aster reproduces the Sestra magnitude to eight digits with the opposite sign. The measured Sestra numbers are kept exactly as measured; only which face they belong to changed. The Sestra leg runs only where Sestra is installed, so a licensed re-run remains the confirmation worth having. Co-Authored-By: oleandor <oleandor@gmail.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KXGBkUbGK6oYWxm5R2WYVB
The bug
A channel exported to Abaqus goes out roughly five times too stiff about its major axis, and the
adapy model is left corrupted for every export after it.
eval_general_propertiesfills in section properties a model does not carry, and decides "missing"by testing
<= 0.0. But every field ofGeneralPropertiesdefaults toNone, soNoneis the onlymarker for missing — and zero is a computed answer.
Iyzis exactly0for every sectionsymmetric about an axis: box, tubular, I-profile, circular, flatbar, channel. Only
calc_angularreturns a non-zero one. So the correct answer was treated as absent for nearly every profile that
reaches this function.
The substituted value made it worse.
(Iy + Iz) / 2is the largest a product of inertia maylegally be, so the fabricated
Iyzimmediately failed the positive-definiteness test just below, andthat branch inflated
Iyas well. Measured:Both numbers reach the
*Beam General Sectiondata line behind a log line whose own text admits"this is not a validated method of solving this issue". A section five times too stiff in the wrong
plane is not something a reader of the deck would notice.
And
Section.propertiescaches into_genprops; the function bound that cached object and assignedto its fields. So writing an Abaqus deck permanently changed the model — a Sesam or IFC export
later in the same process inherited the inflated
Iy. Cross-format contamination with nothing to see.The fix
Missing means
None; a computed zero is kept. WhereIyzgenuinely is unknown the substitute is0.0— symmetric — rather than the extreme of its legal range.Ix,IyandIzkeep theor <= 0.0test, because those cannot legitimately be zero for a realcross-section. That asymmetry is the point rather than an oversight, and a test pins it.
A copy is returned (
dataclasses.replace), so the model is never written to.Impossible properties raise. Adjusting
Iyby 10% until the inequality holds is how a sectionbecomes quietly stiffer. With a real
Iyzthe inequality cannot fail for a physically possiblesection, so a failure means the input is inconsistent, and the error names the numbers.
Evidence
assertion on the number that reaches the deck's data line.
a no-op for a channel, which is why the pair had to be tested together rather than assuming one
test covered both.
tests/core/shows only the pre-existing environmental failures (8, matching clean main).does not call this function — it reads Abaqus and writes Sesam — but it is this project's standing
regression gate and was run anyway.
Note on overlap
#386 touches this file but does not fix this — it carries the same
if gp.Iyz <= 0.0and the sameIyinflation. So this is not duplicated work, though the two will conflict. Happy to rebase onto#386 whenever it lands.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Nej3bWxJevs9GkH6tg1WKv