witwin 0.0.2__tar.gz → 0.3.0__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 (36) hide show
  1. {witwin-0.0.2 → witwin-0.3.0}/.github/workflows/publish-witwin.yml +115 -19
  2. {witwin-0.0.2 → witwin-0.3.0}/.gitignore +0 -3
  3. witwin-0.3.0/PKG-INFO +63 -0
  4. witwin-0.3.0/README.md +40 -0
  5. {witwin-0.0.2 → witwin-0.3.0}/hatch_build.py +3 -1
  6. witwin-0.3.0/pyproject.toml +42 -0
  7. {witwin-0.0.2 → witwin-0.3.0}/scripts/build_mesh_sdf_cuda_prebuilt.py +3 -3
  8. witwin-0.3.0/scripts/verify_cuda_binary_arches.py +73 -0
  9. witwin-0.3.0/tests/test_contracts.py +56 -0
  10. witwin-0.3.0/tests/test_dependency_contract.py +15 -0
  11. {witwin-0.0.2 → witwin-0.3.0}/tests/test_geometry_sdf.py +0 -65
  12. {witwin-0.0.2 → witwin-0.3.0}/tests/test_geometry_smoke.py +19 -49
  13. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/__init__.py +4 -21
  14. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/__init__.py +4 -7
  15. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/base.py +12 -0
  16. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/cuda/backend.py +1 -1
  17. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/cuda/build.py +56 -15
  18. witwin-0.3.0/witwin/core/geometry/cuda/extension.cpp +29 -0
  19. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/cuda/mesh_sdf_kernels.cu +129 -72
  20. witwin-0.3.0/witwin/core/geometry/polygon.py +91 -0
  21. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/primitives.py +4 -300
  22. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/material.py +54 -103
  23. witwin-0.0.2/PKG-INFO +0 -53
  24. witwin-0.0.2/README.md +0 -30
  25. witwin-0.0.2/pyproject.toml +0 -42
  26. witwin-0.0.2/tests/test_scene_base.py +0 -18
  27. witwin-0.0.2/witwin/core/geometry/cuda/extension.cpp +0 -43
  28. witwin-0.0.2/witwin/core/geometry/smpl.py +0 -229
  29. witwin-0.0.2/witwin/core/scene.py +0 -38
  30. witwin-0.0.2/witwin/core/scene_to_mitsuba.py +0 -138
  31. {witwin-0.0.2 → witwin-0.3.0}/benchmark/benchmark_mesh_sdf.py +0 -0
  32. {witwin-0.0.2 → witwin-0.3.0}/tests/conftest.py +0 -0
  33. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/cuda/__init__.py +0 -0
  34. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/mesh.py +0 -0
  35. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/mesh_sdf.py +0 -0
  36. {witwin-0.0.2 → witwin-0.3.0}/witwin/core/math.py +0 -0
@@ -83,14 +83,13 @@ jobs:
83
83
  PY
84
84
 
85
85
  build_cuda_wheels:
86
- name: Build CUDA wheel / ${{ matrix.os }} / py${{ matrix.python_version }}
86
+ name: Build stable CUDA wheel / ${{ matrix.os }}
87
87
  needs: validate_release
88
88
  runs-on: ${{ matrix.os }}
89
89
  strategy:
90
90
  fail-fast: false
91
91
  matrix:
92
92
  os: [ubuntu-22.04, windows-2022]
93
- python_version: ["3.10", "3.11", "3.12"]
94
93
 
95
94
  steps:
96
95
  - name: Check out repository
@@ -99,7 +98,7 @@ jobs:
99
98
  - name: Set up Python
100
99
  uses: actions/setup-python@v5
101
100
  with:
102
- python-version: ${{ matrix.python_version }}
101
+ python-version: "3.10"
103
102
 
104
103
  - name: Free disk space (Linux)
105
104
  if: runner.os == 'Linux'
@@ -113,11 +112,11 @@ jobs:
113
112
  echo "Disk after cleanup:"; df -h /
114
113
 
115
114
  - name: Install CUDA Toolkit
116
- uses: Jimver/cuda-toolkit@v0.2.29
115
+ uses: Jimver/cuda-toolkit@v0.2.35
117
116
  with:
118
- cuda: "12.5.0"
117
+ cuda: "12.8.1"
119
118
  method: local
120
- log-file-suffix: "${{ runner.os }}-py${{ matrix.python_version }}"
119
+ log-file-suffix: "${{ runner.os }}-stable-abi"
121
120
 
122
121
  - name: Set up MSVC
123
122
  if: runner.os == 'Windows'
