Repository navigation
Fix force_field x/y transpose; extract a ComplianceEvaluator seam (0.3.0) - #30
Merged
Merged
Conversation
…nt anchor M0 of the 0.3.0 optimizer work. Pull the per-iteration FE physics (assemble K(xPhys), solve, compliance, sensitivities) out of the optimizer's CPU loop into a reusable ComplianceEvaluator, so a future NLopt MMA backend can call the same physics. The CPU branch now calls the evaluator; the GPU branch is left inline. Behaviour-preserving: top3d() output is byte-identical (array_equal, max diff 0.0; full suite green). Adds tests/test_gradient_check.py (R1): a finite-difference check that the analytic sensitivity dc is the true gradient of compliance through the full design -> filter -> physics chain (mutation-proven: a sign flip or a 1% scaling fails it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…anchor A build_edof rewrite (the commit that added custom force_field support and moved the default load from -y to -z) silently swapped the H8 element's first two local axes, so a user-supplied Fx acted along the y (nely) axis and Fy along x. This was invisible to the default -z load, the shipped cantilever examples, and the golden master (all -z / x<->y-symmetric), so only force_field loads with distinct Fx vs Fy were affected. Restore the correct local node ordering in build_edof (swap local nodes 1<->3 and 5<->7) so the element's first axis aligns with the grid's x (nelx). The golden-master result is unchanged (max abs difference ~4e-13) and the README force_field recipe now behaves as documented (a -y tip load bending in the x-y plane). - tests/test_force_direction.py: locks the public (x, y, z) force convention (Fx is axial on an x-long beam, Fy/Fz transverse) so the transpose cannot regress; also covers the one-time notice (fires for Fx/Fy loads, silent for Fz-only). - tests/test_physics_anchor.py (R2): closed-form absolute anchor -- single-element energy patch (U = 1/2) and a uniaxial bar through the evaluator (C = N) -- under the corrected convention; mutation-proven (KE*2 trips both). - One-time transitional notice for force_field loads carrying Fx/Fy (removed in 0.4.0). - CHANGELOG entry under [Unreleased]. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…design notes The breaking force-direction fix takes the next release number 0.3.0 (mirroring 0.2.0 = the STL coordinate fix); Core Functionality & Interface (mass minimization, GUI, convergence) moves to 0.4.0 and Pre-release Stabilization to 0.5.0, per RELEASING.md (a correctness release takes a number independently of the roadmap; on a collision the roadmap milestone is pushed up). The convergence item is also marked conditional (coupled to mass minimization, not a standalone change). Adds the design notes that drove the 0.3.0 optimizer decision under docs/literature-reviews/: an optimizer survey / decision memo (adopt MMA via NLopt; reject GPL ports and NN/autodiff-as-optimizer) and the implementation plan plus validation strategy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… docstring - README: the roadmap renumber left the table of contents pointing at the old headings (dead anchors for 0.3.0/0.4.0, missing 0.5.0); sync it with the renamed sections. - 0.3.0-implementation-plan.md: note the renumber (this mass-minimization milestone is now 0.4.0; the force-fix took 0.3.0) so the title no longer contradicts the roadmap. - ComplianceEvaluator docstring: stop calling the returned dv the "true gradient" -- with obstacles it is not the exact volume-constraint gradient (cells zeroed after, not before, the filter). Pre-existing behaviour; flagged for the future MMA backend. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 16, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two pieces of 0.3.0 foundation work, with a correctness fix as the headline.
1. Fix a
force_fieldx/y transpose (breaking, but narrow)force_fieldcomponentsFxandFywere swapped: a user-suppliedFxacted along the y (nely) axis andFyalong x. The root cause is abuild_edofrewrite — the same change that added customforce_fieldsupport and moved the default load from-yto-z— which silently swapped the H8 element's first two local axes.Because the new default load is
-z(unaffected by an x↔y swap), every shipped cantilever example and the golden master kept working, so the regression stayed hidden. It only affectsforce_fieldloads carrying distinctFxvsFy.Evidence. On a beam long in x,
Fxshould be axial (stiff) whileFy/Fzbend it (much softer). Before the fix,Fywas the stiff one — i.e.Fyacted along x. The fix restores the correct local-node ordering inbuild_edofso the element's first axis aligns with the grid's x (nelx).Impact. Breaking only for
force_fieldloads with both x and y components. The default load,-z-only loads, supports, obstacle/geometry positions, and the golden-master result are unchanged (max abs difference ~4e-13). The READMEforce_fieldcantilever recipe now behaves as documented. A one-time notice fires for affected loads; pin the previous release to reproduce the old behaviour.2. Extract a
ComplianceEvaluatorseam (behaviour-preserving)Pull the per-iteration FE physics (assemble
K(xPhys), solve, compliance, sensitivities) out of the optimizer's CPU loop into a reusable evaluator, so a future MMA optimizer backend can call the same physics.top3d()output is byte-identical (array_equal, max diff 0.0).Tests
test_force_direction.py(x, y, z)force convention (Fxaxial on an x-long beam) — mutation-verified red→green; plus the one-time noticetest_gradient_check.pytest_physics_anchor.pyU = 1/2, uniaxial barC = NFull CPU suite green.
Roadmap
0.3.0 becomes the force-direction-fix release (mirroring 0.2.0 = the STL coordinate fix); Core Functionality & Interface (mass minimization, GUI) moves to 0.4.0. Design notes for the 0.3.0 optimizer decision (adopt MMA via NLopt) are under
docs/literature-reviews/.🤖 Generated with Claude Code