Skip to content

chore(release): v0.8.0#104

Open
liblaf-release-please[bot] wants to merge 2 commits into
mainfrom
release-please/main
Open

chore(release): v0.8.0#104
liblaf-release-please[bot] wants to merge 2 commits into
mainfrom
release-please/main

Conversation

@liblaf-release-please
Copy link
Copy Markdown

v0.8.0 - 2026-05-18

💥 BREAKING CHANGES

  • Refactored warp elastic energy module structure - 100c251 by @liblaf
  • remove the previous model, inverse, optim, scene, utils, consts, jax.model, and warp.energies APIs; use the new forward, collision, common, and warp.fem modules instead - b7c4348 by @liblaf

✨ Features

  • (apple) improve inverse simulation with coarse-to-fine transfer and enhanced solvers - 61d530e by @liblaf
  • (apple) implement hybrid JAX/Warp model framework and WarpPhaceV2 - 315b58e by @liblaf
  • (apple) add SMAS support and volume fraction for elastic energies - 0710f9c by @liblaf
  • (exp) update inverse-toy simulation and add Newton solver - cee4852 by @liblaf
  • (exp) add SMAS forward simulation experiments - e27c9da by @liblaf
  • (inverse) enhance inverse solver with constraint support and improved adjoint solver - 892dbfd by @liblaf
  • (smas) add stable neo-hookean simulation experiments - 22accae by @liblaf
  • add inverse toy experiments for forward simulation and muscle activation - 19c2c46 by @liblaf
  • [breaking] refactor elastic energies and add inverse physics module - 100c251 by @liblaf
  • add Neo-Hookean Warp energies and line-search PNCG - 0dbe70c by @liblaf
  • add staged PNCG continuation for forward solves - 6362af3 by @liblaf

🐛 Bug Fixes

  • (warp) correct ARAP muscle energy implementation and kernel function names - d734e9b by @liblaf

📝 Documentation

  • (smas) update inverse-unreachable clipped figures - ed3b81a by @liblaf

🎨 Styles

  • collapse multi-line return expressions to single line - 0603f83 by @liblaf

♻️ Code Refactoring

  • [breaking] replace model stack with forward simulation API - b7c4348 by @liblaf

❤️ Contributors

@liblaf-release-please liblaf-release-please Bot added automerge Merge the pull request once unit tests and other checks pass. release-please labels May 18, 2026
@liblaf-mega-linter
Copy link
Copy Markdown

liblaf-mega-linter Bot commented May 18, 2026

⚠️MegaLinter analysis: Success with warnings

Descriptor Linter Files Fixed Errors Warnings Elapsed time
✅ BASH shellcheck 2 0 0 0.02s
✅ BASH shfmt 2 0 0 0 1.32s
⚠️ COPYPASTE jscpd yes 406 no 8.33s
✅ JSON prettier 8 4 0 0 1.62s
✅ JSON v8r 8 0 0 3.45s
✅ MARKDOWN rumdl 19 0 0 0 1.35s
⚠️ PYTHON ruff yes yes 47 no 1.15s
✅ PYTHON ruff-format yes yes no no 0.16s
✅ REPOSITORY git_diff yes no no 1.33s
⚠️ SPELL cspell yes 6 no 5.79s

Detailed Issues

⚠️ SPELL / cspell - 6 errors
.vscode/settings.json:8:10      - Unknown word (grimp)      -- "**/.grimp_cache/": true,
	 Suggestions: [gimp, grim, grip, gramp, grime]