@@ -149,19 +148,28 @@ jobs:
149
148
  - name: Install CUDA extension build dependencies
150
149
  run: |
151
150
  python -m pip install --upgrade pip setuptools wheel ninja numpy
152
- python -m pip install torch==2.6.0 --index-url https://download.pytorch.org/whl/cu124
151
+ python -m pip install torch==2.10.0 --index-url https://download.pytorch.org/whl/cu128
153
152
  python -m pip install build
154
153
 
155
- - name: Build packaged mesh SDF CUDA extension
154
+ - name: Build packaged stable mesh SDF CUDA extension
156
155
  shell: bash
157
156
  env:
158
157
  MAX_JOBS: "2"
159
- TORCH_CUDA_ARCH_LIST: "7.0;8.0;8.6;8.9;9.0+PTX"
160
- WITWIN_CORE_MESH_SDF_CUDA_BUILD_DIR: ${{ runner.temp }}/witwin_core_mesh_sdf_cuda
158
+ WITWIN_CUDA_GENCODE_ARCHES: "7.0;7.5;8.0;8.6;8.9;9.0;10.0;10.1;12.0+PTX"
161
159
  PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }}
160
+ WITWIN_CORE_MESH_SDF_CUDA_BUILD_DIR: ${{ runner.temp }}/witwin_core_mesh_sdf_cuda/stable_abi_v1
162
161
  run: |
163
162
  python "$PACKAGE_DIR"/scripts/build_mesh_sdf_cuda_prebuilt.py --verbose
164
163
 
164
+ - name: Verify compiled CUDA architectures
165
+ shell: bash
166
+ env:
167
+ PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }}
168
+ run: |
169
+ python "$PACKAGE_DIR"/scripts/verify_cuda_binary_arches.py \
170
+ --stem witwin_core_mesh_sdf_cuda \
171
+ "$PACKAGE_DIR"/witwin/core/geometry/cuda/prebuilt
172
+
165
173
  - name: Build platform wheel
166
174
  shell: bash
167
175
  env:
@@ -169,21 +177,25 @@ jobs:
169
177
  run: |
170
178
  python -m build --wheel "$PACKAGE_DIR"
171
179
 
172
- - name: Normalize Linux wheel platform tag
180
+ - name: Repair Linux wheel platform tag
173
181
  if: runner.os == 'Linux'
174
182
  shell: bash
175
183
  env:
176
184
  PACKAGE_DIR: ${{ needs.validate_release.outputs.package_dir }}
177
185
  run: |
178
- # PyPI rejects the raw linux_x86_64 tag; the extension is built on the
179
- # ubuntu-22.04 runner (glibc 2.35), so retag to the matching manylinux
180
- # tag. torch/CUDA runtime libraries are runtime dependencies and stay
181
- # unbundled, so no auditwheel repair is required.
182
- shopt -s nullglob
186
+ python -m pip install auditwheel patchelf
187
+ mkdir -p "$PACKAGE_DIR"/wheelhouse
183
188
  for whl in "$PACKAGE_DIR"/dist/*-linux_x86_64.whl; do
184
- echo "Retagging $whl"
185
- python -m wheel tags --platform-tag manylinux_2_35_x86_64 --remove "$whl"
189
+ python -m auditwheel repair "$whl" \
190
+ --plat manylinux_2_35_x86_64 \
191
+ --wheel-dir "$PACKAGE_DIR"/wheelhouse \
192
+ --exclude libc10.so --exclude libc10_cuda.so \
193
+ --exclude libtorch.so --exclude libtorch_cpu.so \
194
+ --exclude libtorch_cuda.so --exclude libtorch_python.so \
195
+ --exclude libcudart.so.12 --exclude libcuda.so.1
186
196
  done
197
+ rm -f "$PACKAGE_DIR"/dist/*-linux_x86_64.whl
198
+ mv "$PACKAGE_DIR"/wheelhouse/*.whl "$PACKAGE_DIR"/dist/
187
199
  ls -l "$PACKAGE_DIR"/dist
188
200
 
189
201
  - name: Smoke install prebuilt wheel
@@ -197,9 +209,18 @@ jobs:
197
209
  python -m pip install --no-deps "$PACKAGE_DIR"/dist/*.whl
198
210
  python - <<'PY'
199
211
  import importlib.util
212
+ import os
200
213
  import site
214
+ import zipfile
201
215
  from pathlib import Path
202
216
 
217
+ wheel = next((Path(os.environ["PACKAGE_DIR"]) / "dist").glob("*.whl"))
218
+ names = zipfile.ZipFile(wheel).namelist()
219
+ native = [name for name in names if "witwin_core_mesh_sdf_cuda" in name and name.endswith((".so", ".pyd"))]
220
+ assert len(native) == 1, native
221
+ assert "/prebuilt/" in f"/{native[0]}"
222
+ assert "torch_" not in native[0]
223
+
203
224
  candidates = []
204
225
  for root in site.getsitepackages() + [site.getusersitepackages()]:
205
226
  candidates.append(Path(root) / "witwin" / "core" / "geometry" / "cuda" / "build.py")
@@ -212,6 +233,7 @@ jobs:
212
233
 
213
234
  path = module.prebuilt_extension_path()
214
235
  assert path.exists(), f"missing packaged prebuilt extension: {path}"
236
+ assert path.parent.name == "prebuilt"
215
237
  extension = module.build_extension()
216
238
  print(f"Loaded packaged mesh SDF CUDA extension from {extension.__file__}")
217
239
  PY
@@ -219,10 +241,83 @@ jobs:
219
241
  - name: Upload wheel artifact
220
242
  uses: actions/upload-artifact@v4
221
243
  with:
222
- name: wheel-${{ matrix.os }}-py${{ matrix.python_version }}
244
+ name: wheel-${{ matrix.os }}
223
245
  path: ${{ needs.validate_release.outputs.package_dir }}/dist/*.whl
224
246
  if-no-files-found: error
225
247
 
248
+ test_torch_compatibility:
249
+ name: Stable ABI / ${{ matrix.os }} / py${{ matrix.compatibility.python_version }} / torch${{ matrix.compatibility.torch_version }}
250
+ needs:
251
+ - validate_release
252
+ - build_cuda_wheels
253
+ runs-on: ${{ matrix.os }}
254
+ strategy:
255
+ fail-fast: false
256
+ matrix:
257
+ os: [ubuntu-22.04, windows-2022]
258
+ compatibility:
259
+ - {python_version: "3.10", torch_version: "2.10.0", cuda_index: "cu128"}
260
+ - {python_version: "3.11", torch_version: "2.10.0", cuda_index: "cu128"}
261
+ - {python_version: "3.12", torch_version: "2.10.0", cuda_index: "cu128"}
262
+ - {python_version: "3.13", torch_version: "2.10.0", cuda_index: "cu128"}
263
+ - {python_version: "3.14", torch_version: "2.10.0", cuda_index: "cu128"}
264
+ - {python_version: "3.14", torch_version: "2.11.0", cuda_index: "cu128"}
265
+ - {python_version: "3.14", torch_version: "2.12.0", cuda_index: "cu126"}
266
+
267
+ steps:
268
+ - name: Set up Python
269
+ uses: actions/setup-python@v5
270
+ with:
271
+ python-version: ${{ matrix.compatibility.python_version }}
272
+
273
+ - name: Download stable wheel
274
+ uses: actions/download-artifact@v4
275
+ with:
276
+ name: wheel-${{ matrix.os }}
277
+ path: dist
278
+
279
+ - name: Install compatibility environment
280
+ shell: bash
281
+ run: |
282
+ python -m pip install --upgrade pip
283
+ python -m pip install --no-cache-dir "torch==${{ matrix.compatibility.torch_version }}" \
284
+ --index-url "https://download.pytorch.org/whl/${{ matrix.compatibility.cuda_index }}"
285
+ python -m pip install --no-deps dist/*.whl
286
+
287
+ - name: Load the same stable binary
288
+ shell: bash
289
+ env:
290
+ EXPECTED_TORCH: ${{ matrix.compatibility.torch_version }}
291
+ WITWIN_CORE_MESH_SDF_CUDA_PREBUILT: "1"
292
+ WITWIN_CORE_MESH_SDF_CUDA_BUILD_DIR: ${{ runner.temp }}/must-not-jit-build
293
+ run: |
294
+ python - <<'PY'
295
+ import importlib.util
296
+ import os
297
+ import site
298
+ from pathlib import Path
299
+
300
+ import torch
301
+
302
+ assert torch.__version__.split("+", 1)[0] == os.environ["EXPECTED_TORCH"]
303
+ candidates = [
304
+ Path(root) / "witwin" / "core" / "geometry" / "cuda" / "build.py"
305
+ for root in site.getsitepackages() + [site.getusersitepackages()]
306
+ ]
307
+ build_path = next((path for path in candidates if path.exists()), None)
308
+ assert build_path is not None, candidates
309
+ spec = importlib.util.spec_from_file_location("witwin_core_mesh_sdf_cuda_build", build_path)
310
+ module = importlib.util.module_from_spec(spec)
311
+ spec.loader.exec_module(module)
312
+
313
+ native_path = module.prebuilt_extension_path()
314
+ assert native_path.exists(), native_path
315
+ extension = module.build_extension()
316
+ assert Path(extension.__file__).resolve() == native_path.resolve()
317
+ assert extension.query_mesh_unsigned_distance is not None
318
+ print(f"Loaded {native_path.name} with torch {torch.__version__} on Python {os.sys.version.split()[0]}")
319
+ PY
320
+
226
321
  build_sdist:
227
322
  name: Build source distribution
228
323
  needs: validate_release
@@ -257,6 +352,7 @@ jobs:
257
352
  if: github.event_name == 'release'
258
353
  needs:
259
354
  - build_cuda_wheels
355
+ - test_torch_compatibility
260
356
  - build_sdist
261
357
  runs-on: ubuntu-latest
262
358
  permissions:
@@ -171,9 +171,6 @@ venv.bak/
171
171
 
172
172
  # mkdocs documentation
173
173
  /site
174
- .slangtorch_cache/
175
- .fdtd_runtime_*.slang
176
- .adjoint_kernels_runtime_*.slang
177
174
  *.png
178
175
  *.npy
179
176
  *.npz
witwin-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: witwin
3
+ Version: 0.3.0
4
+ Summary: Witwin shared geometry, material, and structure contracts
5
+ License-Expression: MIT
6
+ Requires-Python: <3.15,>=3.10
7
+ Requires-Dist: numpy>=1.24
8
+ Requires-Dist: torch>=2.10
9
+ Provides-Extra: all
10
+ Requires-Dist: witwin-channel-native<0.2,>=0.1; extra == 'all'
11
+ Requires-Dist: witwin-genesis<0.2,>=0.1; extra == 'all'
12
+ Requires-Dist: witwin-maxwell<0.3,>=0.2; extra == 'all'
13
+ Requires-Dist: witwin-radar<0.4,>=0.3; extra == 'all'
14
+ Provides-Extra: channel
15
+ Requires-Dist: witwin-channel-native<0.2,>=0.1; extra == 'channel'
16
+ Provides-Extra: genesis
17
+ Requires-Dist: witwin-genesis<0.2,>=0.1; extra == 'genesis'
18
+ Provides-Extra: maxwell
19
+ Requires-Dist: witwin-maxwell<0.3,>=0.2; extra == 'maxwell'
20
+ Provides-Extra: radar
21
+ Requires-Dist: witwin-radar<0.4,>=0.3; extra == 'radar'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # WiTwin Core
25
+
26
+ WiTwin Core is the shared data and geometry package of the WiTwin stack. It defines the geometry and material contracts consumed by solver packages without imposing a common solver-specific scene lifecycle.
27
+
28
+ ## Get Started
29
+
30
+ CPython 3.10-3.14, PyTorch 2.10 or newer, and an NVIDIA GPU are supported for the main WiTwin simulation workflows.
31
+
32
+ ```bash
33
+ pip install witwin
34
+ ```
35
+
36
+ ## Prebuilt CUDA Support
37
+
38
+ Release wheels are built for Linux x86_64 and Windows x86_64 with CUDA 12.8. Each platform wheel carries one PyTorch Stable ABI native library built against PyTorch 2.10 and reused across supported Python and PyTorch versions; CI currently verifies PyTorch 2.10-2.12. The fat binaries contain native code for compute capabilities 7.0, 7.5, 8.0, 8.6, 8.9, 9.0, 10.0, 10.1, and 12.0, plus compute 12.0 PTX for forward-compatible Blackwell execution. This includes native coverage for RTX 2080-class Turing GPUs and current data-center and RTX/RTX PRO Blackwell families.
39
+
40
+ Linux wheels target `manylinux_2_35_x86_64`. The installed NVIDIA driver must support the CUDA 12.x runtime supplied by PyTorch; the CUDA toolkit is only needed for source/JIT builds.
41
+
42
+ For full CUDA 12.8 and Blackwell support, use at least driver 570.26 on Linux or 570.65 on Windows. Pre-Blackwell systems can use NVIDIA's CUDA 12.x minor-version compatibility floor (525.60.13 on Linux or 528.33 on Windows), subject to NVIDIA's compatibility-mode feature limits.
43
+
44
+ ## What It Provides
45
+
46
+ - Runtime-checkable `GeometrySpec` and `MaterialSpec` contracts
47
+ - Shared analytic geometry primitives and differentiable mesh/SDF utilities
48
+ - Reusable `Material` and `Structure` values for solver-owned scenes
49
+
50
+ Each solver owns its public `Scene` implementation. Radar-specific SMPL geometry lives in `witwin.radar.geometry`; Maxwell-specific `PolySlab` geometry lives in `witwin.maxwell.geometry`.
51
+
52
+ ## Related Solvers
53
+
54
+ - [WiTwin Maxwell](https://github.com/witwin-ai/witwin-maxwell)
55
+ - [WiTwin Radar](https://github.com/witwin-ai/witwin-radar)
56
+
57
+ ## Developer
58
+
59
+ <a href="http://xingyuchen.me/">
60
+ <img src="https://github.com/Asixa.png" alt="Xingyu Chen" width="48" height="48" style="border-radius:50%;">
61
+ </a>
62
+
63
+ [Xingyu Chen](http://xingyuchen.me/)
witwin-0.3.0/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # WiTwin Core
2
+
3
+ WiTwin Core is the shared data and geometry package of the WiTwin stack. It defines the geometry and material contracts consumed by solver packages without imposing a common solver-specific scene lifecycle.
4
+
5
+ ## Get Started
6
+
7
+ CPython 3.10-3.14, PyTorch 2.10 or newer, and an NVIDIA GPU are supported for the main WiTwin simulation workflows.
8
+
9
+ ```bash
10
+ pip install witwin
11
+ ```
12
+
13
+ ## Prebuilt CUDA Support
14
+
15
+ Release wheels are built for Linux x86_64 and Windows x86_64 with CUDA 12.8. Each platform wheel carries one PyTorch Stable ABI native library built against PyTorch 2.10 and reused across supported Python and PyTorch versions; CI currently verifies PyTorch 2.10-2.12. The fat binaries contain native code for compute capabilities 7.0, 7.5, 8.0, 8.6, 8.9, 9.0, 10.0, 10.1, and 12.0, plus compute 12.0 PTX for forward-compatible Blackwell execution. This includes native coverage for RTX 2080-class Turing GPUs and current data-center and RTX/RTX PRO Blackwell families.
16
+
17
+ Linux wheels target `manylinux_2_35_x86_64`. The installed NVIDIA driver must support the CUDA 12.x runtime supplied by PyTorch; the CUDA toolkit is only needed for source/JIT builds.
18
+
19
+ For full CUDA 12.8 and Blackwell support, use at least driver 570.26 on Linux or 570.65 on Windows. Pre-Blackwell systems can use NVIDIA's CUDA 12.x minor-version compatibility floor (525.60.13 on Linux or 528.33 on Windows), subject to NVIDIA's compatibility-mode feature limits.
20
+
21
+ ## What It Provides
22
+
23
+ - Runtime-checkable `GeometrySpec` and `MaterialSpec` contracts
24
+ - Shared analytic geometry primitives and differentiable mesh/SDF utilities
25
+ - Reusable `Material` and `Structure` values for solver-owned scenes
26
+
27
+ Each solver owns its public `Scene` implementation. Radar-specific SMPL geometry lives in `witwin.radar.geometry`; Maxwell-specific `PolySlab` geometry lives in `witwin.maxwell.geometry`.
28
+
29
+ ## Related Solvers
30
+
31
+ - [WiTwin Maxwell](https://github.com/witwin-ai/witwin-maxwell)
32
+ - [WiTwin Radar](https://github.com/witwin-ai/witwin-radar)
33
+
34
+ ## Developer
35
+
36
+ <a href="http://xingyuchen.me/">
37
+ <img src="https://github.com/Asixa.png" alt="Xingyu Chen" width="48" height="48" style="border-radius:50%;">
38
+ </a>
39
+
40
+ [Xingyu Chen](http://xingyuchen.me/)
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import sysconfig
3
4
  from pathlib import Path
4
5
 
5
6
  from hatchling.builders.hooks.plugin.interface import BuildHookInterface
@@ -13,5 +14,6 @@ class CustomBuildHook(BuildHookInterface):
13
14
  prebuilt_dir = Path(self.root) / "witwin" / "core" / "geometry" / "cuda" / "prebuilt"
14
15
  has_prebuilt_extension = any(prebuilt_dir.glob("witwin_core_mesh_sdf_cuda.*"))
15
16
  if has_prebuilt_extension:
16
- build_data["infer_tag"] = True
17
+ platform_tag = sysconfig.get_platform().replace("-", "_").replace(".", "_")
18
+ build_data["tag"] = f"py3-none-{platform_tag}"
17
19
  build_data["pure_python"] = False
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "witwin"
7
+ version = "0.3.0"
8
+ description = "Witwin shared geometry, material, and structure contracts"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10,<3.15"
11
+ license = "MIT"
12
+
13
+ dependencies = [
14
+ "numpy>=1.24",
15
+ "torch>=2.10",
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ maxwell = ["witwin-maxwell>=0.2,<0.3"]
20
+ radar = ["witwin-radar>=0.3,<0.4"]
21
+ channel = ["witwin-channel-native>=0.1,<0.2"]
22
+ genesis = ["witwin-genesis>=0.1,<0.2"]
23
+ all = [
24
+ "witwin-maxwell>=0.2,<0.3",
25
+ "witwin-radar>=0.3,<0.4",
26
+ "witwin-channel-native>=0.1,<0.2",
27
+ "witwin-genesis>=0.1,<0.2",
28
+ ]
29
+
30
+ [tool.hatch.build.targets.wheel]
31
+ packages = ["witwin"]
32
+ artifacts = [
33
+ "witwin/core/geometry/cuda/**/*.cpp",
34
+ "witwin/core/geometry/cuda/**/*.cu",
35
+ "witwin/core/geometry/cuda/**/*.cuh",
36
+ "witwin/core/geometry/cuda/**/*.h",
37
+ "witwin/core/geometry/cuda/prebuilt/**/*.pyd",
38
+ "witwin/core/geometry/cuda/prebuilt/**/*.so",
39
+ ]
40
+
41
+ [tool.hatch.build.hooks.custom]
42
+ path = "hatch_build.py"
@@ -39,24 +39,24 @@ def main() -> None:
39
39
  parser.add_argument("--verbose", action="store_true")
40
40
  args = parser.parse_args()
41
41
 
42
+ cuda_build = _load_cuda_build_module()
42
43
  build_dir = Path(
43
44
  os.environ.get(
44
45
  "WITWIN_CORE_MESH_SDF_CUDA_BUILD_DIR",
45
- Path(tempfile.gettempdir()) / "witwin_core_mesh_sdf_cuda_wheel",
46
+ Path(tempfile.gettempdir()) / "witwin_core_mesh_sdf_cuda_wheel" / "stable_abi_v1",
46
47
  )
47
48
  )
48
49
  os.environ["WITWIN_CORE_MESH_SDF_CUDA_BUILD_DIR"] = str(build_dir)
49
50
  os.environ["WITWIN_CORE_MESH_SDF_CUDA_SKIP_PREBUILT"] = "1"
50
51
  _ensure_current_device_arch()
51
52
 
52
- cuda_build = _load_cuda_build_module()
53
53
  module = cuda_build.build_extension(verbose=args.verbose)
54
54
  module_file = Path(module.__file__).resolve()
55
55
 
56
56
  target_dir = cuda_build.prebuilt_root()
57
57
  target_dir.mkdir(parents=True, exist_ok=True)
58
58
  for suffix in (".pyd", ".so"):
59
- existing = target_dir / f"witwin_core_mesh_sdf_cuda{suffix}"
59
+ existing = target_dir / f"{cuda_build.EXTENSION_NAME}{suffix}"
60
60
  if existing.exists():
