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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ jobs:
- name: Run tests
run: uv run pytest test tests -q

# The verifier bridge needs OCP, which the SDK and the suite above never do: it is overlaid for this one run.
- name: Verifier bridge (OCP)
run: uv run --with "cadquery-ocp-novtk>=7.9.3.1,<8" pytest src/cpp/tests/python/test_verify_bridge.py -q

editor-e2e:
needs: occt
# The editor tests that boot a real marimo kernel (about a minute); the main
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ jobs:
run: |
.venv/bin/python -m pytest test tests -q
.venv/bin/python -m pytest src/cpp/tests/python -q
# Last: the verifier bridge needs OCP, which nothing above may find installed.
uv pip install --python .venv/bin/python "cadquery-ocp-novtk>=7.9.3.1,<8"
.venv/bin/python -m pytest src/cpp/tests/python/test_verify_bridge.py -q

release:
name: Build the skill and publish the release
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ user-facing notes live in [`docs/updates/`](docs/updates/) (English and

## Unreleased

### Added
- `simplecadapi.verify` (`to_ocp`, `from_ocp`) and the `verify` extra (`pip install "simplecadapi[verify]"`, OCP 7.9
without VTK) for verifier scripts: `shape.wrapped` is the SDK's own binding and OCP functions reject it. The SDK
itself still never imports OCP. The skill gains `references/discipline/verifier-scripts.md` with the recipes
(volume, bounds, validity, point in solid, distance, overlap) and a verifier skeleton.

### Removed
- The `fem` extra (`gmsh` and `ccx2paraview[VTK]`) and the README steps that used it; the `gmsh` extra stays. VTK is no
longer in the lock file at all.

## 2.2.7b1 — 2026-10-24

### Added
Expand Down
34 changes: 1 addition & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,37 +484,6 @@ Both outputs contain the same oriented triangles and require no optional
remeshing dependency. Control curved-surface accuracy with `linear_deflection`
and `angular_deflection_degrees`.

### Optional CalculiX FEM workflow

The AP242/Gmsh example also includes an optional CalculiX FEM workflow. Install
the Python-side FEM dependencies with `uv sync --extra fem`, and install the
external CalculiX solver separately (on macOS: `brew install
costerwi/homebrew-calculix/calculix-ccx`). The example uses consistent `mm`,
`N`, and `MPa` units:

```bash
uv run --extra fem python examples/ap242_gmsh_volume_mesh/run_calculix.py \
--ccx "$(brew --prefix calculix-ccx)/bin/ccx_2.23"
uv run --extra fem python examples/ap242_gmsh_volume_mesh/visualize_calculix.py
uv run --extra fem python examples/ap242_gmsh_volume_mesh/study_mesh_convergence.py \
--ccx "$(brew --prefix calculix-ccx)/bin/ccx_2.23" \
--linear-solver "ITERATIVE CHOLESKY" --solver-timeout 2400
```

The analysis writes CalculiX `.inp`, `.dat`, `.frd`, solver-log, summary JSON,
ParaView `.vtu`, and displaced von-Mises PNG artifacts. The preview shows the
load physical group's yellow boundary and red `-Z` force arrows without
covering the stress heatmap. The convergence study supports `--resume`; failed
solver levels are reported separately and never enter the numerical sequence.

The checked-in `-1000 N` study evaluates eleven mesh sizes from `h=3.0 mm` to
`h=0.25 mm`. A platform requires three consecutive refinement pairs below `5%`
maximum-displacement change and `10%` peak integration-point von-Mises change.
The verification level N is `h=0.25 mm` (`0.0331843 mm`, `98.6392 MPa`), so the
recommended production level N-1 is `h=0.27 mm`. Fine levels use iterative
Cholesky after a same-mesh comparison at `h=0.375 mm` matched SPOOLES within
`0.005%`; this avoids the direct solver's in-memory capacity limit.

## Replayable Operation Graphs

Use an explicit `GraphSession` when a geometry flow outside a notebook should
Expand Down Expand Up @@ -681,10 +650,9 @@ uv run python examples/flange_plate/verify.py
sca run examples/compact_two_stage_planetary_reducer/compact_two_stage_planetary_reducer.py
uv run python examples/compact_two_stage_planetary_reducer/export_mjcf.py

# FEM (AP242 STEP -> Gmsh volume mesh -> Calculix statics)
# Gmsh volume mesh (AP242 STEP -> volume mesh)
uv run python examples/ap242_gmsh_volume_mesh/export_step.py
uv run --extra gmsh python examples/ap242_gmsh_volume_mesh/export_fem_mesh.py
uv run --extra fem python examples/ap242_gmsh_volume_mesh/run_calculix.py
```

To try the editor on a real model, `sca example u_link` copies a motor mount
Expand Down
30 changes: 1 addition & 29 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,33 +438,6 @@ STL 与 OBJ 共用 OpenCASCADE 对求值后 BREP 的直接三角化。两种格
定向三角面,不再需要可选 remeshing 依赖。曲面精度由 `linear_deflection` 和
`angular_deflection_degrees` 控制。

### 可选 CalculiX FEM 流程

AP242/Gmsh 示例还包含可选 CalculiX FEM 流程。Python 侧依赖通过
`uv sync --extra fem` 安装;CalculiX 求解器需要单独安装(macOS:
`brew install costerwi/homebrew-calculix/calculix-ccx`):

```bash
uv run --extra fem python examples/ap242_gmsh_volume_mesh/run_calculix.py \
--ccx "$(brew --prefix calculix-ccx)/bin/ccx_2.23"
uv run --extra fem python examples/ap242_gmsh_volume_mesh/visualize_calculix.py
uv run --extra fem python examples/ap242_gmsh_volume_mesh/study_mesh_convergence.py \
--ccx "$(brew --prefix calculix-ccx)/bin/ccx_2.23" \
--linear-solver "ITERATIVE CHOLESKY" --solver-timeout 2400
```

分析统一使用 `mm`、`N`、`MPa`,输出 CalculiX `.inp`、`.dat`、`.frd`、求解日志、
摘要 JSON、ParaView `.vtu` 和位移放大后的 von Mises 云图 PNG。图中用黄色轮廓
标出载荷 physical group,并用红色 `-Z` 箭头表示载荷方向,不覆盖应力热力图。
收敛研究支持 `--resume`;失败的求解级别单独记录,不会混入数值序列。

仓库中的 `-1000 N` 研究从 `h=3.0 mm` 加密到 `h=0.25 mm`,共 11 级。平台要求
连续三组细化同时满足最大位移变化低于 `5%`、积分点峰值 von Mises 应力变化低于
`10%`。第 N 级验证网格为 `h=0.25 mm`(`0.0331843 mm`、`98.6392 MPa`),
因此生产计算推荐第 N-1 级 `h=0.27 mm`。细网格使用迭代 Cholesky;在
`h=0.375 mm` 同网格上与 SPOOLES 的位移和峰值应力差异均低于 `0.005%`,从而
绕过直接求解器的内存容量限制。

## 可重放操作图

notebook 之外的几何流程需要检查、序列化或重放时,使用显式 `GraphSession`:
Expand Down Expand Up @@ -572,10 +545,9 @@ uv run python examples/flange_plate/verify.py
sca run examples/compact_two_stage_planetary_reducer/compact_two_stage_planetary_reducer.py
uv run python examples/compact_two_stage_planetary_reducer/export_mjcf.py

# FEM(AP242 STEP -> Gmsh 体网格 -> CalculiX 静力)
# Gmsh 体网格(AP242 STEP -> 体网格)
uv run python examples/ap242_gmsh_volume_mesh/export_step.py
uv run --extra gmsh python examples/ap242_gmsh_volume_mesh/export_fem_mesh.py
uv run --extra fem python examples/ap242_gmsh_volume_mesh/run_calculix.py
```

想在真实模型上试用编辑器:`sca example u_link` 把一个电机座(两个零件 notebook 加
Expand Down
1 change: 1 addition & 0 deletions docs/skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ print(len(rebuilt))
- `references/workflows/` — goal-oriented workflows, including drawing reconstruction
- `references/domains/` — capability domains, including drawing inspection
- `references/discipline/` — modeling knowledge and invariants
- `references/discipline/verifier-scripts.md` — writing a verifier script (OCP through `simplecadapi.verify`)
- `references/SDK_OVERVIEW.md` — package-level map
- `references/inspect/brep-reverse-engineering.md`
- `references/domains/addon-development.md` — `sca` addon CLI and authoring guide
Expand Down
4 changes: 4 additions & 0 deletions docs/skill/references/discipline/geometric-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ invalid — correct bounds and volume, one bad face — which survives until the
next operation fails far from the cause. Gate every boolean result, not just
the final one.

A verifier that measures with the full OCCT toolbox (point classification,
minimum distance, Boolean overlap) is covered by
`references/discipline/verifier-scripts.md`.

## Incremental grounding

Every major step prints a small QL-derived fact: selected face count, a
Expand Down
147 changes: 147 additions & 0 deletions docs/skill/references/discipline/verifier-scripts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Discipline: Verifier Scripts

A verifier script measures a finished model against the specification and exits
non-zero when a requirement fails. It is separate from the modeling script: it
imports the model, measures it independently, and prints one line per check.

## Evidence labels

- **SDK contract:** `simplecadapi.verify.to_ocp(shape)` returns an OCP
`TopoDS_Shape` copy of any SDK shape; `from_ocp(shape)` returns the SDK shape
of an OCP shape. `Solid.get_volume()`, `simplecadapi.inspect.brep` and QL
need no extra install.
- **Validation guidance:** the checks and the skeleton below are
recommendations, not SDK methods.

## Use the SDK first, OCP for the rest

Measure with the SDK where it can answer: `get_volume()`, QL for face and edge
facts, `inspect.brep` for validity and shell closure
(`references/discipline/geometric-validation.md`). Reach for OCP only for what
the SDK does not expose: point-in-solid classification, minimum distance
between shapes, Boolean overlap volume, mass-property centroids and inertia.

The SDK itself does not use OCP. Install it for verifier scripts only:

```bash
pip install "simplecadapi[verify]" # OCP 7.9, the OCCT generation the SDK ships
```

## `shape.wrapped` is not an OCP shape

`shape.wrapped` is the SDK's own binding. Passing it to an OCP function fails
with `incompatible function arguments`. Convert with `to_ocp`:

```python
from simplecadapi.verify import to_ocp

BRepGProp.VolumeProperties_s(solid.wrapped, props) # TypeError
BRepGProp.VolumeProperties_s(to_ocp(solid), props) # works
```

`to_ocp` copies through OpenCASCADE's binary format (milliseconds for a part).
Convert once per shape and reuse the result; tags and tracking history stay on
the SDK shape. An OCP shape you built yourself comes back with `from_ocp`.

The other direction has one more step: the SDK's diagnostic functions
(`inspect_shape_rbrepinspection`, ...) take the raw shape of an SDK shape, its
`.wrapped`. For a shape that came out of OCP, pass `from_ocp(shape).wrapped`
(or keep the SDK shape and pass `solid.wrapped`).

## Recipes (OCP 7.9)

```python
from OCP.BRepAlgoAPI import BRepAlgoAPI_Common
from OCP.BRepBndLib import BRepBndLib
from OCP.BRepCheck import BRepCheck_Analyzer
from OCP.BRepClass3d import BRepClass3d_SolidClassifier
from OCP.BRepExtrema import BRepExtrema_DistShapeShape
from OCP.BRepGProp import BRepGProp
from OCP.Bnd import Bnd_Box
from OCP.GProp import GProp_GProps
from OCP.TopAbs import TopAbs_IN
from OCP.TopoDS import TopoDS
from OCP.gp import gp_Pnt


def volume(shape):
props = GProp_GProps()
BRepGProp.VolumeProperties_s(shape, props)
return props.Mass()


def bounds(shape): # (xmin, ymin, zmin, xmax, ymax, zmax)
box = Bnd_Box()
BRepBndLib.Add_s(shape, box)
return box.Get()


def is_valid(shape):
return BRepCheck_Analyzer(shape).IsValid()


def contains(solid, x, y, z, tol=1e-6): # solid: an OCP solid, see the cast below
return BRepClass3d_SolidClassifier(solid, gp_Pnt(x, y, z), tol).State() == TopAbs_IN


def gap(a, b): # minimum distance, 0 when they touch or overlap
measure = BRepExtrema_DistShapeShape(a, b)
assert measure.IsDone()
return measure.Value()


def overlap_volume(a, b): # interference between two parts
common = BRepAlgoAPI_Common(a, b)
assert common.IsDone()
return volume(common.Shape())
```

- `to_ocp` returns a generic `TopoDS_Shape`. Functions that need the concrete
type want a cast: `TopoDS.Solid_s(to_ocp(solid))`.
- OCP 7.9 casts and static methods end in `_s` (`TopoDS.Solid_s`,
`BRepGProp.VolumeProperties_s`). OCP 8 renames them; the `verify` extra pins
7.9 so documentation and code agree.
- Do not guess OCP names. Open the class in the OCCT reference, or
`help(OCP.<Module>.<Class>)`, before the first call (a method that is not in
the documentation does not exist, however plausible it looks).

## Skeleton

```python
"""verify_bracket.py: exits 1 when a requirement is not met."""
import math
import sys

from simplecadapi.verify import to_ocp

from bracket import build # the modeling script's result: a Solid

checks = []


def check(name, ok, detail=""):
checks.append(ok)
print(("PASS" if ok else "FAIL"), name, detail)


part = build()
shape = to_ocp(part)

check("valid", is_valid(shape))
check("volume 5151.77 mm3 +-0.1%", math.isclose(volume(shape), 5151.77, rel_tol=1e-3), f"{volume(shape):.2f}")
x0, y0, z0, x1, y1, z1 = bounds(shape)
check("height 30 mm", math.isclose(z1 - z0, 30.0, abs_tol=1e-6), f"{z1 - z0:.4f}")
check("bore is empty", not contains(TopoDS.Solid_s(shape), 0, 0, 15))

sys.exit(0 if all(checks) else 1)
```

Rules for a verifier:

- Every user-specified dimension gets its own targeted check with the measured
value printed; "valid" alone proves nothing about size.
- Tolerances come from the specification, not from what happens to pass.
- A check that cannot run (missing OCP, unreadable file) is a failure, not a
skip: report it and exit non-zero.
- Do not edit the model to make a check pass; fix the model or report the
discrepancy.
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,18 @@ editor = [
# simplecadapi.inspect.brep is part of the default install; this extra is kept empty so that
# `pip install "simplecadapi[inspect]"` from older instructions still works.
inspect = []
# Verifier scripts measure models with OCP; the SDK itself never imports it (simplecadapi.verify bridges the two).
# The build without VTK (same `OCP` import, no 100 MB VTK) and the OCCT generation the SDK ships (7.9): OCP 8 renames
# the casts and changes signatures.
verify = [
"cadquery-ocp-novtk>=7.9.3.1,<8",
]
whucad = [
"h5py>=3.8",
]
gmsh = [
"gmsh>=4.15,<5",
]
fem = [
"gmsh>=4.15,<5",
"ccx2paraview[VTK]>=3.2,<4",
]

[project.urls]
Homepage = "https://github.com/PhySpace/SimpleCADAPI"
Expand Down
Loading
Loading