Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- gitnexus:start -->
# GitNexus — Code Intelligence

This project is indexed by GitNexus as **MechDSL** (14707 symbols, 30253 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
This project is indexed by GitNexus as **MechDSL** (15661 symbols, 32507 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.

> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.

Expand Down
139 changes: 139 additions & 0 deletions RELEASE_ORDER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Release Order — MechDSL law compile → NumerixWeave consumption

This is the operational runbook for shipping a `mechdsl-lawgen`-emitted
constitutive law from **MechDSL** (this repo) into **NumerixWeave**
(`ticonstit.generated`). It is the P4-3 deliverable of MFront-mimic Cycle M0
(`dev/plans/mfront_cycleM0.md`, R1).

## Why this doc exists (R1)

`NumerixWeave/tools/check_dependency_graph.py` walks the **workspace** import
graph (`libs/`, `apps/`, `bundles/`) to enforce that `ticonstit` never gains a
runtime dependency on MechDSL or SymPy. It cannot see — and is not meant to
see — the **cross-repo build edge**: a MechDSL CLI process reading a YAML law
spec and writing generated Python files into a NumerixWeave checkout is a
build-time / process dependency, not a Python import, so it never appears as
an edge in either repo's dependency graph.

That invisible edge is real, though: NumerixWeave's `ticonstit.generated`
package depends on MechDSL having produced specific, byte-stable files at
specific paths. The seam that keeps this safe is:

1. **Committed artifacts** — the generated files are checked into
NumerixWeave, not built at NumerixWeave's install/CI time. NumerixWeave
never invokes MechDSL as part of its own build.
2. **`source_hash`** — `_manifest.json` pins the SHA-256 of the canonical
input formula string for each law, so drift between the committed artifact
and its MechDSL source is detectable without re-running the compiler.
3. **This documented order** — the three steps below, always run in this
sequence, from the correct venv in each repo.

**MechDSL must never become a NumerixWeave runtime dependency.** The
generated Python under `ticonstit/generated/` imports only Taichi and the
Python standard library — never `mechdsl`, never `sympy`. MechDSL only ever
appears on the NumerixWeave side as an optional, path-pinned **subprocess**
invoked from tests (see `libs/ticonstit/tests/generated/test_swift_voce_equivalence.py`
in NumerixWeave), which is exempt from the runtime-import ban by construction
— it never imports MechDSL/SymPy into the NumerixWeave process.

## The 3-step release sequence

### Step 1 — Compile the law in MechDSL (MechDSL venv)

Run from the **MechDSL** repo root, using MechDSL's own `uv`-managed
environment (R3 — never run this from NumerixWeave's `.venv`):

```bash
cd /Users/shmuelosovski/Github/Personal/MechDSL
uv run mechdsl-lawgen compile laws/plasticity/swift_voce.yaml \
--target ticonstit \
--out /Users/shmuelosovski/Github/Personal/NumerixWeave/libs/ticonstit/src/ticonstit/generated/
```

Notes:

- `--out` points at the **generated-level** directory
(`libs/ticonstit/src/ticonstit/generated/`) — *not*
`.../generated/plasticity/`. The compiler creates/updates the
`plasticity/` subdirectory itself; pointing `--out` one level too deep
double-nests `plasticity/plasticity/`.
- This writes/updates three artifacts under that `--out` directory:
- `plasticity/swift_voce.py` — the generated Taichi carrier class.
- `_manifest.json` — the law registry entry, including `source_hash`.
- `tests/test_swift_voce.py` — a self-contained generated smoke test.
- Output is byte-stable: running this command twice against an unchanged
`swift_voce.yaml` produces byte-identical `swift_voce.py` and the same
`source_hash` in `_manifest.json`. For the current `swift_voce.yaml`,
`source_hash` is
`7b5af3a8bb79c2e44e0055a7076dd2c9de2ce8c75eb2e262b80bb4e0232d557f`
(SHA-256 of the canonical input formula string, not of the whole file).

### Step 2 — Commit the generated artifacts into NumerixWeave

Switch to the **NumerixWeave** checkout and commit the files Step 1 wrote (or
overwrote) under `libs/ticonstit/src/ticonstit/generated/`:

```bash
cd /Users/shmuelosovski/Github/Personal/NumerixWeave
git add libs/ticonstit/src/ticonstit/generated/plasticity/swift_voce.py \
libs/ticonstit/src/ticonstit/generated/_manifest.json \
libs/ticonstit/src/ticonstit/generated/tests/test_swift_voce.py
git commit -m "chore(ticonstit): regenerate SwiftVoce carrier from MechDSL lawgen"
```

The generated files are **checked into version control**, not produced by
NumerixWeave's own build or CI. Anyone building or testing NumerixWeave gets
the artifacts from git, not from a live MechDSL invocation — this is what
keeps MechDSL out of NumerixWeave's runtime/build dependency graph.

Do not hand-edit files under `generated/` (see NumerixWeave's
`libs/ticonstit/src/ticonstit/generated/GENERATED.md`) — re-run Step 1
instead, so the source of truth stays the MechDSL YAML law spec.

### Step 3 — NumerixWeave CI verifies and gates

NumerixWeave CI (and any local `pytest` run) then:

- Runs the **equivalence gate**,
`libs/ticonstit/tests/generated/test_swift_voce_equivalence.py`, which
re-invokes `mechdsl-lawgen compile` as a **subprocess** (via
`uv run --project <MechDSL checkout> mechdsl-lawgen ...`) against a sibling
MechDSL checkout, and byte/value-compares the freshly emitted carrier
against the committed one at `rtol=1e-10`. This test skips cleanly (does
not fail) if no MechDSL checkout is available at
`MECHDSL_ROOT` (default `/Users/shmuelosovski/Github/Personal/MechDSL`).
- Confirms the committed `_manifest.json`'s `source_hash` matches the
pinned/expected value — catching silent drift between the YAML law source
and the committed generated artifact.
- Runs `tools/check_dependency_graph.py`, which enforces (among other things)
that nothing under `libs/` or `apps/` imports `mechdsl` or `sympy` at
runtime. `ticonstit.generated.plasticity.swift_voce` imports only `taichi`.

## Why the order matters

The steps must run in this sequence — compile, then commit, then
verify/consume — because NumerixWeave's own tooling (`check_dependency_graph.py`,
its `pyproject.toml` workspace membership, its CI) has no visibility into
MechDSL at all except through:

- files that already exist in the NumerixWeave git tree (Step 2's commit),
and
- the one deliberately-isolated subprocess call in the equivalence test
(Step 3), which runs MechDSL in MechDSL's own venv and never imports it
into the NumerixWeave process.

If Step 2 is skipped or done out of order (e.g. NumerixWeave CI tries to
regenerate artifacts itself, or a stale artifact is committed without
re-running Step 1 after a law YAML change), the `source_hash` check in Step 3
is what catches the drift — it is the only cross-repo consistency signal that
survives the fact that the build edge itself is invisible to static
dependency analysis.

## See also

- NumerixWeave: `libs/ticonstit/src/ticonstit/generated/GENERATED.md` — the
consumer-side note on the same seam.
- `dev/plans/mfront_cycleM0.md` (Phase 4, R1) — the plan risk this doc
mitigates.
- `dev/plans/mfront_cycleM0/Phase_4_context_summary.md` — phase-level
context for the compile → commit → consume flow.
54 changes: 54 additions & 0 deletions laws/plasticity/swift_voce.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Swift-Voce isotropic hardening law — authoritative MechDSL source.
#
# MFront-mimic Cycle M0, Phase 4 (dev/plans/mfront_cycleM0.md lines 114-116).
#
# This YAML is the single source of truth for the SwiftVoce hardening carrier.
# ``mechdsl-lawgen compile laws/plasticity/swift_voce.yaml --target ticonstit
# --out <dir>`` lowers the R/H/Q expressions below into a Taichi ``class SwiftVoce``
# (swift_voce.py), a Cycle 0-shaped _manifest.json, and a self-contained pytest
# file — the artifacts NumerixWeave's ``ticonstit.generated`` package consumes.
#
# Canonical formula + source_hash
# --------------------------------
# The ``R`` expression string below is spelled VERBATIM to reproduce Cycle 0's
# published ``source_hash`` — the SHA-256 of the canonical generator-input
# formula string "R = sigma0 + Q*(1-exp(-b*p)) + K*((p+p0)**n - p0**n)"
# (= 7b5af3a8bb79c2e44e0055a7076dd2c9de2ce8c75eb2e262b80bb4e0232d557f). The
# compiler prepends "R = " to this raw string and hashes it verbatim (no
# whitespace normalisation), so the spacing here (``1-exp`` and ``**n - p0**n``)
# is load-bearing — do not reformat it.
#
# Q-vs-Q_inf naming (transitional divergence)
# -------------------------------------------
# The Voce saturation magnitude is spelled ``Q`` in this formula (matching the
# Cycle 0 formula string), while the hand-authored material card names it
# ``Q_inf``. Reconciling the two names is deferred; the manifest emitter runs with
# check_matches_spec disabled during this transition (see manifest.py).
#
# H and Q factors
# ---------------
# H (strain-rate factor) and Q (thermal factor) are the two multiplicative
# factors of the flow stress — NOT dR/dp. This authoritative SwiftVoce law is
# rate-independent and isothermal, so both factors are the neutral ``1`` (their
# derivatives are ``0``), matching the Cycle 0 reference's fallback when the
# optional rate (edot0, m) / thermal (alpha, T_ref) parameters are absent.

name: SwiftVoce

# Material parameters, in Cycle 0 order: the Voce base (sigma0, Q, b) followed by
# the Swift power-law term (K, n, p0). Q is the saturation magnitude (see the
# Q-vs-Q_inf note above).
parameters: [sigma0, Q, b, K, n, p0]

# Free-variable bindings: accumulated plastic strain p (R's primary axis), plastic
# strain rate edot (H's axis), temperature T (Q's axis).
variables: [p, edot, T]

expressions:
# Isotropic-hardening flow stress: Voce saturation + Swift power-law.
# Spelled verbatim for source_hash fidelity (see the header note).
R: "sigma0 + Q*(1-exp(-b*p)) + K*((p+p0)**n - p0**n)"
# Rate factor (neutral: rate-independent).
H: "1"
# Thermal factor (neutral: isothermal).
Q: "1"
2 changes: 1 addition & 1 deletion packages/algo2code/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version = "0.2.0"
description = "Transpile LaTeX algorithm boxes (algpseudocode) to executable Taichi/NumPy/C code"
readme = "README.md"
license = "MIT"
requires-python = ">=3.12,<3.13"
requires-python = ">=3.11,<3.14"
authors = [
{ name = "Shmuel Osovski" },
]
Expand Down
9 changes: 6 additions & 3 deletions packages/mechdsl-core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version = "0.2.0"
description = "MechDSL core — LaTeX tensor expressions to FEM solver code"
readme = "README.md"
license = "MIT"
requires-python = ">=3.12,<3.13"
requires-python = ">=3.11,<3.14"
authors = [
{ name = "Shmuel Osovski" },
]
Expand All @@ -23,9 +23,9 @@ classifiers = [
]
dependencies = [
"sympy>=1.12",
"numpy>=1.24",
"numpy>=2.4.2",
"opt-einsum>=3.3",
"pyyaml>=6.0",
"pyyaml>=6.0.3",
"scipy>=1.17.0",
"nrpylatex @ git+https://github.com/SOSOVSKI/nrpylatex",
]
Expand Down Expand Up @@ -53,6 +53,9 @@ dependencies = [
# rest of the on-device seam path; the base install stays algo2code/Taichi-free.
verify = ["torch>=2.0", "taichi>=1.7", "ti-runtime", "algo2code"]

[project.scripts]
mechdsl-lawgen = "mechdsl.lawgen.cli:main"

[tool.uv.sources]
ti-runtime = { workspace = true }
algo2code = { workspace = true }
Expand Down
Loading
Loading