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.
- {witwin-0.0.2 → witwin-0.3.0}/.github/workflows/publish-witwin.yml +115 -19
- {witwin-0.0.2 → witwin-0.3.0}/.gitignore +0 -3
- witwin-0.3.0/PKG-INFO +63 -0
- witwin-0.3.0/README.md +40 -0
- {witwin-0.0.2 → witwin-0.3.0}/hatch_build.py +3 -1
- witwin-0.3.0/pyproject.toml +42 -0
- {witwin-0.0.2 → witwin-0.3.0}/scripts/build_mesh_sdf_cuda_prebuilt.py +3 -3
- witwin-0.3.0/scripts/verify_cuda_binary_arches.py +73 -0
- witwin-0.3.0/tests/test_contracts.py +56 -0
- witwin-0.3.0/tests/test_dependency_contract.py +15 -0
- {witwin-0.0.2 → witwin-0.3.0}/tests/test_geometry_sdf.py +0 -65
- {witwin-0.0.2 → witwin-0.3.0}/tests/test_geometry_smoke.py +19 -49
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/__init__.py +4 -21
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/__init__.py +4 -7
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/base.py +12 -0
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/cuda/backend.py +1 -1
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/cuda/build.py +56 -15
- witwin-0.3.0/witwin/core/geometry/cuda/extension.cpp +29 -0
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/cuda/mesh_sdf_kernels.cu +129 -72
- witwin-0.3.0/witwin/core/geometry/polygon.py +91 -0
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/primitives.py +4 -300
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/material.py +54 -103
- witwin-0.0.2/PKG-INFO +0 -53
- witwin-0.0.2/README.md +0 -30
- witwin-0.0.2/pyproject.toml +0 -42
- witwin-0.0.2/tests/test_scene_base.py +0 -18
- witwin-0.0.2/witwin/core/geometry/cuda/extension.cpp +0 -43
- witwin-0.0.2/witwin/core/geometry/smpl.py +0 -229
- witwin-0.0.2/witwin/core/scene.py +0 -38
- witwin-0.0.2/witwin/core/scene_to_mitsuba.py +0 -138
- {witwin-0.0.2 → witwin-0.3.0}/benchmark/benchmark_mesh_sdf.py +0 -0
- {witwin-0.0.2 → witwin-0.3.0}/tests/conftest.py +0 -0
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/cuda/__init__.py +0 -0
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/mesh.py +0 -0
- {witwin-0.0.2 → witwin-0.3.0}/witwin/core/geometry/mesh_sdf.py +0 -0
- {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 }}
|
|
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:
|
|
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.
|
|
115
|
+
uses: Jimver/cuda-toolkit@v0.2.35
|
|
117
116
|
with:
|
|
118
|
-
cuda: "12.
|
|
117
|
+
cuda: "12.8.1"
|
|
119
118
|
method: local
|
|
120
|
-
log-file-suffix: "${{ runner.os }}-
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
179
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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 }}
|
|
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:
|
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
|
-
|
|
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"
|
|
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")
|