61
61
  existing.unlink()
62
62
  target = cuda_build.prebuilt_extension_path()
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import subprocess
5
+ import tempfile
6
+ import zipfile
7
+ from pathlib import Path
8
+
9
+
10
+ EXPECTED_SASS = ("70", "75", "80", "86", "89", "90", "100", "101", "120")
11
+ EXPECTED_PTX_TARGET = "sm_120"
12
+
13
+
14
+ def _matches(path: Path, stems: tuple[str, ...]) -> bool:
15
+ return path.suffix in {".pyd", ".so"} and any(path.name.startswith(stem) for stem in stems)
16
+
17
+
18
+ def _collect_binaries(inputs: list[Path], stems: tuple[str, ...], extract_root: Path) -> list[Path]:
19
+ binaries: list[Path] = []
20
+ for input_path in inputs:
21
+ if input_path.suffix == ".whl":
22
+ with zipfile.ZipFile(input_path) as wheel:
23
+ for name in wheel.namelist():
24
+ member = Path(name)
25
+ if not _matches(member, stems):
26
+ continue
27
+ wheel.extract(name, extract_root)
28
+ binaries.append(extract_root / member)
29
+ continue
30
+ if input_path.is_dir():
31
+ binaries.extend(path for path in input_path.rglob("*") if _matches(path, stems))
32
+ continue
33
+ if _matches(input_path, stems):
34
+ binaries.append(input_path)
35
+ return sorted(set(path.resolve() for path in binaries))
36
+
37
+
38
+ def _cuobjdump(flag: str, binary: Path) -> str:
39
+ result = subprocess.run(
40
+ ["cuobjdump", flag, str(binary)],
41
+ check=True,
42
+ capture_output=True,
43
+ text=True,
44
+ )
45
+ return f"{result.stdout}\n{result.stderr}"
46
+
47
+
48
+ def main() -> None:
49
+ parser = argparse.ArgumentParser(description="Verify CUDA SASS and PTX targets in release binaries.")
50
+ parser.add_argument("inputs", nargs="+", type=Path)
51
+ parser.add_argument("--stem", action="append", required=True)
52
+ args = parser.parse_args()
53
+
54
+ with tempfile.TemporaryDirectory(prefix="witwin_cuda_arch_verify_") as temp_dir:
55
+ binaries = _collect_binaries(args.inputs, tuple(args.stem), Path(temp_dir))
56
+ if not binaries:
57
+ raise SystemExit(f"No native binaries matching {args.stem!r} were found.")
58
+
59
+ for binary in binaries:
60
+ elf_listing = _cuobjdump("--list-elf", binary)
61
+ missing_sass = [arch for arch in EXPECTED_SASS if f"sm_{arch}" not in elf_listing]
62
+ if missing_sass:
63
+ raise SystemExit(f"{binary} is missing SASS targets: {', '.join(missing_sass)}")
64
+
65
+ ptx_dump = _cuobjdump("--dump-ptx", binary)
66
+ if f".target {EXPECTED_PTX_TARGET}" not in ptx_dump:
67
+ raise SystemExit(f"{binary} is missing compute 12.0 PTX.")
68
+
69
+ print(f"Verified CUDA architectures in {binary}")
70
+
71
+
72
+ if __name__ == "__main__":
73
+ main()
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+
3
+ import pytest
4
+ import torch
5
+
6
+ from witwin.core import (
7
+ FrequencyMaterialSample,
8
+ GeometrySpec,
9
+ MaterialCapabilities,
10
+ MaterialSpec,
11
+ StaticMaterialSample,
12
+ Structure,
13
+ )
14
+
15
+
16
+ class DuckGeometry:
17
+ kind = "duck"
18
+
19
+ def to_mesh(self, segments: int = 16):
20
+ del segments
21
+ return torch.zeros((3, 3)), torch.tensor([[0, 1, 2]])
22
+
23
+
24
+ class DuckMaterial:
25
+ name = "duck"
26
+
27
+ def capabilities(self):
28
+ return MaterialCapabilities()
29
+
30
+ def evaluate_static(self):
31
+ return StaticMaterialSample(eps_r=2.0)
32
+
33
+ def evaluate_at_frequency(self, frequency: float):
34
+ del frequency
35
+ return FrequencyMaterialSample(eps_r=2.0)
36
+
37
+
38
+ def test_structure_accepts_protocol_conforming_values():
39
+ geometry = DuckGeometry()
40
+ material = DuckMaterial()
41
+
42
+ assert isinstance(geometry, GeometrySpec)
43
+ assert isinstance(material, MaterialSpec)
44
+ assert Structure(geometry=geometry, material=material).geometry is geometry
45
+
46
+
47
+ @pytest.mark.parametrize(
48
+ ("geometry", "material", "message"),
49
+ [
50
+ (object(), DuckMaterial(), "GeometrySpec"),
51
+ (DuckGeometry(), object(), "MaterialSpec"),
52
+ ],
53
+ )
54
+ def test_structure_rejects_values_outside_shared_contracts(geometry, material, message):
55
+ with pytest.raises(TypeError, match=message):
56
+ Structure(geometry=geometry, material=material)
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ import tomllib
4
+ from pathlib import Path
5
+
6
+
7
+ def test_runtime_and_optional_dependencies_do_not_include_slangtorch():
8
+ pyproject = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
9
+ groups = [pyproject["project"]["dependencies"]]
10
+ groups.extend(pyproject["project"].get("optional-dependencies", {}).values())
11
+ assert all(
12
+ not dependency.lower().startswith("slangtorch")
13
+ for group in groups
14
+ for dependency in group
15
+ )
@@ -4,7 +4,6 @@ import pytest
4
4
  import torch