src/liblaf/apple/jax/fem/region/_region.py:23:5      - Unknown word (dhdr)       -- dhdr: Float[Array, "q a J
	 Suggestions: [ddr, xhdr, DDR, hdd, dada]
src/liblaf/apple/jax/fem/region/_region.py:88:9      - Unknown word (dhdr)       -- dhdr: Float[Array, "q a J
	 Suggestions: [ddr, xhdr, DDR, hdd, dada]
src/liblaf/apple/jax/fem/region/_region.py:92:44     - Unknown word (dhdr)       -- points[self.cells_local], dhdr, "c a I, q a J -> c
	 Suggestions: [ddr, xhdr, DDR, hdd, dada]
src/liblaf/apple/jax/fem/region/_region.py:101:13    - Unknown word (dhdr)       -- dhdr, drdX, "q a I, c q I
	 Suggestions: [ddr, xhdr, DDR, hdd, dada]
src/liblaf/apple/jax/fem/region/_region.py:104:14    - Unknown word (dhdr)       -- self.dhdr = dhdr
	 Suggestions: [ddr, xhdr, DDR, hdd, dada]
CSpell: Files checked: 260, skipped: 2, Issues found: 6 in 2 files.


You can skip this misspellings by defining the following .cspell.json file at the root of your repository
Of course, please correct real typos before :)

{
    "version": "0.2",
    "language": "en",
    "ignorePaths": [
        "**/node_modules/**",
        "**/vscode-extension/**",
        "**/.git/**",
        "**/.pnpm-lock.json",
        ".vscode",
        "package-lock.json",
        "megalinter-reports"
    ],
    "words": [
        "dhdr",
        "grimp"
    ]
}


You can also copy-paste megalinter-reports/LINTER_DEFAULT at the root of your repository
⚠️ COPYPASTE / jscpd - 406 errors
e found (python):
 - exp/2025/08/27/inverse/src/30-inverse.py [72:5 - 87:7] (15 lines, 221 tokens)
   exp/2025/08/27/inverse-segement/src/30-inverse.py [77:5 - 92:4]

Clone found (python):
 - exp/2025/08/27/inverse/src/30-inverse.py [85:9 - 92:3] (7 lines, 78 tokens)
   exp/2025/09/10/inverse-mixed/src/30-inverse.py [130:13 - 137:67]

Clone found (python):
 - exp/2025/08/27/inverse/src/30-inverse.py [87:2 - 102:13] (15 lines, 211 tokens)
   exp/2025/08/27/inverse-segement/src/30-inverse.py [92:2 - 107:15]

Clone found (python):
 - exp/2025/08/27/inverse/src/30-inverse.py [98:9 - 104:11] (6 lines, 94 tokens)
   exp/2025/09/10/inverse/src/30-inverse.py [133:9 - 157:15]

Clone found (python):
 - exp/2025/08/27/inverse/src/30-inverse.py [113:1 - 128:8] (15 lines, 166 tokens)
   exp/2025/09/24/inverse-grin/src/30-inverse.py [229:1 - 140:8]

Clone found (python):
 - exp/2025/08/27/inverse/src/30-inverse.py [122:12 - 130:11] (8 lines, 102 tokens)
   exp/2025/09/10/inverse-mixed/src/30-inverse.py [261:6 - 267:12]

Clone found (python):
 - exp/2025/08/27/inverse/src/30-inverse.py [131:8 - 173:2] (42 lines, 472 tokens)
   exp/2025/08/27/inverse-segement/src/30-inverse.py [138:2 - 242:2]

Clone found (python):
 - exp/2025/08/27/inverse/src/20-forward.py [1:1 - 29:11] (28 lines, 244 tokens)
   exp/2025/08/27/inverse-segement/src/20-forward.py [1:1 - 29:12]

Clone found (python):
 - exp/2025/08/27/inverse/src/20-forward.py [33:2 - 51:2] (18 lines, 191 tokens)
   exp/2025/08/27/inverse-segement/src/20-forward.py [33:15 - 51:2]

Clone found (python):
 - exp/2025/08/27/inverse/src/10-gen.py [32:5 - 39:2] (7 lines, 85 tokens)
   exp/2025/08/27/inverse-segement/src/10-gen.py [46:5 - 53:2]

Clone found (python):
 - exp/2025/07/30/phace/wind/main.py [126:5 - 136:6] (10 lines, 141 tokens)
   exp/2025/07/30/dynamics/collision/inter-collision/main.py [132:2 - 142:7]

Clone found (python):
 - exp/2025/07/30/phace/wind/main.py [139:9 - 152:2] (13 lines, 156 tokens)
   exp/2025/07/30/dynamics/collision/inter-collision/main.py [142:9 - 155:2]

Clone found (python):
 - exp/2025/07/30/phace/open-mouth/main.py [9:1 - 19:9] (10 lines, 102 tokens)
   exp/2025/07/30/phace/wind/main.py [8:1 - 18:8]

Clone found (python):
 - exp/2025/07/30/phace/open-mouth/main.py [24:5 - 36:10] (12 lines, 96 tokens)
   exp/2025/07/30/dynamics/collision/collision-bunny-bunny/main.py [19:5 - 31:5]

Clone found (python):
 - exp/2025/07/30/phace/open-mouth/main.py [25:2 - 55:8] (30 lines, 317 tokens)
   exp/2025/07/30/phace/wind/main.py [30:2 - 60:11]

Clone found (python):
 - exp/2025/07/30/phace/open-mouth/main.py [95:2 - 105:17] (10 lines, 145 tokens)
   exp/2025/07/30/dynamics/collision/inter-collision/main.py [132:2 - 136:22]

Clone found (python):
 - exp/2025/07/30/phace/open-mouth/main.py [105:2 - 119:7] (14 lines, 153 tokens)
   exp/2025/07/30/phace/wind/main.py [138:9 - 155:5]

Clone found (python):
 - exp/2025/07/30/phace/grin/main.py [1:1 - 12:6] (11 lines, 103 tokens)
   exp/2025/07/30/phace/wind/main.py [1:1 - 12:7]

Clone found (python):
 - exp/2025/07/30/phace/grin/main.py [19:5 - 36:8] (17 lines, 144 tokens)
   exp/2025/07/30/phace/open-mouth/main.py [20:5 - 42:8]

Clone found (python):
 - exp/2025/07/30/phace/dynamics/main.py [1:1 - 14:29] (13 lines, 106 tokens)
   exp/2025/07/30/phace/wind/main.py [1:1 - 15:6]

Clone found (python):
 - exp/2025/07/30/phace/dynamics/main.py [35:5 - 53:14] (18 lines, 166 tokens)
   exp/2025/07/30/phace/wind/main.py [32:5 - 44:21]

Clone found (python):
 - exp/2025/07/30/phace/dynamics/main.py [53:14 - 66:2] (13 lines, 146 tokens)
   exp/2025/07/30/phace/grin/main.py [44:21 - 56:2]

Clone found (python):
 - exp/2025/07/30/phace/dynamics/main.py [90:10 - 97:2] (7 lines, 79 tokens)
   exp/2025/07/30/phace/grin/main.py [90:6 - 97:2]

Clone found (python):
 - exp/2025/07/30/phace/dynamics/main.py [107:5 - 117:5] (10 lines, 139 tokens)
   exp/2025/07/30/phace/grin/main.py [101:5 - 111:18]

Clone found (python):
 - exp/2025/07/30/phace/dynamics/main.py [118:5 - 126:15] (8 lines, 101 tokens)
   exp/2025/07/30/phace/grin/main.py [112:18 - 120:13]

Clone found (python):
 - exp/2025/07/30/phace/collide-ball/main.py [9:1 - 18:4] (9 lines, 95 tokens)
   exp/2025/07/30/phace/wind/main.py [9:1 - 19:4]

Clone found (python):
 - exp/2025/07/30/phace/collide-ball/main.py [12:7 - 35:6] (23 lines, 197 tokens)
   exp/2025/07/30/phace/grin/main.py [12:4 - 41:13]

Clone found (python):
 - exp/2025/07/30/phace/collide-ball/main.py [35:6 - 42:13] (7 lines, 122 tokens)
   exp/2025/07/30/phace/wind/main.py [41:13 - 48:11]

Clone found (python):
 - exp/2025/07/30/phace/close-mouth/main.py [1:1 - 22:8] (21 lines, 180 tokens)
   exp/2025/07/30/phace/open-mouth/main.py [1:1 - 21:6]

Clone found (python):
 - exp/2025/07/30/phace/close-mouth/main.py [29:5 - 57:8] (28 lines, 313 tokens)
   exp/2025/07/30/phace/wind/main.py [32:5 - 60:11]

Clone found (python):
 - exp/2025/07/30/phace/close-mouth/main.py [101:5 - 140:17] (39 lines, 452 tokens)
   exp/2025/07/30/phace/open-mouth/main.py [55:5 - 124:5]

Clone found (python):
 - exp/2025/07/30/phace/close-mouth/main.py [140:5 - 165:2] (25 lines, 353 tokens)
   exp/2025/07/30/phace/open-mouth/main.py [94:5 - 155:2]

Clone found (python):
 - exp/2025/07/30/dynamics/free-falling/main.py [25:2 - 58:4] (33 lines, 360 tokens)
   exp/2025/07/30/dynamics/collision/collision-bunny/main.py [24:2 - 57:7]

Clone found (python):
 - exp/2025/07/30/dynamics/free-falling/main.py [78:5 - 84:3] (6 lines, 86 tokens)
   exp/2025/07/30/dynamics/collision/collision-bunny/main.py [75:5 - 81:3]

Clone found (python):
 - exp/2025/07/30/dynamics/free-falling/main.py [137:5 - 151:10] (14 lines, 138 tokens)
   exp/2025/07/30/dynamics/collision/collision-bunny-sphere/main.py [69:5 - 83:10]

Clone found (python):
 - exp/2025/04/30/fat-muscle/src/20-simulate.py [17:5 - 30:6] (13 lines, 138 tokens)
   exp/2025/04/30/human-head/src/10-simulate.py [18:5 - 31:8]

Clone found (python):
 - exp/2025/04/30/fat-muscle/src/20-simulate.py [48:2 - 58:6] (10 lines, 136 tokens)
   exp/2025/04/30/human-head/src/10-simulate.py [47:2 - 57:8]

Clone found (python):
 - exp/2025/04/30/fat-muscle/src/20-simulate.py [59:6 - 72:3] (13 lines, 109 tokens)
   exp/2025/04/30/human-head/src/10-simulate.py [58:8 - 71:4]

Clone found (python):
 - exp/2025/04/30/cubic/src/10-gen.py [21:4 - 44:4] (23 lines, 196 tokens)
   exp/2025/04/30/human-head/src/10-simulate.py [24:7 - 46:9]

Clone found (python):
 - exp/2025/04/30/cubic/src/10-gen.py [44:2 - 71:2] (27 lines, 305 tokens)
   exp/2025/04/30/fat-muscle/src/20-simulate.py [46:3 - 73:2]

Clone found (python):
 - src/liblaf/apple/warp/model/_adapter.py [83:19 - 88:10] (5 lines, 74 tokens)
   src/liblaf/apple/warp/model/_adapter.py [66:14 - 69:5]

Clone found (python):
 - src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [55:1 - 61:27] (6 lines, 83 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [34:4 - 40:9]

Clone found (python):
 - src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [78:6 - 90:8] (12 lines, 171 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [34:6 - 67:44]

Clone found (python):
 - src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [98:6 - 104:2] (6 lines, 82 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [19:6 - 24:3]

Clone found (python):
 - src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [99:9 - 111:8] (12 lines, 153 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [55:6 - 67:44]

Clone found (python):
 - src/liblaf/apple/warp/fem/_stable_neo_hookean.py [6:1 - 19:3] (13 lines, 93 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [7:1 - 20:2]

Clone found (python):
 - src/liblaf/apple/warp/fem/_stable_neo_hookean.py [22:2 - 32:3] (10 lines, 126 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [25:2 - 35:2]

Clone found (python):
 - src/liblaf/apple/warp/fem/_stable_neo_hookean.py [71:4 - 81:8] (10 lines, 127 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean.py [50:1 - 60:44]

Clone found (python):
 - src/liblaf/apple/warp/fem/_stable_neo_hookean.py [90:9 - 100:8] (10 lines, 121 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean.py [50:6 - 60:44]

Clone found (python):
 - src/liblaf/apple/warp/fem/_stable_neo_hookean.py [115:2 - 152:8] (37 lines, 339 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean_muscle.py [131:13 - 168:11]

Clone found (python):
 - src/liblaf/apple/warp/fem/_base.py [231:6 - 238:2] (7 lines, 88 tokens)
   src/liblaf/apple/warp/fem/_base.py [207:9 - 214:7]

Clone found (python):
 - src/liblaf/apple/warp/fem/_base.py [277:5 - 284:2] (7 lines, 92 tokens)
   src/liblaf/apple/warp/fem/_base.py [253:9 - 260:4]

Clone found (python):
 - src/liblaf/apple/warp/fem/_base.py [308:9 - 314:7] (6 lines, 88 tokens)
   src/liblaf/apple/warp/fem/_base.py [254:9 - 260:4]

Clone found (python):
 - src/liblaf/apple/warp/fem/_base.py [334:2 - 342:7] (8 lines, 95 tokens)
   src/liblaf/apple/warp/fem/_base.py [304:2 - 312:10]

Clone found (python):
 - src/liblaf/apple/warp/fem/_base.py [369:9 - 376:7] (7 lines, 105 tokens)
   src/liblaf/apple/warp/fem/_base.py [338:9 - 345:7]

Clone found (python):
 - src/liblaf/apple/warp/fem/_arap.py [1:1 - 19:3] (18 lines, 128 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean.py [1:1 - 20:2]

Clone found (python):
 - src/liblaf/apple/warp/fem/_arap.py [87:2 - 124:3] (37 lines, 350 tokens)
   src/liblaf/apple/warp/fem/_stable_neo_hookean.py [115:2 - 168:11]

Clone found (python):
 - exp/2025/07/30/static/stretch.py [12:5 - 19:2] (7 lines, 107 tokens)
   exp/2025/07/30/static/random/main.py [19:5 - 25:3]

Clone found (python):
 - exp/2025/07/30/static/stretch.py [28:9 - 39:3] (11 lines, 124 tokens)
   exp/2025/07/30/static/random/main.py [40:9 - 51:8]

Clone found (python):
 - exp/2025/07/30/static/stretch.py [49:1 - 54:6] (5 lines, 83 tokens)
   exp/2025/07/30/static/random/main.py [60:17 - 65:6]

Clone found (python):
 - exp/2025/07/30/static/stretch.py [67:5 - 77:10] (10 lines, 115 tokens)
   exp/2025/07/30/static/random/main.py [73:5 - 83:6]

Clone found (python):
 - src/liblaf/apple/collision/_collision.py [161:5 - 178:29] (17 lines, 100 tokens)
   src/liblaf/apple/collision/_collision.py [138:7 - 155:23]

Clone found (python):
 - src/liblaf/apple/collision/_collision.py [188:2 - 204:29] (16 lines, 88 tokens)
   src/liblaf/apple/collision/_collision.py [139:9 - 155:23]

Clone found (python):
 - src/liblaf/apple/collision/_collision.py [211:2 - 227:24] (16 lines, 95 tokens)
   src/liblaf/apple/collision/_collision.py [139:2 - 155:23]

Clone found (python):
 - src/liblaf/apple/collision/_collision.py [235:2 - 251:29] (16 lines, 88 tokens)
   src/liblaf/apple/collision/_collision.py [139:9 - 155:23]

Clone found (python):
 - tests/forward/test_static_simulation.py [77:5 - 85:7] (8 lines, 80 tokens)
   exp/2026/05/06/toy/src/10-stretched-cylinder.py [84:17 - 92:13]

Clone found (python):
 - benches/test_aggregation.py [115:9 - 129:7] (14 lines, 161 tokens)
   benches/test_aggregation.py [68:2 - 80:38]

Clone found (python):
 - benches/bench_pncg_branching_backends.py [180:16 - 192:4] (12 lines, 87 tokens)
   benches/bench_pncg_branching_backends.py [141:10 - 153:5]

Clone found (python):
 - benches/bench_pncg_branching_backends.py [347:9 - 357:3] (10 lines, 94 tokens)
   benches/bench_pncg_branching_backends.py [279:9 - 289:5]

Clone found (python):
 - benches/bench_pncg_branching_backends.py [459:36 - 481:2] (22 lines, 172 tokens)
   benches/bench_pncg_branching_backends.py [413:30 - 435:14]

Clone found (python):
 - benches/bench_pncg_branching_backends.py [649:6 - 663:18] (14 lines, 159 tokens)
   benches/bench_pncg_branching_backends.py [475:4 - 489:21]

┌────────┬────────────────┬─────────────┬──────────────┬──────────────┬──────────────────┬───────────────────┐
│ Format │ Files analyzed │ Total lines │ Total tokens │ Clones found │ Duplicated lines │ Duplicated tokens │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ python │ 175            │ 24390       │ 246126       │ 406          │ 10813 (44.33%)   │ 113316 (46.04%)   │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ bash   │ 2              │ 45          │ 136          │ 0            │ 0 (0%)           │ 0 (0%)            │
├────────┼────────────────┼─────────────┼──────────────┼──────────────┼──────────────────┼───────────────────┤
│ Total: │ 177            │ 24435       │ 246262       │ 406          │ 10813 (44.25%)   │ 113316 (46.01%)   │
└────────┴────────────────┴─────────────┴──────────────┴──────────────┴──────────────────┴───────────────────┘
Found 406 clones.
HTML report saved to megalinter-reports/copy-paste/html/
ERROR: jscpd found too many duplicates (44.25%) over threshold (0%)
Error: ERROR: jscpd found too many duplicates (44.25%) over threshold (0%)
    at ThresholdReporter.report (/node-deps/node_modules/@jscpd/finder/dist/index.js:615:13)
    at /node-deps/node_modules/@jscpd/finder/dist/index.js:109:18
    at Array.forEach (<anonymous>)
    at /node-deps/node_modules/@jscpd/finder/dist/index.js:108:22
    at async /node-deps/node_modules/jscpd/dist/bin/jscpd.js:9:5

(Truncated to last 13333 characters out of 78347)
⚠️ PYTHON / ruff - 47 errors
able first
   --> exp/2026/01/28/smas/src/20-forward-ext-force-stable-neo-hookean-ramped.py:206:21
    |
204 |               if not stage_result.solver_result.success:
205 |                   raise RuntimeError(
206 | /                     "forward stage failed at "
207 | |                     f"force scale {force_scale:g} with status "
208 | |                     f"{stage_result.solver_result.status}"
    | |__________________________________________________________^
209 |                   )
210 |               cherries.set_step(stage_index)
    |
help: Assign to variable; remove f-string literal

RUF046 Value being cast to `int` is already an integer
  --> exp/2026/01/28/smas/src/20-forward-ext-force-stable-neo-hookean-smas-only.py:48:20
   |
46 | def format_lambda_value(lambda_value: float) -> str:
47 |     if np.isclose(lambda_value, round(lambda_value)):
48 |         return str(int(round(lambda_value)))
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^
49 |     mantissa, exponent = f"{lambda_value:.0e}".split("e")
50 |     return f"{mantissa}e{int(exponent)}"
   |
help: Remove unnecessary `int` call

TRY003 Avoid specifying long messages outside the exception class
  --> exp/2026/01/28/smas/src/20-forward-ext-force-stable-neo-hookean-smas-only.py:57:15
   |
55 |   ) -> pv.UnstructuredGrid:
56 |       if "Force" not in mesh.point_data:
57 |           raise KeyError(
   |  _______________^
58 | |             "mesh is missing point_data['Force']; run 11-smas-bottom-force.py first"
59 | |         )
   | |_________^
60 |       mesh.point_data["Force"] = force_scale * np.asarray(mesh.point_data["Force"])
61 |       return mesh
   |

EM101 Exception must not use a string literal, assign to variable first
  --> exp/2026/01/28/smas/src/20-forward-ext-force-stable-neo-hookean-smas-only.py:58:13
   |
56 |     if "Force" not in mesh.point_data:
57 |         raise KeyError(
58 |             "mesh is missing point_data['Force']; run 11-smas-bottom-force.py first"
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
59 |         )
60 |     mesh.point_data["Force"] = force_scale * np.asarray(mesh.point_data["Force"])
   |
help: Assign to variable; remove string literal

RUF046 Value being cast to `int` is already an integer
  --> exp/2026/01/28/smas/src/20-forward-ext-force-stable-neo-hookean.py:49:20
   |
47 | def format_lambda_value(lambda_value: float) -> str:
48 |     if np.isclose(lambda_value, round(lambda_value)):
49 |         return str(int(round(lambda_value)))
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^
50 |     mantissa, exponent = f"{lambda_value:.0e}".split("e")
51 |     return f"{mantissa}e{int(exponent)}"
   |
help: Remove unnecessary `int` call

RUF046 Value being cast to `int` is already an integer
  --> exp/2026/01/28/smas/src/20-forward-ext-force.py:49:20
   |
47 | def format_lambda_value(lambda_value: float) -> str:
48 |     if np.isclose(lambda_value, round(lambda_value)):
49 |         return str(int(round(lambda_value)))
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^
50 |     mantissa, exponent = f"{lambda_value:.0e}".split("e")
51 |     return f"{mantissa}e{int(exponent)}"
   |
help: Remove unnecessary `int` call

ARG001 Unused function argument: `failure_dir`
   --> exp/2026/01/28/smas/src/30-inverse-unreachable-stable-neo-hookean.py:286:5
    |
284 |     target: pv.UnstructuredGrid,
285 |     forward: Forward,
286 |     failure_dir: Path,
    |     ^^^^^^^^^^^
287 | ) -> MyInverse:
288 |     surface_indices: Integer[Array, " surface_points"] = mesh.surface_indices()
    |

D205 1 blank line required between summary line and description
  --> exp/2026/04/18/2d-demo/src/10-simulation.py:1:1
   |
 1 | / """2D Muscle-Fascia Neohookean FEM Simulation
 2 | | ==========================================
 3 | |
 4 | | Simulates a flat meat slab (100x11 elements) with a stiff fascia middle layer.
 5 | | Uses analytical First Piola-Kirchhoff stress with NumPy vectorized computation.
 6 | | Real-time visualization with Pygame.
 7 | |
 8 | | Material: Neohookean hyperelastic
 9 | |   Psi(F) = mu/2 * (tr(F^T F) - 2) - mu * ln(J) + lam/2 * (ln J)^2
10 | |   P      = mu * (F - F^{-T}) + lam * ln(J) * F^{-T}
11 | |
12 | | Controls:
13 | |     Up/Down    Increase/decrease wind force
14 | |     R          Reset simulation
15 | |     SPACE      Pause/resume
16 | |     W          Toggle wireframe overlay
17 | |     ESC        Quit
18 | | """
   | |___^
19 |
20 |   import sys
   |
help: Insert single blank line

D415 First line should end with a period, question mark, or exclamation point
  --> exp/2026/04/18/2d-demo/src/10-simulation.py:1:1
   |
 1 | / """2D Muscle-Fascia Neohookean FEM Simulation
 2 | | ==========================================
 3 | |
 4 | | Simulates a flat meat slab (100x11 elements) with a stiff fascia middle layer.
 5 | | Uses analytical First Piola-Kirchhoff stress with NumPy vectorized computation.
 6 | | Real-time visualization with Pygame.
 7 | |
 8 | | Material: Neohookean hyperelastic
 9 | |   Psi(F) = mu/2 * (tr(F^T F) - 2) - mu * ln(J) + lam/2 * (ln J)^2
10 | |   P      = mu * (F - F^{-T}) + lam * ln(J) * F^{-T}
11 | |
12 | | Controls:
13 | |     Up/Down    Increase/decrease wind force
14 | |     R          Reset simulation
15 | |     SPACE      Pause/resume
16 | |     W          Toggle wireframe overlay
17 | |     ESC        Quit
18 | | """
   | |___^
19 |
20 |   import sys
   |
help: Add closing punctuation

ANN001 Missing type annotation for function argument `f2v`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:126:16
    |
126 | def init_state(f2v):
    |                ^^^
127 |     """Initialize node positions and precompute reference configuration.
    |

ANN001 Missing type annotation for function argument `pos`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:185:28
    |
185 | def compute_elastic_forces(pos, f2v, Dm_inv, Dm_inv_T, W, elem_mu, elem_lam):
    |                            ^^^
186 |     """Compute internal elastic forces using the analytical Neohookean stress.
    |

ANN001 Missing type annotation for function argument `f2v`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:185:33
    |
185 | def compute_elastic_forces(pos, f2v, Dm_inv, Dm_inv_T, W, elem_mu, elem_lam):
    |                                 ^^^
186 |     """Compute internal elastic forces using the analytical Neohookean stress.
    |

ANN001 Missing type annotation for function argument `Dm_inv`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:185:38
    |
185 | def compute_elastic_forces(pos, f2v, Dm_inv, Dm_inv_T, W, elem_mu, elem_lam):
    |                                      ^^^^^^
186 |     """Compute internal elastic forces using the analytical Neohookean stress.
    |

ANN001 Missing type annotation for function argument `Dm_inv_T`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:185:46
    |
185 | def compute_elastic_forces(pos, f2v, Dm_inv, Dm_inv_T, W, elem_mu, elem_lam):
    |                                              ^^^^^^^^
186 |     """Compute internal elastic forces using the analytical Neohookean stress.
    |

ANN001 Missing type annotation for function argument `W`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:185:56
    |
185 | def compute_elastic_forces(pos, f2v, Dm_inv, Dm_inv_T, W, elem_mu, elem_lam):
    |                                                        ^
186 |     """Compute internal elastic forces using the analytical Neohookean stress.
    |

ANN001 Missing type annotation for function argument `elem_mu`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:185:59
    |
185 | def compute_elastic_forces(pos, f2v, Dm_inv, Dm_inv_T, W, elem_mu, elem_lam):
    |                                                           ^^^^^^^
186 |     """Compute internal elastic forces using the analytical Neohookean stress.
    |

ANN001 Missing type annotation for function argument `elem_lam`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:185:68
    |
185 | def compute_elastic_forces(pos, f2v, Dm_inv, Dm_inv_T, W, elem_mu, elem_lam):
    |                                                                    ^^^^^^^^
186 |     """Compute internal elastic forces using the analytical Neohookean stress.
    |

ANN001 Missing type annotation for function argument `pos`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:256:5
    |
255 | def substep(
256 |     pos,
    |     ^^^
257 |     vel,
258 |     rest_pos,
    |

ANN001 Missing type annotation for function argument `vel`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:257:5
    |
255 | def substep(
256 |     pos,
257 |     vel,
    |     ^^^
258 |     rest_pos,
259 |     f2v,
    |

ANN001 Missing type annotation for function argument `rest_pos`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:258:5
    |
256 |     pos,
257 |     vel,
258 |     rest_pos,
    |     ^^^^^^^^
259 |     f2v,
260 |     Dm_inv,
    |

ANN001 Missing type annotation for function argument `f2v`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:259:5
    |
257 |     vel,
258 |     rest_pos,
259 |     f2v,
    |     ^^^
260 |     Dm_inv,
261 |     Dm_inv_T,
    |

ANN001 Missing type annotation for function argument `Dm_inv`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:260:5
    |
258 |     rest_pos,
259 |     f2v,
260 |     Dm_inv,
    |     ^^^^^^
261 |     Dm_inv_T,
262 |     W,
    |

ANN001 Missing type annotation for function argument `Dm_inv_T`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:261:5
    |
259 |     f2v,
260 |     Dm_inv,
261 |     Dm_inv_T,
    |     ^^^^^^^^
262 |     W,
263 |     elem_mu,
    |

ANN001 Missing type annotation for function argument `W`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:262:5
    |
260 |     Dm_inv,
261 |     Dm_inv_T,
262 |     W,
    |     ^
263 |     elem_mu,
264 |     elem_lam,
    |

ANN001 Missing type annotation for function argument `elem_mu`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:263:5
    |
261 |     Dm_inv_T,
262 |     W,
263 |     elem_mu,
    |     ^^^^^^^
264 |     elem_lam,
265 |     fixed_mask,
    |

ANN001 Missing type annotation for function argument `elem_lam`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:264:5
    |
262 |     W,
263 |     elem_mu,
264 |     elem_lam,
    |     ^^^^^^^^
265 |     fixed_mask,
266 |     bottom_mask,
    |

ANN001 Missing type annotation for function argument `fixed_mask`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:265:5
    |
263 |     elem_mu,
264 |     elem_lam,
265 |     fixed_mask,
    |     ^^^^^^^^^^
266 |     bottom_mask,
267 |     wind,
    |

ANN001 Missing type annotation for function argument `bottom_mask`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:266:5
    |
264 |     elem_lam,
265 |     fixed_mask,
266 |     bottom_mask,
    |     ^^^^^^^^^^^
267 |     wind,
268 |     mass,
    |

ANN001 Missing type annotation for function argument `wind`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:267:5
    |
265 |     fixed_mask,
266 |     bottom_mask,
267 |     wind,
    |     ^^^^
268 |     mass,
269 | ):
    |

ANN001 Missing type annotation for function argument `mass`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:268:5
    |
266 |     bottom_mask,
267 |     wind,
268 |     mass,
    |     ^^^^
269 | ):
270 |     """Single substep of Symplectic Euler integration.
    |

ANN001 Missing type annotation for function argument `sim_pos`
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:307:19
    |
307 | def sim_to_screen(sim_pos):
    |                   ^^^^^^^
308 |     """Convert simulation coordinates to screen pixel coordinates (y-flipped)."""
309 |     screen = np.empty_like(sim_pos)
    |

C901 `main` is too complex (18 > 10)
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:320:5
    |
320 | def main():
    |     ^^^^
321 |     # --- Build mesh & initialize state ---
322 |     f2v, is_fascia, elem_mu, elem_lam = build_mesh()
    |

PLR0912 Too many branches (17 > 12)
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:320:5
    |
320 | def main():
    |     ^^^^
321 |     # --- Build mesh & initialize state ---
322 |     f2v, is_fascia, elem_mu, elem_lam = build_mesh()
    |

PLR0915 Too many statements (57 > 50)
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:320:5
    |
320 | def main():
    |     ^^^^
321 |     # --- Build mesh & initialize state ---
322 |     f2v, is_fascia, elem_mu, elem_lam = build_mesh()
    |

FBT003 Boolean positional value in function call
   --> exp/2026/04/18/2d-demo/src/10-simulation.py:431:37
    |
429 |         y_pos = 12
430 |         for fnt, text, color in hud_lines:
431 |             surf = fnt.render(text, True, color)
    |                                     ^^^^
432 |             screen.blit(surf, (15, y_pos))
433 |             y_pos += 28
    |

B007 Loop control variable `step` not used within loop body
   --> exp/2026/04/18/2d-demo/src/30-simulation-stable-neo-hookean-self-collision.py:829:9
    |
827 |     contact_points = np.empty((0, 2), dtype=pos.dtype)
828 |
829 |     for step in range(SUBSTEPS):
    |         ^^^^
830 |         substep_contacts, substep_points = substep(
831 |             pos,
    |
help: Rename unused `step` to `_step`

Found 47 errors.
No fixes available (13 hidden fixes can be enabled with the `--unsafe-fixes` option).

(Truncated to last 13333 characters out of 18919)

See detailed reports in MegaLinter artifacts

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge Merge the pull request once unit tests and other checks pass. release-please

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant