mechdsl-core 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. mechdsl_core-0.2.1/.gitignore +75 -0
  2. mechdsl_core-0.2.1/LICENSE +21 -0
  3. mechdsl_core-0.2.1/PKG-INFO +41 -0
  4. mechdsl_core-0.2.1/README.md +13 -0
  5. mechdsl_core-0.2.1/pyproject.toml +71 -0
  6. mechdsl_core-0.2.1/src/mechdsl/__init__.py +263 -0
  7. mechdsl_core-0.2.1/src/mechdsl/codegen/__init__.py +90 -0
  8. mechdsl_core-0.2.1/src/mechdsl/codegen/_experimental.py +71 -0
  9. mechdsl_core-0.2.1/src/mechdsl/codegen/anisotropic_emitter.py +233 -0
  10. mechdsl_core-0.2.1/src/mechdsl/codegen/artifact.py +319 -0
  11. mechdsl_core-0.2.1/src/mechdsl/codegen/boundary_codegen.py +277 -0
  12. mechdsl_core-0.2.1/src/mechdsl/codegen/common.py +1 -0
  13. mechdsl_core-0.2.1/src/mechdsl/codegen/einsum_optimizer.py +507 -0
  14. mechdsl_core-0.2.1/src/mechdsl/codegen/energy_emitter.py +316 -0
  15. mechdsl_core-0.2.1/src/mechdsl/codegen/family_registry.py +226 -0
  16. mechdsl_core-0.2.1/src/mechdsl/codegen/hex20_tables.py +314 -0
  17. mechdsl_core-0.2.1/src/mechdsl/codegen/hex8_reduced_tables.py +91 -0
  18. mechdsl_core-0.2.1/src/mechdsl/codegen/hex8_tables.py +214 -0
  19. mechdsl_core-0.2.1/src/mechdsl/codegen/hourglass.py +359 -0
  20. mechdsl_core-0.2.1/src/mechdsl/codegen/mfem_printer.py +819 -0
  21. mechdsl_core-0.2.1/src/mechdsl/codegen/mfem_template/CMakeLists.txt +51 -0
  22. mechdsl_core-0.2.1/src/mechdsl/codegen/moose_printer.py +603 -0
  23. mechdsl_core-0.2.1/src/mechdsl/codegen/moose_template/__init__.py +6 -0
  24. mechdsl_core-0.2.1/src/mechdsl/codegen/moose_template/input_template.i +112 -0
  25. mechdsl_core-0.2.1/src/mechdsl/codegen/spectral_emitter.py +284 -0
  26. mechdsl_core-0.2.1/src/mechdsl/codegen/taichi_printer.py +3597 -0
  27. mechdsl_core-0.2.1/src/mechdsl/codegen/templates/__init__.py +1 -0
  28. mechdsl_core-0.2.1/src/mechdsl/codegen/tet10_tables.py +267 -0
  29. mechdsl_core-0.2.1/src/mechdsl/codegen/tet4_tables.py +178 -0
  30. mechdsl_core-0.2.1/src/mechdsl/frontend/ARCHITECTURE.md +55 -0
  31. mechdsl_core-0.2.1/src/mechdsl/frontend/__init__.py +406 -0
  32. mechdsl_core-0.2.1/src/mechdsl/frontend/directives.py +748 -0
  33. mechdsl_core-0.2.1/src/mechdsl/frontend/math_parser.py +541 -0
  34. mechdsl_core-0.2.1/src/mechdsl/frontend/parser.py +294 -0
  35. mechdsl_core-0.2.1/src/mechdsl/frontend/two_point.py +12 -0
  36. mechdsl_core-0.2.1/src/mechdsl/integration/__init__.py +1100 -0
  37. mechdsl_core-0.2.1/src/mechdsl/ir/ARCHITECTURE.md +40 -0
  38. mechdsl_core-0.2.1/src/mechdsl/ir/__init__.py +39 -0
  39. mechdsl_core-0.2.1/src/mechdsl/ir/element_factory.py +192 -0
  40. mechdsl_core-0.2.1/src/mechdsl/ir/element_ir.py +822 -0
  41. mechdsl_core-0.2.1/src/mechdsl/ir/mechanics_ir.py +1438 -0
  42. mechdsl_core-0.2.1/src/mechdsl/lawgen/README.md +32 -0
  43. mechdsl_core-0.2.1/src/mechdsl/lawgen/REUSE.md +120 -0
  44. mechdsl_core-0.2.1/src/mechdsl/lawgen/__init__.py +40 -0
  45. mechdsl_core-0.2.1/src/mechdsl/lawgen/budgets.py +404 -0
  46. mechdsl_core-0.2.1/src/mechdsl/lawgen/carrier_emitter.py +447 -0
  47. mechdsl_core-0.2.1/src/mechdsl/lawgen/cli.py +588 -0
  48. mechdsl_core-0.2.1/src/mechdsl/lawgen/contracts.py +276 -0
  49. mechdsl_core-0.2.1/src/mechdsl/lawgen/diagnostics.py +263 -0
  50. mechdsl_core-0.2.1/src/mechdsl/lawgen/guard_transforms.py +234 -0
  51. mechdsl_core-0.2.1/src/mechdsl/lawgen/manifest.py +461 -0
  52. mechdsl_core-0.2.1/src/mechdsl/lawgen/sympy_to_taichi.py +738 -0
  53. mechdsl_core-0.2.1/src/mechdsl/lawgen/test_emitter.py +503 -0
  54. mechdsl_core-0.2.1/src/mechdsl/lib/__init__.py +25 -0
  55. mechdsl_core-0.2.1/src/mechdsl/lib/plasticity.py +214 -0
  56. mechdsl_core-0.2.1/src/mechdsl/lib/plasticity_kinematic.py +316 -0
  57. mechdsl_core-0.2.1/src/mechdsl/lib/plasticity_mixed.py +368 -0
  58. mechdsl_core-0.2.1/src/mechdsl/lib/tensor_ops.py +141 -0
  59. mechdsl_core-0.2.1/src/mechdsl/lowering/__init__.py +29 -0
  60. mechdsl_core-0.2.1/src/mechdsl/lowering/boundary.py +152 -0
  61. mechdsl_core-0.2.1/src/mechdsl/lowering/einsum_extract.py +255 -0
  62. mechdsl_core-0.2.1/src/mechdsl/lowering/fe_localise.py +411 -0
  63. mechdsl_core-0.2.1/src/mechdsl/solver/__init__.py +76 -0
  64. mechdsl_core-0.2.1/src/mechdsl/solver/_seam_runtime.py +67 -0
  65. mechdsl_core-0.2.1/src/mechdsl/solver/critical_timestep.py +300 -0
  66. mechdsl_core-0.2.1/src/mechdsl/solver/history_fields.py +116 -0
  67. mechdsl_core-0.2.1/src/mechdsl/solver/import_adapter.py +551 -0
  68. mechdsl_core-0.2.1/src/mechdsl/solver/integration.py +60 -0
  69. mechdsl_core-0.2.1/src/mechdsl/solver/jacobi_preconditioner.py +90 -0
  70. mechdsl_core-0.2.1/src/mechdsl/solver/load_stepping.py +143 -0
  71. mechdsl_core-0.2.1/src/mechdsl/solver/lumped_mass.py +139 -0
  72. mechdsl_core-0.2.1/src/mechdsl/solver/mesh_io.py +449 -0
  73. mechdsl_core-0.2.1/src/mechdsl/solver/newton.py +230 -0
  74. mechdsl_core-0.2.1/src/mechdsl/solver/seam_integrate.py +129 -0
  75. mechdsl_core-0.2.1/src/mechdsl/solver/seam_solve.py +173 -0
  76. mechdsl_core-0.2.1/src/mechdsl/symbolic/__init__.py +11 -0
  77. mechdsl_core-0.2.1/src/mechdsl/symbolic/anisotropic_energy.py +344 -0
  78. mechdsl_core-0.2.1/src/mechdsl/symbolic/bridge.py +234 -0
  79. mechdsl_core-0.2.1/src/mechdsl/symbolic/constitutive.py +97 -0
  80. mechdsl_core-0.2.1/src/mechdsl/symbolic/convected.py +586 -0
  81. mechdsl_core-0.2.1/src/mechdsl/symbolic/energy.py +492 -0
  82. mechdsl_core-0.2.1/src/mechdsl/symbolic/invariants.py +186 -0
  83. mechdsl_core-0.2.1/src/mechdsl/symbolic/kinematics.py +177 -0
  84. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/__init__.py +12 -0
  85. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/hgo.py +238 -0
  86. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/j2_power_law.py +441 -0
  87. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/johnson_cook.py +598 -0
  88. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/lemaitre.py +375 -0
  89. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/mooney_rivlin.py +214 -0
  90. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/neo_hookean.py +204 -0
  91. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/ogden.py +227 -0
  92. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/perzyna.py +443 -0
  93. mechdsl_core-0.2.1/src/mechdsl/symbolic/models/svk.py +142 -0
  94. mechdsl_core-0.2.1/src/mechdsl/symbolic/objective_rates.py +370 -0
  95. mechdsl_core-0.2.1/src/mechdsl/symbolic/spectral_energy.py +291 -0
  96. mechdsl_core-0.2.1/src/mechdsl/symbolic/voigt.py +166 -0
  97. mechdsl_core-0.2.1/src/mechdsl/verify/__init__.py +1 -0
  98. mechdsl_core-0.2.1/src/mechdsl/verify/_assembly.py +278 -0
  99. mechdsl_core-0.2.1/src/mechdsl/verify/_patch_test_kernels.py +185 -0
  100. mechdsl_core-0.2.1/src/mechdsl/verify/ad_oracle.py +481 -0
  101. mechdsl_core-0.2.1/src/mechdsl/verify/analytical.py +284 -0
  102. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/__init__.py +101 -0
  103. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/_core.py +143 -0
  104. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/_elastic_solver.py +226 -0
  105. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/_j2_solver.py +185 -0
  106. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/_meshes.py +398 -0
  107. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/_taylor_runtime.py +961 -0
  108. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/cantilever.py +164 -0
  109. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/cook_membrane.py +266 -0
  110. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/hgo_strip.py +448 -0
  111. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/necking_bar.py +402 -0
  112. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/notched_bar.py +589 -0
  113. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/plate_with_hole.py +587 -0
  114. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/taylor_impact.py +399 -0
  115. mechdsl_core-0.2.1/src/mechdsl/verify/benchmarks/thick_cylinder.py +629 -0
  116. mechdsl_core-0.2.1/src/mechdsl/verify/convergence.py +587 -0
  117. mechdsl_core-0.2.1/src/mechdsl/verify/mms_matrix.py +319 -0
  118. mechdsl_core-0.2.1/src/mechdsl/verify/patch_test.py +523 -0
  119. mechdsl_core-0.2.1/src/mechdsl/verify/perf/__init__.py +47 -0
  120. mechdsl_core-0.2.1/src/mechdsl/verify/perf/baseline.py +267 -0
  121. mechdsl_core-0.2.1/src/mechdsl/verify/perf/regenerate_baseline.py +62 -0
  122. mechdsl_core-0.2.1/src/mechdsl/verify/perf/registry.py +577 -0
  123. mechdsl_core-0.2.1/src/mechdsl/verify/perf/run_compare.py +119 -0
  124. mechdsl_core-0.2.1/tests/__init__.py +0 -0
  125. mechdsl_core-0.2.1/tests/_e2e_helpers.py +116 -0
  126. mechdsl_core-0.2.1/tests/_gen_cooks_ref.py +72 -0
  127. mechdsl_core-0.2.1/tests/conftest.py +21 -0
  128. mechdsl_core-0.2.1/tests/generate_golden.py +522 -0
  129. mechdsl_core-0.2.1/tests/golden/.gitkeep +0 -0
  130. mechdsl_core-0.2.1/tests/golden/boundary_neumann.ti.txt +26 -0
  131. mechdsl_core-0.2.1/tests/golden/generated_elastic.py.golden +683 -0
  132. mechdsl_core-0.2.1/tests/golden/generated_plastic.py.golden +912 -0
  133. mechdsl_core-0.2.1/tests/golden/generated_ul_svk.py.golden +570 -0
  134. mechdsl_core-0.2.1/tests/golden/perf/README.md +60 -0
  135. mechdsl_core-0.2.1/tests/golden/perf/baseline_smoke.json +59 -0
  136. mechdsl_core-0.2.1/tests/golden/template_family_emission_baseline.json +88 -0
  137. mechdsl_core-0.2.1/tests/lawgen/__init__.py +0 -0
  138. mechdsl_core-0.2.1/tests/lawgen/fixtures/linear_min.yaml +10 -0
  139. mechdsl_core-0.2.1/tests/lawgen/test_budgets.py +311 -0
  140. mechdsl_core-0.2.1/tests/lawgen/test_carrier_emitter.py +235 -0
  141. mechdsl_core-0.2.1/tests/lawgen/test_cli.py +359 -0
  142. mechdsl_core-0.2.1/tests/lawgen/test_contracts.py +293 -0
  143. mechdsl_core-0.2.1/tests/lawgen/test_diagnostics.py +289 -0
  144. mechdsl_core-0.2.1/tests/lawgen/test_guard_injection.py +401 -0
  145. mechdsl_core-0.2.1/tests/lawgen/test_lowering_table.py +404 -0
  146. mechdsl_core-0.2.1/tests/lawgen/test_manifest.py +382 -0
  147. mechdsl_core-0.2.1/tests/lawgen/test_sympy_to_taichi.py +201 -0
  148. mechdsl_core-0.2.1/tests/lawgen/test_test_emitter.py +259 -0
  149. mechdsl_core-0.2.1/tests/ref/.gitkeep +0 -0
  150. mechdsl_core-0.2.1/tests/ref/__init__.py +0 -0
  151. mechdsl_core-0.2.1/tests/ref/ref_hex8_elastic.py +489 -0
  152. mechdsl_core-0.2.1/tests/ref/ref_hex8_hgo.py +263 -0
  153. mechdsl_core-0.2.1/tests/ref/ref_hex8_plastic.py +488 -0
  154. mechdsl_core-0.2.1/tests/ref/ref_hex8_ul.py +450 -0
  155. mechdsl_core-0.2.1/tests/ref/ref_j2_kinematic.py +204 -0
  156. mechdsl_core-0.2.1/tests/ref/ref_j2_mixed.py +252 -0
  157. mechdsl_core-0.2.1/tests/test_ad_oracle.py +265 -0
  158. mechdsl_core-0.2.1/tests/test_analytical.py +650 -0
  159. mechdsl_core-0.2.1/tests/test_anisotropic_emission.py +125 -0
  160. mechdsl_core-0.2.1/tests/test_artifact_bundle.py +251 -0
  161. mechdsl_core-0.2.1/tests/test_artifacts.py +498 -0
  162. mechdsl_core-0.2.1/tests/test_benchmarks.py +1079 -0
  163. mechdsl_core-0.2.1/tests/test_benchmarks_cantilever_matrix.py +93 -0
  164. mechdsl_core-0.2.1/tests/test_benchmarks_cook_membrane_matrix.py +101 -0
  165. mechdsl_core-0.2.1/tests/test_benchmarks_necking_bar_matrix.py +196 -0
  166. mechdsl_core-0.2.1/tests/test_boundaries.py +1 -0
  167. mechdsl_core-0.2.1/tests/test_boundary_codegen.py +406 -0
  168. mechdsl_core-0.2.1/tests/test_boundary_neumann.py +103 -0
  169. mechdsl_core-0.2.1/tests/test_ci_config.py +153 -0
  170. mechdsl_core-0.2.1/tests/test_codegen.py +470 -0
  171. mechdsl_core-0.2.1/tests/test_compile_latex_docstring.py +62 -0
  172. mechdsl_core-0.2.1/tests/test_compile_pipeline.py +131 -0
  173. mechdsl_core-0.2.1/tests/test_constitutive_abc.py +271 -0
  174. mechdsl_core-0.2.1/tests/test_convected.py +159 -0
  175. mechdsl_core-0.2.1/tests/test_convected_curvilinear.py +557 -0
  176. mechdsl_core-0.2.1/tests/test_convected_patch.py +265 -0
  177. mechdsl_core-0.2.1/tests/test_convergence.py +533 -0
  178. mechdsl_core-0.2.1/tests/test_critical_timestep.py +219 -0
  179. mechdsl_core-0.2.1/tests/test_cross_backend.py +850 -0
  180. mechdsl_core-0.2.1/tests/test_documentation.py +346 -0
  181. mechdsl_core-0.2.1/tests/test_e2e.py +367 -0
  182. mechdsl_core-0.2.1/tests/test_e2e_plastic.py +736 -0
  183. mechdsl_core-0.2.1/tests/test_e2e_taichi.py +321 -0
  184. mechdsl_core-0.2.1/tests/test_einsum.py +433 -0
  185. mechdsl_core-0.2.1/tests/test_einsum_extract.py +108 -0
  186. mechdsl_core-0.2.1/tests/test_einsum_optimizer.py +310 -0
  187. mechdsl_core-0.2.1/tests/test_element_factory.py +190 -0
  188. mechdsl_core-0.2.1/tests/test_element_ir.py +308 -0
  189. mechdsl_core-0.2.1/tests/test_emission_verification.py +787 -0
  190. mechdsl_core-0.2.1/tests/test_emit_lame_conversion.py +152 -0
  191. mechdsl_core-0.2.1/tests/test_energy_codegen_svk.py +110 -0
  192. mechdsl_core-0.2.1/tests/test_energy_svk.py +103 -0
  193. mechdsl_core-0.2.1/tests/test_explicit_dynamics_acceptance.py +544 -0
  194. mechdsl_core-0.2.1/tests/test_explicit_integrator.py +208 -0
  195. mechdsl_core-0.2.1/tests/test_formulation_switching.py +181 -0
  196. mechdsl_core-0.2.1/tests/test_frontend.py +1 -0
  197. mechdsl_core-0.2.1/tests/test_frontend_build_context.py +320 -0
  198. mechdsl_core-0.2.1/tests/test_frontend_parser.py +713 -0
  199. mechdsl_core-0.2.1/tests/test_full_pipeline.py +317 -0
  200. mechdsl_core-0.2.1/tests/test_hex20_basis.py +247 -0
  201. mechdsl_core-0.2.1/tests/test_hex8_reduced.py +172 -0
  202. mechdsl_core-0.2.1/tests/test_hex8_tables.py +275 -0
  203. mechdsl_core-0.2.1/tests/test_hgo.py +195 -0
  204. mechdsl_core-0.2.1/tests/test_hgo_benchmark.py +225 -0
  205. mechdsl_core-0.2.1/tests/test_hgo_solver_e2e.py +205 -0
  206. mechdsl_core-0.2.1/tests/test_history_fields.py +276 -0
  207. mechdsl_core-0.2.1/tests/test_hourglass_control.py +217 -0
  208. mechdsl_core-0.2.1/tests/test_hourglass_suppression.py +123 -0
  209. mechdsl_core-0.2.1/tests/test_hyperelastic_uniaxial.py +198 -0
  210. mechdsl_core-0.2.1/tests/test_integration_surface.py +754 -0
  211. mechdsl_core-0.2.1/tests/test_invariants.py +84 -0
  212. mechdsl_core-0.2.1/tests/test_j2.py +420 -0
  213. mechdsl_core-0.2.1/tests/test_j2_radial_return_parity.py +149 -0
  214. mechdsl_core-0.2.1/tests/test_johnson_cook.py +588 -0
  215. mechdsl_core-0.2.1/tests/test_kinematics.py +235 -0
  216. mechdsl_core-0.2.1/tests/test_kinematics_ul.py +143 -0
  217. mechdsl_core-0.2.1/tests/test_lemaitre_acceptance.py +975 -0
  218. mechdsl_core-0.2.1/tests/test_lemaitre_codegen.py +226 -0
  219. mechdsl_core-0.2.1/tests/test_lemaitre_evolution.py +309 -0
  220. mechdsl_core-0.2.1/tests/test_load_stepping.py +286 -0
  221. mechdsl_core-0.2.1/tests/test_localise.py +354 -0
  222. mechdsl_core-0.2.1/tests/test_localise_model_validation.py +72 -0
  223. mechdsl_core-0.2.1/tests/test_mechanics_ir.py +321 -0
  224. mechdsl_core-0.2.1/tests/test_mechanics_ir_configuration.py +223 -0
  225. mechdsl_core-0.2.1/tests/test_mesh_io.py +721 -0
  226. mechdsl_core-0.2.1/tests/test_metric_assign_directives.py +116 -0
  227. mechdsl_core-0.2.1/tests/test_mfem_printer.py +332 -0
  228. mechdsl_core-0.2.1/tests/test_mms_convergence_matrix.py +93 -0
  229. mechdsl_core-0.2.1/tests/test_mooney_rivlin.py +205 -0
  230. mechdsl_core-0.2.1/tests/test_moose_printer.py +317 -0
  231. mechdsl_core-0.2.1/tests/test_neo_hookean.py +205 -0
  232. mechdsl_core-0.2.1/tests/test_newton.py +475 -0
  233. mechdsl_core-0.2.1/tests/test_notched_bar_benchmark.py +305 -0
  234. mechdsl_core-0.2.1/tests/test_nrpylatex_round_trip.py +162 -0
  235. mechdsl_core-0.2.1/tests/test_objective_rates.py +268 -0
  236. mechdsl_core-0.2.1/tests/test_ogden.py +191 -0
  237. mechdsl_core-0.2.1/tests/test_ogden_solver_e2e.py +203 -0
  238. mechdsl_core-0.2.1/tests/test_patch_test.py +403 -0
  239. mechdsl_core-0.2.1/tests/test_patch_test_all_elements.py +126 -0
  240. mechdsl_core-0.2.1/tests/test_perf_regression.py +396 -0
  241. mechdsl_core-0.2.1/tests/test_perzyna.py +444 -0
  242. mechdsl_core-0.2.1/tests/test_phase10_benchmark_registry.py +235 -0
  243. mechdsl_core-0.2.1/tests/test_phase10_elastic_solver.py +103 -0
  244. mechdsl_core-0.2.1/tests/test_phase10_j2_solver.py +127 -0
  245. mechdsl_core-0.2.1/tests/test_phase10_mesh_utils.py +92 -0
  246. mechdsl_core-0.2.1/tests/test_phase10_taylor_benchmark.py +180 -0
  247. mechdsl_core-0.2.1/tests/test_phase10_taylor_runtime.py +360 -0
  248. mechdsl_core-0.2.1/tests/test_phase10_taylor_state.py +421 -0
  249. mechdsl_core-0.2.1/tests/test_plastic_emission.py +560 -0
  250. mechdsl_core-0.2.1/tests/test_plate_with_hole.py +59 -0
  251. mechdsl_core-0.2.1/tests/test_ref_elastic.py +582 -0
  252. mechdsl_core-0.2.1/tests/test_ref_plastic.py +580 -0
  253. mechdsl_core-0.2.1/tests/test_review_followups.py +116 -0
  254. mechdsl_core-0.2.1/tests/test_smoke.py +29 -0
  255. mechdsl_core-0.2.1/tests/test_solver.py +293 -0
  256. mechdsl_core-0.2.1/tests/test_spectral_eigensolver.py +169 -0
  257. mechdsl_core-0.2.1/tests/test_svk.py +249 -0
  258. mechdsl_core-0.2.1/tests/test_symbolic.py +1 -0
  259. mechdsl_core-0.2.1/tests/test_symbolic_ir_interface.py +311 -0
  260. mechdsl_core-0.2.1/tests/test_taichi_printer.py +440 -0
  261. mechdsl_core-0.2.1/tests/test_taichi_printer_ul.py +431 -0
  262. mechdsl_core-0.2.1/tests/test_taylor_impact.py +316 -0
  263. mechdsl_core-0.2.1/tests/test_template_family_budget.py +301 -0
  264. mechdsl_core-0.2.1/tests/test_tensor_ops.py +278 -0
  265. mechdsl_core-0.2.1/tests/test_tet10_basis.py +264 -0
  266. mechdsl_core-0.2.1/tests/test_tet4_basis.py +192 -0
  267. mechdsl_core-0.2.1/tests/test_thick_cylinder.py +108 -0
  268. mechdsl_core-0.2.1/tests/test_ul_equivalence.py +286 -0
  269. mechdsl_core-0.2.1/tests/test_verification_gaps_p5t2.py +582 -0
  270. mechdsl_core-0.2.1/tests/test_verification_gaps_p5t3.py +122 -0
  271. mechdsl_core-0.2.1/tests/test_verify_assembly_cast.py +223 -0
  272. mechdsl_core-0.2.1/tests/test_viscoplastic_acceptance.py +235 -0
  273. mechdsl_core-0.2.1/tests/test_voigt.py +253 -0
  274. mechdsl_core-0.2.1/tests/tools/__init__.py +1 -0
  275. mechdsl_core-0.2.1/tests/tools/regen_p9_3_baseline.py +151 -0
@@ -0,0 +1,75 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+ *.egg
7
+ dist/
8
+ build/
9
+ *.whl
10
+ .eggs/
11
+
12
+ # Virtual environments
13
+ .venv/
14
+ venv/
15
+ env/
16
+
17
+ # IDE
18
+ .idea/
19
+ .vscode/
20
+ *.swp
21
+ *.swo
22
+ *~
23
+
24
+ # Testing
25
+ .pytest_cache/
26
+ .coverage
27
+ htmlcov/
28
+ .mypy_cache/
29
+ .ruff_cache/
30
+
31
+ # OS
32
+ .DS_Store
33
+ Thumbs.db
34
+
35
+ # Taichi
36
+ *.tcb
37
+ ti_cache/
38
+
39
+ # Artifacts
40
+ *.artifact.json
41
+ *.artifact.yaml
42
+
43
+ # Jupyter
44
+ .ipynb_checkpoints/
45
+
46
+ # Claude Code
47
+ .claude/settings.local.json
48
+
49
+ # Claude CoWork
50
+ /MechDsl/
51
+ *.npz
52
+ # Golden-file regression snapshots must be tracked
53
+ !packages/mechdsl-core/tests/golden/*.npz
54
+ .gitnexus
55
+
56
+ # Local working artifacts (orchestra runtime, plan-edit backups)
57
+ .orchestra/
58
+ **/.orchestra/
59
+ *.original.md
60
+ *.original.[0-9].md
61
+ .claude/worktrees/
62
+ .publisher-worktrees/
63
+ .sesskey
64
+
65
+ # Public-release staging tree (curated copy pushed to CEmM2/MechDSL)
66
+ /dev/MechDSL/
67
+
68
+ # MkDocs build output
69
+ /site/
70
+
71
+ # Generated wiki / code-intelligence index (logic-loom / akms tooling)
72
+ .repo_wiki/
73
+
74
+ # comment-sweep working tree
75
+ /.comment-review/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shmuel Osovski
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ Metadata-Version: 2.4
2
+ Name: mechdsl-core
3
+ Version: 0.2.1
4
+ Summary: MechDSL core — LaTeX tensor expressions to FEM solver code
5
+ Project-URL: Repository, https://github.com/CEmM2/MechDSL
6
+ Author: Shmuel Osovski
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: code-generation,computational-mechanics,dsl,fem,finite-elements,taichi
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Scientific/Engineering :: Physics
16
+ Requires-Python: <3.14,>=3.11
17
+ Requires-Dist: nrpylatex<2,>=1.4.0
18
+ Requires-Dist: numpy>=2.4.2
19
+ Requires-Dist: opt-einsum>=3.3
20
+ Requires-Dist: pyyaml>=6.0.3
21
+ Requires-Dist: scipy>=1.17.0
22
+ Requires-Dist: sympy>=1.12
23
+ Provides-Extra: verify
24
+ Requires-Dist: algo2code<0.3,>=0.2.1; extra == 'verify'
25
+ Requires-Dist: taichi>=1.7; extra == 'verify'
26
+ Requires-Dist: ti-runtime<0.3,>=0.2.1; extra == 'verify'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # mechdsl-core
30
+
31
+ Core compiler package for MechDSL — LaTeX tensor expressions to FEM solver code.
32
+
33
+ Handles constitutive equations, element-level physics (Einstein notation), kinematics,
34
+ and Taichi code generation through the Mechanics IR → Element IR → Einsum IR pipeline.
35
+
36
+ ## Documentation
37
+
38
+ - **User docs:** <https://sosovski.group/MechDSL/mechdsl-core/> — introduction, getting
39
+ started, the LaTeX directive reference, the constitutive-model catalog, and examples.
40
+ - **Design specs:** `dev/design_docs/` (authoritative source of truth).
41
+ - See the [monorepo root](../../README.md) for the full project overview.
@@ -0,0 +1,13 @@
1
+ # mechdsl-core
2
+
3
+ Core compiler package for MechDSL — LaTeX tensor expressions to FEM solver code.
4
+
5
+ Handles constitutive equations, element-level physics (Einstein notation), kinematics,
6
+ and Taichi code generation through the Mechanics IR → Element IR → Einsum IR pipeline.
7
+
8
+ ## Documentation
9
+
10
+ - **User docs:** <https://sosovski.group/MechDSL/mechdsl-core/> — introduction, getting
11
+ started, the LaTeX directive reference, the constitutive-model catalog, and examples.
12
+ - **Design specs:** `dev/design_docs/` (authoritative source of truth).
13
+ - See the [monorepo root](../../README.md) for the full project overview.
@@ -0,0 +1,71 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "mechdsl-core"
7
+ version = "0.2.1"
8
+ description = "MechDSL core — LaTeX tensor expressions to FEM solver code"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.11,<3.14"
12
+ authors = [
13
+ { name = "Shmuel Osovski" },
14
+ ]
15
+ keywords = ["fem", "finite-elements", "computational-mechanics", "dsl", "taichi", "code-generation"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Scientific/Engineering :: Physics",
23
+ ]
24
+ dependencies = [
25
+ "sympy>=1.12",
26
+ "numpy>=2.4.2",
27
+ "opt-einsum>=3.3",
28
+ "pyyaml>=6.0.3",
29
+ "scipy>=1.17.0",
30
+ "nrpylatex>=1.4.0,<2",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ # Taichi is the runtime backend — imported *only* on the verify() / run path
35
+ # (the emit, transpile, capabilities and model_catalog surfaces are proven
36
+ # Taichi-free; see tests/test_integration_surface.py::*taichi_free*). It lives
37
+ # in the `verify` extra so downstream consumers (compmech_reference_pack,
38
+ # akms_learn, the Logic-Loom sidecar) can install mechdsl-core for code
39
+ # emission without pulling Taichi's ~170 MB into their closure. Install
40
+ # `mechdsl-core[verify]` to run verification / solves. opt-einsum stays a base
41
+ # dep — it is a 0.4 MB pure-Python compile-time requirement of the emit path.
42
+ #
43
+ # The generated matrix-free SVK tangent @ti.kernel emits
44
+ # `from ti_runtime import ...` (Tier-1 tensor helpers + hex8 shape gradients +
45
+ # the apply_A injection seam) — the mechdsl-core -> ti-runtime production
46
+ # dependency edge. ti-runtime is Taichi-bearing, so it rides in the `verify`
47
+ # extra alongside taichi; the base install (emit / transpile / capabilities)
48
+ # stays Taichi-free. The generated matrix-free PCG solver is transpiled via
49
+ # `algo2code` in runtime mode and injected through the ti_runtime `set_solver`
50
+ # seam; algo2code is a stdlib-only compile-time tool and rides in the same
51
+ # extra. `verify` is the full install (~1 GB closure via Taichi) — everything
52
+ # needed to run and verify solves, not just emit code.
53
+ # NOTE: the AD oracle uses central finite differences (mechdsl.verify.ad_oracle)
54
+ # — torch was never imported anywhere and is deliberately NOT a dependency.
55
+ verify = ["taichi>=1.7", "ti-runtime>=0.2.1,<0.3", "algo2code>=0.2.1,<0.3"]
56
+
57
+ [project.scripts]
58
+ mechdsl-lawgen = "mechdsl.lawgen.cli:main"
59
+
60
+ [tool.uv.sources]
61
+ ti-runtime = { workspace = true }
62
+ algo2code = { workspace = true }
63
+
64
+ [project.urls]
65
+ Repository = "https://github.com/CEmM2/MechDSL"
66
+
67
+ [tool.hatch.metadata]
68
+ allow-direct-references = true
69
+
70
+ [tool.hatch.build.targets.wheel]
71
+ packages = ["src/mechdsl"]
@@ -0,0 +1,263 @@
1
+ """MechDSL core — LaTeX tensor expressions to FEM solver code (Taichi).
2
+
3
+ The canonical MVP-stable entry point is :func:`compile_latex`, which accepts
4
+ a LaTeX source string and produces an :class:`~mechdsl.codegen.artifact.ArtifactBundle`.
5
+ The lower-level :func:`compile` is preserved as a programmatic API that
6
+ takes a pre-built :class:`~mechdsl.ir.mechanics_ir.ProblemIR`.
7
+
8
+ See ``README.md`` Support tiers and
9
+ ``dev/plans/recovery_plan_latex_contract.md`` Phase 2 (R1) for the recovery
10
+ context.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from typing import TYPE_CHECKING
16
+
17
+ from mechdsl import integration as integration
18
+ from mechdsl.codegen import compile
19
+
20
+ if TYPE_CHECKING:
21
+ from os import PathLike
22
+
23
+ from mechdsl.codegen.artifact import ArtifactBundle
24
+
25
+ __version__ = "0.2.1"
26
+
27
+ # Allowed values for the `profile` argument of :func:`compile_latex`. Module-
28
+ # level so callers can introspect the supported set instead of guessing from
29
+ # the docstring. Add new profiles by extending this set explicitly — never
30
+ # silently relax the gate.
31
+ ALLOWED_PROFILES: frozenset[str] = frozenset({"mvp"})
32
+
33
+ __all__ = ["ALLOWED_PROFILES", "compile", "compile_latex", "integration"]
34
+
35
+ # Authored-invariant markers that select a non-default constitutive derivation
36
+ # path. Principal stretches (lbar1/2/3) belong to the spectral (Ogden) engine;
37
+ # the fiber pseudo-invariant (Ibar4) belongs to the anisotropic (HGO) engine.
38
+ _SPECTRAL_STRETCH = ("lbar1", "lbar2", "lbar3")
39
+ _FIBER_INVARIANT = "Ibar4"
40
+
41
+
42
+ def _derive_constitutive_energy(energy_source: str):
43
+ """Derive the constitutive model, routing by the authored invariants.
44
+
45
+ The free symbols of the parsed energy decide the path (robust to comments,
46
+ unlike a raw substring scan): a fiber pseudo-invariant ``Ibar4`` -> the
47
+ anisotropic (HGO) engine; principal stretches ``lbar1/2/3`` -> the spectral
48
+ (Ogden) engine; otherwise the named-invariant engine. Each engine
49
+ re-validates and rejects symbols outside its own contract.
50
+ """
51
+ from mechdsl.symbolic.energy import parse_energy_scalar_sympy
52
+
53
+ psi, _ = parse_energy_scalar_sympy(energy_source)
54
+ names = {s.name for s in psi.free_symbols}
55
+ if _FIBER_INVARIANT in names:
56
+ from mechdsl.symbolic.anisotropic_energy import derive_from_anisotropic_energy
57
+
58
+ return derive_from_anisotropic_energy(energy_source)
59
+ if any(s in names for s in _SPECTRAL_STRETCH):
60
+ from mechdsl.symbolic.spectral_energy import derive_from_spectral_energy
61
+
62
+ return derive_from_spectral_energy(energy_source)
63
+ from mechdsl.symbolic.energy import derive_from_energy
64
+
65
+ return derive_from_energy(energy_source)
66
+
67
+
68
+ def compile_latex(
69
+ source: str,
70
+ profile: str = "mvp",
71
+ *,
72
+ energy_source: str | None = None,
73
+ energy_file: str | PathLike[str] | None = None,
74
+ ) -> ArtifactBundle:
75
+ """Compile a LaTeX-source mechanics problem to an artifact bundle.
76
+
77
+ This is the canonical MVP-stable entry point. The function:
78
+
79
+ 1. Parses ``source`` through
80
+ :func:`mechdsl.frontend.parse_compile_context`, the math-aware
81
+ frontend entry point that preserves directive-only behaviour while
82
+ rejecting unsupported math-bearing sources before IR construction.
83
+ 2. Adapts the resulting context dict to a :class:`ProblemIR` via
84
+ :meth:`ProblemIR.from_latex_semantics` (fgram P6-1). This is the
85
+ LaTeX-semantic constructor: its MVP-stable *core* (dim, formulation,
86
+ element type, material, boundaries) is identical to
87
+ :meth:`ProblemIR.from_context`, so the emitted Taichi code is
88
+ byte-for-byte unchanged. The constructor additionally attaches a
89
+ serializable :class:`~mechdsl.ir.mechanics_ir.LatexSemantics` record
90
+ to the IR (declared fields, directive-tagged constitutive
91
+ ``(symbol, role)`` pairs, the weak-form label, and any ``$...$``
92
+ equation roles). That record rides into the artifact bundle's
93
+ ``problem_ir_dict["latex_semantics"]`` so emitted code sections are
94
+ traceable back to their source equation roles for review and golden
95
+ diffing — the codegen contract itself stays the typed IR fields
96
+ (``material`` / ``residual_contract``), not the advisory record.
97
+ 3. Forwards to :func:`compile` (the existing programmatic façade)
98
+ to run localisation, einsum planning, and Taichi emission.
99
+
100
+ **Backend stability:** Taichi is the only MVP-stable backend on the
101
+ canonical LaTeX compile path. All code produced by this function
102
+ targets the Taichi runtime. MFEM and MOOSE are experimental backends
103
+ preserved in the tree (``mechdsl.codegen.mfem_printer`` and
104
+ ``mechdsl.codegen.moose_printer``) but they are not reachable through
105
+ this façade and are not part of the stable contract. Reaching those
106
+ surfaces requires calling the experimental printers directly; see
107
+ ``README.md`` Support tiers and
108
+ ``dev/plans/recovery_plan_latex_contract.md`` Phase 5 (R4).
109
+
110
+ Parameters
111
+ ----------
112
+ source:
113
+ LaTeX source text containing ``% mechanics`` directives.
114
+ profile:
115
+ Compile profile selector. Only ``"mvp"`` is currently accepted;
116
+ other profiles (e.g. extended LaTeX math grammar via NRPyLaTeX,
117
+ non-canonical backends) are deferred to later recovery phases.
118
+ energy_source:
119
+ Optional self-contained nrpylatex strain-energy block (``% declare``
120
+ metric + ``EDD`` strain, then the scalar energy assignment). When
121
+ provided, it is run
122
+ through :func:`mechdsl.symbolic.energy.derive_from_energy` and the
123
+ resulting symbolic PK2 stress + material tangent are attached to the
124
+ :class:`ProblemIR` as ``derived_energy``. The generated solver then
125
+ emits its constitutive law and consistent tangent from the derived
126
+ energy (parameterised on the energy's own material-parameter names),
127
+ instead of the named-model ``(lam, mu)`` dispatch. The problem
128
+ ``source``'s ``MaterialSpec`` params must supply a value for every
129
+ parameter the energy uses.
130
+ energy_file:
131
+ Path to a ``.tex`` file holding the same energy block as
132
+ ``energy_source``. Mutually exclusive with ``energy_source``; the file
133
+ is read as UTF-8.
134
+
135
+ Boundary conditions
136
+ -------------------
137
+ LaTeX ``% mechanics boundary`` directives populate
138
+ :class:`mechdsl.ir.mechanics_ir.BoundaryCondition` slots on the
139
+ resulting :class:`ProblemIR`. Two boundary kinds are supported:
140
+
141
+ - **Dirichlet** — fixed displacement components, applied at solve
142
+ time by the runtime adapter.
143
+ - **Neumann** — surface tractions. When the directive carries a
144
+ numeric traction triple, post_recovery_plan P1-5 emits an
145
+ ``f_ext`` initialisation kernel (see ``f_ext_kernel`` in
146
+ *Returns* below) and the bundle is self-contained for that BC.
147
+ Neumann BCs with a symbolic-string traction leave the kernel
148
+ slot empty.
149
+
150
+ Numeric ``f_ext`` provisioning is the **caller's responsibility**
151
+ when no kernel is emitted (Dirichlet-only problems or
152
+ symbolic-traction Neumann): the caller supplies the global
153
+ external-force vector to the solver. When ``f_ext_kernel`` is
154
+ present, the emitted kernel overrides any manual ``f_ext``
155
+ provisioning for the directive-driven contribution; the caller
156
+ is still responsible for compositing it with any additional body
157
+ forces. ``BoundaryCondition`` objects therefore document intent
158
+ at the IR level, while the artifact bundle decides whether the
159
+ contract is satisfied entirely by emitted code or jointly by
160
+ caller-provided ``f_ext``.
161
+
162
+ Returns
163
+ -------
164
+ ArtifactBundle
165
+ The same bundle :func:`compile` produces, ready for golden-file
166
+ comparison or Taichi emission. When the parsed problem contains
167
+ one or more Neumann BCs with numeric traction (post_recovery_plan
168
+ P1-5), the bundle's optional ``f_ext_kernel`` field carries the
169
+ emitted ``init_f_ext_from_neumann_<bc>`` kernel source. The kernel
170
+ signature is parametric in ``f_factor`` (= face_area /
171
+ n_face_nodes) so the runtime mesh adapter supplies the per-node
172
+ weighting at call time. Pure Dirichlet problems and Neumann BCs
173
+ with symbolic-string traction leave ``f_ext_kernel`` at ``None``;
174
+ existing residual / tangent fields and ``emitted_source`` shape
175
+ are unchanged.
176
+
177
+ Raises
178
+ ------
179
+ ValueError
180
+ When ``profile`` is anything other than ``"mvp"``.
181
+ mechdsl.frontend.parser.ParseError
182
+ When the LaTeX source has a malformed ``% mechanics`` directive.
183
+ mechdsl.frontend.FrontendSemanticError
184
+ When a math-bearing LaTeX source cannot produce supported frontend
185
+ math semantics before IR construction.
186
+ mechdsl.symbolic.convected.UnsupportedError
187
+ When the parsed context falls outside the MVP-supported subset.
188
+
189
+ """
190
+ if profile not in ALLOWED_PROFILES:
191
+ raise ValueError(
192
+ f"compile_latex profile={profile!r} is not supported. "
193
+ f"Allowed profiles: {sorted(ALLOWED_PROFILES)}. "
194
+ "Broader profile support is planned for later recovery-plan "
195
+ "phases (see dev/plans/recovery_plan_latex_contract.md)."
196
+ )
197
+
198
+ if energy_source is not None and energy_file is not None:
199
+ raise ValueError(
200
+ "compile_latex accepts at most one of energy_source / energy_file, not both."
201
+ )
202
+ if energy_file is not None:
203
+ from pathlib import Path
204
+
205
+ energy_source = Path(energy_file).read_text(encoding="utf-8")
206
+
207
+ from mechdsl.frontend import parse_compile_context
208
+
209
+ ctx = parse_compile_context(source)
210
+
211
+ import dataclasses
212
+
213
+ from mechdsl.codegen.taichi_printer import (
214
+ EmissionContext,
215
+ emit_neumann_f_ext_kernel_for_ir,
216
+ )
217
+ from mechdsl.ir.mechanics_ir import BCType, ProblemIR
218
+
219
+ # Build the IR through the LaTeX-semantic constructor so the emitted
220
+ # bundle carries the source-role metadata (latex_semantics) that links
221
+ # generated code sections back to source equation roles. The MVP-stable
222
+ # core is identical to from_context, so emitted Taichi is unchanged;
223
+ # only the additive latex_semantics record is new.
224
+ problem_ir = ProblemIR.from_latex_semantics(ctx)
225
+ # When a strain-energy block was supplied, derive its symbolic stress and
226
+ # attach it to the IR. The codegen layer then emits the constitutive law and
227
+ # consistent tangent from the derived energy (taichi_printer + the matching
228
+ # emitter), parameterised on the energy's own material-parameter names rather
229
+ # than the named-model (lam, mu) dispatch. The derivation path is selected
230
+ # from the authored invariants: principal stretches (Ogden) -> spectral,
231
+ # a fiber pseudo-invariant (HGO) -> anisotropic, else the named-invariant path.
232
+ if energy_source is not None:
233
+ problem_ir = dataclasses.replace(
234
+ problem_ir, derived_energy=_derive_constitutive_energy(energy_source)
235
+ )
236
+ # Enforce the MVP-stable contract at the canonical compile-path
237
+ # boundary so users hitting an experimental combination or a missing
238
+ # required-param see a clean IR-level rejection instead of a deep
239
+ # codegen / runtime failure.
240
+ problem_ir.assert_mvp_stable()
241
+ bundle = compile(problem_ir)
242
+
243
+ # Surface a Neumann ``f_ext`` initialisation kernel for every Neumann
244
+ # BC carrying numeric traction. Symbolic (string) traction stays
245
+ # handled by the legacy imported numeric injection path so existing
246
+ # callers keep working unchanged. Pure Dirichlet problems leave
247
+ # ``f_ext_kernel`` at ``None``.
248
+ neumann_bcs = [
249
+ bc
250
+ for bc in problem_ir.boundaries
251
+ if bc.bc_type == BCType.NEUMANN and isinstance(bc.traction, tuple)
252
+ ]
253
+ if not neumann_bcs:
254
+ return bundle
255
+ em_ctx = EmissionContext()
256
+ for bc in neumann_bcs:
257
+ emit_neumann_f_ext_kernel_for_ir(
258
+ em_ctx,
259
+ bc_name=bc.name,
260
+ surface_tag=bc.effective_surface_tag,
261
+ traction=bc.traction, # type: ignore[arg-type]
262
+ )
263
+ return dataclasses.replace(bundle, f_ext_kernel=em_ctx.get_source())
@@ -0,0 +1,90 @@
1
+ """Einsum optimizer, Taichi code generation, boundary condition codegen.
2
+
3
+ Backend support tiers
4
+ ---------------------
5
+ The MVP-stable canonical backend is :mod:`mechdsl.codegen.taichi_printer`.
6
+ :mod:`mechdsl.codegen.mfem_printer` and :mod:`mechdsl.codegen.moose_printer`
7
+ are preserved in-tree as **experimental** surfaces (see
8
+ ``dev/plans/recovery_plan_latex_contract.md`` Phase 5 (R4)).
9
+
10
+ The ``__experimental__`` convention
11
+ -----------------------------------
12
+ Every experimental printer module exposes a module-level constant::
13
+
14
+ __experimental__: bool = True
15
+
16
+ so callers and tests can detect experimental status programmatically::
17
+
18
+ from mechdsl.codegen import mfem_printer
19
+ assert mfem_printer.__experimental__ is True
20
+
21
+ Stable backends do **not** set this flag. On the first call to its public
22
+ ``emit`` function, each experimental printer additionally raises a single
23
+ :class:`mechdsl.codegen._experimental.ExperimentalBackendWarning` that can
24
+ be filtered with the standard :mod:`warnings` machinery.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from typing import TYPE_CHECKING
30
+
31
+ from mechdsl.codegen._experimental import ExperimentalBackendWarning
32
+ from mechdsl.codegen.taichi_printer import TaichiCodegenFacade
33
+
34
+ if TYPE_CHECKING:
35
+ from mechdsl.codegen.artifact import ArtifactBundle
36
+ from mechdsl.ir.mechanics_ir import ProblemIR
37
+
38
+ __all__ = ["ExperimentalBackendWarning", "TaichiCodegenFacade", "compile"]
39
+
40
+
41
+ def compile(problem_ir: ProblemIR) -> ArtifactBundle:
42
+ """Compile a ProblemIR into a self-contained Taichi solver.
43
+
44
+ Runs the full pipeline: localise → optimise → emit.
45
+
46
+ **Backend stability:** Taichi is the only MVP-stable backend on the
47
+ canonical LaTeX compile path. MFEM and MOOSE printers exist in the
48
+ tree as experimental surfaces (see ``mechdsl.codegen.mfem_printer``
49
+ and ``mechdsl.codegen.moose_printer``) but they are not part of the
50
+ stable contract and may not be supported in future recovery phases
51
+ without explicit roadmap work (Plan B §B8).
52
+
53
+ Parameters
54
+ ----------
55
+ problem_ir : ProblemIR
56
+ The semantic problem specification.
57
+
58
+ Returns
59
+ -------
60
+ ArtifactBundle
61
+ Bundle with ``emitted_source`` populated. Contains all
62
+ intermediate products (IRs, contraction plans).
63
+
64
+ Raises
65
+ ------
66
+ ValueError
67
+ Propagated when the IR contains an unsupported formulation,
68
+ element type, or material model for the MVP pipeline.
69
+ BudgetExceededError
70
+ Propagated when einsum optimisation exceeds the absolute JIT
71
+ budget ceiling.
72
+ """
73
+ from mechdsl.codegen.artifact import ArtifactBundle as _AB
74
+ from mechdsl.codegen.taichi_printer import emit
75
+ from mechdsl.lowering.fe_localise import localise_and_optimize
76
+
77
+ loc_result, plans = localise_and_optimize(problem_ir)
78
+ bundle = _AB.from_pipeline(problem_ir, loc_result, plans)
79
+ source = emit(bundle)
80
+
81
+ return _AB(
82
+ problem_ir_dict=bundle.problem_ir_dict,
83
+ element_ir_summary=bundle.element_ir_summary,
84
+ contraction_plans=bundle.contraction_plans,
85
+ emitted_source=source,
86
+ metadata=bundle.metadata,
87
+ # Preserve the derived-energy Python-object channel through the
88
+ # bundle rebuild so a LaTeX-derived constitutive law reaches codegen.
89
+ derived_energy=bundle.derived_energy,
90
+ )
@@ -0,0 +1,71 @@
1
+ """Experimental-backend infrastructure for mechdsl.codegen.
2
+
3
+ Defines the shared :class:`ExperimentalBackendWarning` that each non-stable
4
+ printer emits on first use, and documents the ``__experimental__`` flag
5
+ convention used across the codegen package.
6
+
7
+ Convention
8
+ ----------
9
+ Every experimental backend module exposes a module-level constant::
10
+
11
+ __experimental__: bool = True
12
+
13
+ This allows tooling and tests to detect experimental status
14
+ programmatically (e.g. ``import mechdsl.codegen.mfem_printer as m;
15
+ assert m.__experimental__``) rather than parsing docstrings. Stable
16
+ backends (currently only :mod:`mechdsl.codegen.taichi_printer`) do **not**
17
+ set this flag.
18
+
19
+ At runtime, each experimental printer emits one
20
+ :class:`ExperimentalBackendWarning` per Python session on the first call to
21
+ its public ``emit`` function. The warning is filterable with the standard
22
+ :mod:`warnings` machinery::
23
+
24
+ import warnings
25
+ warnings.filterwarnings("ignore", category=ExperimentalBackendWarning)
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import warnings
31
+
32
+ __all__ = ["ExperimentalBackendWarning", "warn_experimental_backend_once"]
33
+
34
+
35
+ class ExperimentalBackendWarning(UserWarning):
36
+ """Warning raised on the first call to an experimental codegen backend.
37
+
38
+ Experimental backends (MFEM, MOOSE) are preserved in-tree but are not
39
+ part of the MVP stable contract. This warning signals that the caller
40
+ is using a surface outside the stable Taichi compile path.
41
+
42
+ Filter with::
43
+
44
+ import warnings
45
+ warnings.filterwarnings("ignore", category=ExperimentalBackendWarning)
46
+ """
47
+
48
+
49
+ def warn_experimental_backend_once(state: dict, backend_name: str) -> None:
50
+ """Emit an ExperimentalBackendWarning the first time this is called for the given state.
51
+
52
+ Idempotent: subsequent calls with the same state dict are no-ops.
53
+
54
+ Parameters
55
+ ----------
56
+ state : dict
57
+ Module-level dict tracking warning state. Caller passes a single shared
58
+ dict (e.g. ``_warn_state``); this function flips ``state["warned"]`` to True.
59
+ backend_name : str
60
+ Human-readable backend name (e.g. "MFEM", "MOOSE") for the warning message.
61
+ """
62
+ if state.get("warned"):
63
+ return
64
+ warnings.warn(
65
+ f"{backend_name} backend is experimental — its API may change without "
66
+ f"deprecation notice. Suppress with "
67
+ f"warnings.filterwarnings('ignore', category=ExperimentalBackendWarning).",
68
+ ExperimentalBackendWarning,
69
+ stacklevel=3,
70
+ )
71
+ state["warned"] = True