5
5
 
6
6
  from witwin.core import Box, Cone, Cylinder, Ellipsoid, HollowBox, Mesh, Prism, Pyramid, Sphere, Torus
7
- from witwin.core.geometry import ComplexPolySlab, PolySlab
8
7
  from witwin.core.geometry.mesh_sdf import (
9
8
  _mesh_sdf_available,
10
9
  _triangle_mesh_smooth_signed_distance_torch,
@@ -152,39 +151,6 @@ def _require_cuda_mesh_sdf():
152
151
  (0.5, 0.0, 0.0),
153
152
  (0.7, 0.0, 0.0),
154
153
  ),
155
- (
156
- PolySlab(
157
- vertices=[(-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)],
158
- bounds=(-0.5, 0.5),
159
- axis="z",
160
- ),
161
- (0.0, 0.0, 0.0),
162
- (0.5, 0.0, 0.0),
163
- (0.7, 0.0, 0.0),
164
- ),
165
- (
166
- # Non-convex L-shape: the notch quadrant must be outside.
167
- PolySlab(
168
- vertices=[(-0.5, -0.5), (0.5, -0.5), (0.5, 0.0), (0.0, 0.0), (0.0, 0.5), (-0.5, 0.5)],
169
- bounds=(-0.5, 0.5),
170
- axis="z",
171
- ),
172
- (-0.25, -0.25, 0.0),
173
- (0.5, -0.25, 0.0),
174
- (0.25, 0.25, 0.0),
175
- ),
176
- (
177
- ComplexPolySlab(
178
- loops=[
179
- [(-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)],
180
- [(-0.2, -0.2), (0.2, -0.2), (0.2, 0.2), (-0.2, 0.2)],
181
- ],
182
- bounds=(-0.5, 0.5),
183
- ),
184
- (0.35, 0.0, 0.0),
185
- (0.5, 0.0, 0.0),
186
- (0.0, 0.0, 0.0),
187
- ),
188
154
  ],
189
155
  )
190
156
  def test_primitive_signed_distance_has_expected_sign(geometry, inside_point, surface_point, outside_point):
@@ -214,16 +180,6 @@ def test_primitive_signed_distance_has_expected_sign(geometry, inside_point, sur
214
180
  (Cone(radius=0.4, height=1.0, axis="z"), (0.0, 0.0, 0.5), (0.2, 0.0, 0.5), (0.3, 0.0, 0.5)),
215
181
  (Pyramid(base_size=1.0, height=1.0, axis="z"), (0.0, 0.0, 0.5), (0.25, 0.0, 0.5), (0.35, 0.0, 0.5)),
216
182
  (Prism(radius=0.4, height=1.0, num_sides=5, axis="z"), (0.0, 0.0, 0.0), (0.4, 0.0, 0.0), (0.6, 0.0, 0.0)),
217
- (
218
- PolySlab(
219
- vertices=[(-0.4, -0.4), (0.4, -0.4), (0.4, 0.4), (-0.4, 0.4)],
220
- bounds=(-0.5, 0.5),
221
- axis="z",
222
- ),
223
- (0.0, 0.0, 0.0),
224
- (0.4, 0.0, 0.0),
225
- (0.7, 0.0, 0.0),
226
- ),
227
183
  ],
228
184
  )
229
185
  def test_primitive_occupancy_is_monotone_and_bounded(geometry, inside_point, boundary_point, outside_point):
@@ -371,27 +327,6 @@ def test_prism_occupancy_has_gradients_for_position_radius_height_and_rotation()
371
327
  assert abs(grad.item()) > 1.0e-6
372
328
 
373
329
 
374
- def test_polyslab_occupancy_has_gradients_for_vertices_bounds_and_sidewall_angle():
375
- xx, yy, zz = _asymmetric_grid()
376
- vertices = torch.nn.Parameter(
377
- torch.tensor([[-0.31, -0.24], [0.29, -0.24], [0.29, 0.26], [-0.31, 0.26]], dtype=torch.float32)
378
- )
379
- bounds = torch.nn.Parameter(torch.tensor([-0.28, 0.17], dtype=torch.float32))
380
- sidewall_angle = torch.nn.Parameter(torch.tensor(0.10, dtype=torch.float32))
381
-
382
- geometry = PolySlab(vertices, bounds, axis="z", sidewall_angle=sidewall_angle)
383
- loss = geometry.to_mask(xx, yy, zz, beta=0.06).sum()
384
- loss.backward()
385
-
386
- for grad in (vertices.grad, bounds.grad):
387
- assert grad is not None
388
- assert torch.all(torch.isfinite(grad))
389
- assert torch.any(grad.abs() > 1.0e-6)
390
- assert sidewall_angle.grad is not None
391
- assert torch.isfinite(sidewall_angle.grad)
392
- assert abs(sidewall_angle.grad.item()) > 1.0e-6
393
-
394
-
395
330
  def test_mesh_signed_distance_matches_box_sign_for_closed_cube():
396
331
  vertices, faces = _cube_mesh(size=1.0)
397
332
  mesh = Mesh(vertices, faces, recenter=False, fill_mode="solid")