raystrack 1.0.1__tar.gz → 2.0.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.
- raystrack-2.0.0/LICENSE +21 -0
- raystrack-2.0.0/PKG-INFO +283 -0
- raystrack-2.0.0/README.md +261 -0
- {raystrack-1.0.1 → raystrack-2.0.0}/pyproject.toml +9 -5
- raystrack-2.0.0/src/raystrack/__init__.py +11 -0
- raystrack-2.0.0/src/raystrack/backends/__init__.py +8 -0
- raystrack-2.0.0/src/raystrack/backends/area_pair.py +142 -0
- raystrack-2.0.0/src/raystrack/backends/calibration.py +231 -0
- raystrack-2.0.0/src/raystrack/backends/cuda_fused.py +251 -0
- raystrack-2.0.0/src/raystrack/backends/devices.py +61 -0
- raystrack-2.0.0/src/raystrack/backends/launch.py +22 -0
- raystrack-2.0.0/src/raystrack/backends/workspaces.py +185 -0
- raystrack-2.0.0/src/raystrack/engine/__init__.py +1 -0
- raystrack-2.0.0/src/raystrack/engine/controls.py +23 -0
- raystrack-2.0.0/src/raystrack/engine/postprocessing.py +52 -0
- raystrack-2.0.0/src/raystrack/engine/sampling.py +77 -0
- raystrack-2.0.0/src/raystrack/engine/scheduler.py +408 -0
- raystrack-2.0.0/src/raystrack/integrations/__init__.py +4 -0
- raystrack-2.0.0/src/raystrack/integrations/grasshopper/__init__.py +7 -0
- raystrack-2.0.0/src/raystrack/integrations/grasshopper/serialization.py +301 -0
- raystrack-2.0.0/src/raystrack/integrations/grasshopper/worker.py +447 -0
- raystrack-2.0.0/src/raystrack/integrations/rhino.py +48 -0
- raystrack-2.0.0/src/raystrack/io/__init__.py +245 -0
- raystrack-2.0.0/src/raystrack/io/_arrays.py +160 -0
- raystrack-2.0.0/src/raystrack/io/_v1.py +261 -0
- raystrack-2.0.0/src/raystrack/model/__init__.py +5 -0
- raystrack-2.0.0/src/raystrack/model/geometry.py +155 -0
- raystrack-2.0.0/src/raystrack/model/scene.py +209 -0
- raystrack-2.0.0/src/raystrack/solver/__init__.py +7 -0
- raystrack-2.0.0/src/raystrack/solver/options.py +119 -0
- raystrack-2.0.0/src/raystrack/solver/query.py +82 -0
- raystrack-2.0.0/src/raystrack/solver/result.py +204 -0
- raystrack-2.0.0/src/raystrack/solver/runtime.py +346 -0
- raystrack-2.0.0/src/raystrack/solver/snapshot.py +79 -0
- raystrack-2.0.0/src/raystrack/utils/__init__.py +1 -0
- raystrack-2.0.0/src/raystrack/utils/bvh.py +170 -0
- {raystrack-1.0.1 → raystrack-2.0.0}/src/raystrack/utils/cpu_trace.py +106 -0
- {raystrack-1.0.1 → raystrack-2.0.0}/src/raystrack/utils/cuda_trace.py +102 -6
- raystrack-2.0.0/src/raystrack/utils/helpers.py +35 -0
- raystrack-2.0.0/src/raystrack/utils/instancing.py +258 -0
- raystrack-2.0.0/src/raystrack/utils/prepared.py +928 -0
- {raystrack-1.0.1 → raystrack-2.0.0}/src/raystrack/utils/ray_builder.py +8 -6
- raystrack-2.0.0/src/raystrack/utils/taichi_trace.py +663 -0
- raystrack-2.0.0/src/raystrack.egg-info/PKG-INFO +283 -0
- raystrack-2.0.0/src/raystrack.egg-info/SOURCES.txt +69 -0
- raystrack-2.0.0/src/raystrack.egg-info/requires.txt +8 -0
- raystrack-2.0.0/tests/test_adaptive_execution.py +156 -0
- raystrack-2.0.0/tests/test_cuda_fused_v2.py +164 -0
- raystrack-2.0.0/tests/test_dynamic.py +270 -0
- raystrack-2.0.0/tests/test_execution.py +132 -0
- raystrack-2.0.0/tests/test_gpu_sim.py +90 -0
- raystrack-2.0.0/tests/test_grasshopper_contract.py +110 -0
- raystrack-2.0.0/tests/test_grasshopper_packaging.py +133 -0
- raystrack-2.0.0/tests/test_grasshopper_worker.py +537 -0
- raystrack-2.0.0/tests/test_instancing.py +296 -0
- raystrack-2.0.0/tests/test_io_v2.py +261 -0
- raystrack-2.0.0/tests/test_model_v2.py +272 -0
- raystrack-2.0.0/tests/test_preview.py +191 -0
- raystrack-2.0.0/tests/test_public_repr.py +74 -0
- raystrack-2.0.0/tests/test_refinement.py +288 -0
- raystrack-2.0.0/tests/test_self_viewing.py +110 -0
- raystrack-2.0.0/tests/test_solver_v2.py +530 -0
- raystrack-2.0.0/tests/test_store.py +219 -0
- raystrack-2.0.0/tests/test_taichi.py +402 -0
- raystrack-2.0.0/tests/test_tuning.py +161 -0
- raystrack-2.0.0/tests/test_visibility.py +135 -0
- raystrack-1.0.1/LICENSE +0 -674
- raystrack-1.0.1/PKG-INFO +0 -125
- raystrack-1.0.1/README.md +0 -107
- raystrack-1.0.1/src/raystrack/__init__.py +0 -30
- raystrack-1.0.1/src/raystrack/api.py +0 -198
- raystrack-1.0.1/src/raystrack/io.py +0 -238
- raystrack-1.0.1/src/raystrack/main.py +0 -2194
- raystrack-1.0.1/src/raystrack/params.py +0 -129
- raystrack-1.0.1/src/raystrack/utils/__init__.py +0 -23
- raystrack-1.0.1/src/raystrack/utils/bvh.py +0 -74
- raystrack-1.0.1/src/raystrack/utils/geometry.py +0 -66
- raystrack-1.0.1/src/raystrack/utils/helpers.py +0 -284
- raystrack-1.0.1/src/raystrack/utils/prepared.py +0 -442
- raystrack-1.0.1/src/raystrack.egg-info/PKG-INFO +0 -125
- raystrack-1.0.1/src/raystrack.egg-info/SOURCES.txt +0 -23
- raystrack-1.0.1/src/raystrack.egg-info/requires.txt +0 -2
- {raystrack-1.0.1 → raystrack-2.0.0}/setup.cfg +0 -0
- {raystrack-1.0.1 → raystrack-2.0.0}/setup.py +0 -0
- {raystrack-1.0.1 → raystrack-2.0.0}/src/raystrack/utils/halton.py +0 -0
- {raystrack-1.0.1 → raystrack-2.0.0}/src/raystrack.egg-info/dependency_links.txt +0 -0
- {raystrack-1.0.1 → raystrack-2.0.0}/src/raystrack.egg-info/top_level.txt +0 -0
raystrack-2.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Philip Balizki
|
|
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.
|
raystrack-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: raystrack
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Reusable scene and solver API for Monte Carlo view factors on CPU, CUDA and portable GPUs
|
|
5
|
+
Author-email: Philip Balizki <philip@metis.earth>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/philip-ba/raystrack
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering
|
|
12
|
+
Requires-Python: <3.14,>=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: numpy<3.0,>=1.24
|
|
16
|
+
Requires-Dist: numba<0.65,>=0.60
|
|
17
|
+
Provides-Extra: portable-gpu
|
|
18
|
+
Requires-Dist: taichi<1.8,>=1.7.4; extra == "portable-gpu"
|
|
19
|
+
Provides-Extra: cuda
|
|
20
|
+
Requires-Dist: numba-cuda>=0.20; extra == "cuda"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# Raystrack
|
|
24
|
+
|
|
25
|
+
<img src="https://raw.githubusercontent.com/philip-ba/raystrack/main/raystrack_icon.svg" alt="Raystrack" width="160">
|
|
26
|
+
|
|
27
|
+
Raystrack computes radiative view factors between triangulated surfaces using
|
|
28
|
+
quasi Monte Carlo ray tracing. Version 2.0.0 has a unified API built
|
|
29
|
+
around `Scene`, `Solver`, `Query`, `Run`, and immutable `Result` snapshots.
|
|
30
|
+
The v1 calculation functions have been replaced. See the
|
|
31
|
+
[migration guide](https://github.com/philip-ba/raystrack/blob/main/docs/v2-migration.md) before upgrading an existing script.
|
|
32
|
+
|
|
33
|
+
The [Grasshopper integration](https://github.com/philip-ba/raystrack/blob/main/docs/grasshopper.md) lives in this repository too.
|
|
34
|
+
It provides compiled **RS** components, live background solves, a Raystrack
|
|
35
|
+
ribbon icon, and a bundled Python runtime through Yak or a standalone ZIP.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
pip install "raystrack==2.0.0"
|
|
41
|
+
pip install "raystrack[portable-gpu]==2.0.0" # optional Taichi Vulkan/Metal
|
|
42
|
+
pip install "raystrack[cuda]==2.0.0" # optional NVIDIA CUDA
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
In Rhino 8.35 or later on Windows, open **PackageManager**, search for
|
|
46
|
+
**raystrack**, and install version **2.0.0**. Restart Rhino, then open
|
|
47
|
+
Grasshopper's **Raystrack** tab. The Yak package includes its Python runtime.
|
|
48
|
+
The [Grasshopper guide](https://github.com/philip-ba/raystrack/blob/main/docs/grasshopper.md) covers components and examples.
|
|
49
|
+
|
|
50
|
+
To install from a source checkout:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
pip install .
|
|
54
|
+
pip install ".[portable-gpu]" # optional Taichi Vulkan/Metal
|
|
55
|
+
pip install ".[cuda]" # optional NVIDIA CUDA target for Numba
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The package requires Python 3.9–3.13, NumPy, and Numba. CPU tracing works without
|
|
59
|
+
optional GPU dependencies. `device="cpu"`, `"cuda"`, `"vulkan"`, or `"metal"`
|
|
60
|
+
chooses a backend explicitly; unavailable GPU requests raise availability errors.
|
|
61
|
+
`"gpu"` requires an available GPU, and `"taichi"` chooses a portable GPU runtime.
|
|
62
|
+
`"auto"` can select CPU or GPU. These are compute tracers with software BVHs;
|
|
63
|
+
they do not use hardware ray tracing extensions.
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from raystrack import available_devices
|
|
67
|
+
print(available_devices())
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Vulkan has been tested on an AMD Radeon 860M. Metal, Intel GPUs, and physical
|
|
71
|
+
CUDA hardware need their own validation; CUDA simulator checks verify kernel
|
|
72
|
+
behavior rather than performance. Taichi reuses one compatible FP32 runtime per
|
|
73
|
+
process and serializes access. Set `TI_VISIBLE_DEVICE` before first GPU use when
|
|
74
|
+
choosing a Taichi adapter.
|
|
75
|
+
|
|
76
|
+
## Quick start
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import numpy as np
|
|
80
|
+
from raystrack import (
|
|
81
|
+
Mesh, Scene, Solver, Query, SolveOptions, Sampling, Accuracy,
|
|
82
|
+
Budget, Channel,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
vertices = np.array([[-1,-1,0], [1,-1,0], [1,1,0], [-1,1,0]], np.float32)
|
|
86
|
+
faces = np.array([[0,1,2], [0,2,3]], np.int32)
|
|
87
|
+
scene = Scene.from_meshes({
|
|
88
|
+
"lower": Mesh(vertices, faces),
|
|
89
|
+
"upper": Mesh(vertices + [0,0,1], faces[:, ::-1]),
|
|
90
|
+
})
|
|
91
|
+
options = SolveOptions(
|
|
92
|
+
sampling=Sampling(density=16, rays_per_cell=128, seed=11),
|
|
93
|
+
accuracy=Accuracy(max_replicates=20, min_replicates=5, tolerance=1e-4),
|
|
94
|
+
batch_size=65536,
|
|
95
|
+
)
|
|
96
|
+
with Solver(scene, device="cpu", bvh="builtin") as solver:
|
|
97
|
+
result = solver.solve(Query.row("lower", sky="merged"), options,
|
|
98
|
+
Budget(rays=65536))
|
|
99
|
+
|
|
100
|
+
print(result.value("lower", Channel("surface", "upper", "front")))
|
|
101
|
+
print(result.value("lower", Channel("sky")))
|
|
102
|
+
print(result.status, result.rays_used, result.cumulative_rays)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`Mesh` owns immutable float32 vertices and int32 triangle faces. Winding defines
|
|
106
|
+
the front side. Surface IDs are unique strings independent of display labels;
|
|
107
|
+
IDs containing `_front`, spaces, or Unicode stay unambiguous. `Scene` owns
|
|
108
|
+
ordered surfaces and rigid instance transforms. Editing a label leaves the
|
|
109
|
+
geometry revision unchanged.
|
|
110
|
+
|
|
111
|
+
## Choose outputs and sampling
|
|
112
|
+
|
|
113
|
+
`Query.matrix()` requests all surface rows. `Query.row("wall")` requests one
|
|
114
|
+
sender, and `Query.pair("wall", "roof")` selects one receiver. Receiver selection
|
|
115
|
+
filters outputs; every surface can still occlude rays. `receiver_sides` can
|
|
116
|
+
select `("front",)`, `("back",)`, or both.
|
|
117
|
+
|
|
118
|
+
Add `sky="merged"` or `sky="tregenza145"` to a scene query to trace sky with the
|
|
119
|
+
same rays. `Query.sky(senders=["wall"], discrete=True)` requests only sky outputs.
|
|
120
|
+
Sky patches are indexed `0..144` through `Channel("sky", patch=index)`.
|
|
121
|
+
|
|
122
|
+
`SolveOptions` holds one immutable configuration for a query:
|
|
123
|
+
|
|
124
|
+
- `Sampling`: emission density, rays per cell, seed, face reversal, fair/adaptive
|
|
125
|
+
allocation, and estimator strategy.
|
|
126
|
+
- `Accuracy`: replicate cap, minimum replicate/ray floors, tolerance, and
|
|
127
|
+
convergence check interval.
|
|
128
|
+
- `Postprocessing`: explicit reciprocity transformation after complete solves.
|
|
129
|
+
- `batch_size`: a cap for bounded trace chunks.
|
|
130
|
+
|
|
131
|
+
The default cosine strategy uses independently shifted Halton replicates.
|
|
132
|
+
`mode="fair"` serves each selected sender; `"adaptive"` gives more work to rows
|
|
133
|
+
with larger uncertainty after initial exploration. A minimum ray floor reduces
|
|
134
|
+
zero-hit early stopping but does not prove that rare receivers were observed.
|
|
135
|
+
|
|
136
|
+
For a small selected receiver, use `Sampling(strategy="area_pair",
|
|
137
|
+
pair_samples=8192, sequence="shifted_halton")` with `Query.pair(...)`.
|
|
138
|
+
Unsupported estimator/output combinations raise `CapabilityError`. Area-pair
|
|
139
|
+
sampling weights uniformly sampled surface pairs by their cosine and
|
|
140
|
+
inverse-distance factors and checks visibility against all occluders. It supports
|
|
141
|
+
CPU only; unsupported GPU, sky, or reciprocity combinations raise explicitly.
|
|
142
|
+
`sequence="random"` is also available for this estimator.
|
|
143
|
+
|
|
144
|
+
Meshes can see and occlude themselves: other faces of the emitting surface
|
|
145
|
+
participate in tracing, and `Query.pair("box", "box")` selects its self view
|
|
146
|
+
factor. Matrix/row queries include the same channels. For a closed box with
|
|
147
|
+
outward mesh normals, use `Sampling(flip_faces=True)` to emit into its interior;
|
|
148
|
+
the self factor is 1 on `Channel("surface", "box", "back")`, with zero escape.
|
|
149
|
+
With inward normals, use the front channel and leave `flip_faces=False`.
|
|
150
|
+
Receiver side labels always follow the stored mesh winding.
|
|
151
|
+
|
|
152
|
+
Reciprocity is opt-in through `Postprocessing("bidirectional")` or
|
|
153
|
+
`Postprocessing("shortcut")`, both requiring complete sender/receiver tables.
|
|
154
|
+
The shortcut transformation preserves the v1 upper-direction estimate but v2
|
|
155
|
+
still traces every requested row; it does not halve the workload. Transformed
|
|
156
|
+
uncertainties are unknown and the transformation is recorded in provenance.
|
|
157
|
+
`"rowsum"` additionally requires a closed enclosure without escape or sky.
|
|
158
|
+
Raw previews use `Postprocessing("none")`, the default.
|
|
159
|
+
|
|
160
|
+
## Moving scenes and continued work
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
with Solver(scene, device="cpu", acceleration="instanced") as solver:
|
|
164
|
+
query = Query.row("lower", sky="merged")
|
|
165
|
+
run = solver.start(query, options)
|
|
166
|
+
first = run.advance(Budget(rays=4096))
|
|
167
|
+
refined = run.advance(Budget(rays=12288)) # only additional rays
|
|
168
|
+
assert refined.cumulative_rays == 16384
|
|
169
|
+
|
|
170
|
+
transform = np.eye(4)
|
|
171
|
+
transform[0, 3] = 2
|
|
172
|
+
scene.update_transform("upper", transform)
|
|
173
|
+
new_frame = solver.start(query, options).advance(Budget(rays=4096))
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Transforms are absolute proper rotations and translations relative to the local
|
|
177
|
+
mesh; scale, shear, reflection, invalid matrices, and float32 overflow are
|
|
178
|
+
rejected. `scene.update_vertices(id, world_vertices)` retains topology and resets
|
|
179
|
+
the transform. `scene.update_mesh(id, Mesh(...))` replaces geometry and resets
|
|
180
|
+
the transform. Updates invalidate existing runs before waiting for active work.
|
|
181
|
+
A new run starts a new stream for the new `scene.revision`; old results remain
|
|
182
|
+
immutable snapshots labeled with their original revision.
|
|
183
|
+
|
|
184
|
+
Flat acceleration refits packed triangle bounds when topology is unchanged.
|
|
185
|
+
`acceleration="instanced"` keeps local object BVHs and updates instance transforms
|
|
186
|
+
and a top-level hierarchy. Its hierarchy refits and rebuilds when its measured
|
|
187
|
+
bound cost deteriorates. World emitter frames are prepared lazily; emitter work
|
|
188
|
+
can still scale with changed triangle counts. Share local geometry explicitly:
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
prototype = Mesh(vertices, faces)
|
|
192
|
+
instances = Scene.from_instances({"panel": prototype}, [
|
|
193
|
+
("panel0", "panel", np.eye(4)),
|
|
194
|
+
("panel1", "panel", transform),
|
|
195
|
+
])
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Rigid updates retain local geometry/BVH buffers on CPU, Vulkan/Metal, and CUDA.
|
|
199
|
+
Deformation refreshes the affected object, and topology changes rebuild it.
|
|
200
|
+
Instancing reduces update/storage work; tracing speed depends on the scene.
|
|
201
|
+
|
|
202
|
+
`Run.advance` preserves exact ray offsets and completed replicate statistics.
|
|
203
|
+
Accuracy, postprocessing, and batch size can change on a stationary run; sampling
|
|
204
|
+
settings require a new run. Solver configuration and run queries are read-only;
|
|
205
|
+
create a new solver to change the backend. Use `advance(options=...)` to adjust
|
|
206
|
+
accuracy. Ray budgets are additional work. Time budgets are
|
|
207
|
+
soft deadlines checked between chunks and include preparation; cold compilation
|
|
208
|
+
can exceed them. `Budget(rays=0)` returns the current estimate without tracing.
|
|
209
|
+
|
|
210
|
+
`Run.submit(...)` returns a future. Repeated submissions queue additive advances
|
|
211
|
+
through one solver worker. Cancel an obsolete run explicitly before replacing
|
|
212
|
+
it. `Run.cancel()` is terminal: later advances return a cancelled snapshot with
|
|
213
|
+
zero additional rays. Cancellation retains partial estimates. A geometry change
|
|
214
|
+
marks the run `invalidated`; later advances raise and require a new run. Solver
|
|
215
|
+
and run context managers close resources.
|
|
216
|
+
|
|
217
|
+
## Read results and uncertainty
|
|
218
|
+
|
|
219
|
+
`Result.row(sender)` maps typed channels to estimates. Surface, sky, `rest`, and
|
|
220
|
+
`unrequested` channels avoid suffix parsing. Rest contains escaped rays outside
|
|
221
|
+
requested sky; unrequested contains hits omitted by receiver/side selection.
|
|
222
|
+
For cosine queries these channels preserve raw energy accounting.
|
|
223
|
+
|
|
224
|
+
`coverage` aligns with `sender_ids`: `0` means not started, `1` means sampled,
|
|
225
|
+
and `-1` means coverage is unknown in imported data. A requested zero in a
|
|
226
|
+
sampled row is known zero; an unsampled value is `None`. Sparse arrays hold
|
|
227
|
+
estimates and errors, while `.dense()` exports a mutable copy.
|
|
228
|
+
|
|
229
|
+
Estimates include partial replicates. `Result.error(...)` is `None` until there
|
|
230
|
+
are at least two complete randomized replicates and no pending partial replicate.
|
|
231
|
+
`statistics["emitters"]` retains completed-replicate standard errors, sample
|
|
232
|
+
counts, pending rays, and convergence details. Partial work cannot claim
|
|
233
|
+
convergence. Error estimates describe sampling variation, not mesh or floating
|
|
234
|
+
point error, and zero observed hits can underestimate rare-event uncertainty.
|
|
235
|
+
|
|
236
|
+
## Calibration and storage
|
|
237
|
+
|
|
238
|
+
`solver.warmup(query, options)` compiles and measures representative synchronized
|
|
239
|
+
CPU/GPU tracing. Probe rays are diagnostic and do not enter results or ray
|
|
240
|
+
budgets. An unrestricted `Solver.solve` may calibrate automatically; bounded
|
|
241
|
+
runs use cached/provisional plans. Resumed runs retain their numerical backend.
|
|
242
|
+
Warm up before interactive deadlines, or use an explicit device with
|
|
243
|
+
`auto_tune=False` for a fixed execution choice.
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
from raystrack.io import save, load, import_v1_json
|
|
247
|
+
|
|
248
|
+
save("case.raystrack", scene, new_frame, metadata={"case": "moving wall"})
|
|
249
|
+
stored = load("case.raystrack")
|
|
250
|
+
legacy = import_v1_json("examples/street_canyon.json", "examples/vf_matrix.json")
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Stores are versioned directories with chunked arrays and a manifest published
|
|
254
|
+
last. Existing stores are never overwritten. V2 preserves stable IDs, shared
|
|
255
|
+
geometry, transforms, sparse channels, statistics, execution, and provenance.
|
|
256
|
+
`load` also reads v1 stores. Legacy imports preserve unknown sampling coverage
|
|
257
|
+
and uncertainty instead of inventing counts. V2 currently loads a full immutable
|
|
258
|
+
snapshot; selective on-disk row loading and appendable writers are unavailable.
|
|
259
|
+
|
|
260
|
+
Rhino conversion is isolated in `raystrack.integrations.rhino` through
|
|
261
|
+
`from_rhino_mesh` and `from_rhino_scene`; importing the numerical package does
|
|
262
|
+
not require Rhino. The converter triangulates quads and copies vertices.
|
|
263
|
+
|
|
264
|
+
## Examples and verification
|
|
265
|
+
|
|
266
|
+
Run examples directly from this checkout, beginning with
|
|
267
|
+
`python examples/ex00_street_canyon_geometry.py`. Examples also build the canyon
|
|
268
|
+
in memory, so generated geometry files are optional. `ex06_dynamic_preview.py`
|
|
269
|
+
accepts `--device`, `--acceleration`, `--budget`, `--frames`, and `--warmup`.
|
|
270
|
+
|
|
271
|
+
```sh
|
|
272
|
+
PYTHONPATH=src python -m pytest tests -q
|
|
273
|
+
python validation/validate_dynamic_backends.py --devices cpu vulkan --accelerations flat instanced
|
|
274
|
+
python validation/benchmark_v2_vs_v1.py --gpu-device vulkan
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The [v2 accuracy report](validation/results/v2_dynamic_accuracy.json) passes 72
|
|
278
|
+
closed-form comparisons across CPU/Vulkan and flat/instanced traversal. The
|
|
279
|
+
[validation guide](validation/readme.md) describes larger analytical checks,
|
|
280
|
+
optional benchmarks and their limits. Performance timing validation was skipped
|
|
281
|
+
at the user's request because background simulations made measurements
|
|
282
|
+
unreliable; this change makes no speedup claim. The [migration guide](https://github.com/philip-ba/raystrack/blob/main/docs/v2-migration.md)
|
|
283
|
+
shows v1-to-v2 replacements.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# Raystrack
|
|
2
|
+
|
|
3
|
+
<img src="https://raw.githubusercontent.com/philip-ba/raystrack/main/raystrack_icon.svg" alt="Raystrack" width="160">
|
|
4
|
+
|
|
5
|
+
Raystrack computes radiative view factors between triangulated surfaces using
|
|
6
|
+
quasi Monte Carlo ray tracing. Version 2.0.0 has a unified API built
|
|
7
|
+
around `Scene`, `Solver`, `Query`, `Run`, and immutable `Result` snapshots.
|
|
8
|
+
The v1 calculation functions have been replaced. See the
|
|
9
|
+
[migration guide](https://github.com/philip-ba/raystrack/blob/main/docs/v2-migration.md) before upgrading an existing script.
|
|
10
|
+
|
|
11
|
+
The [Grasshopper integration](https://github.com/philip-ba/raystrack/blob/main/docs/grasshopper.md) lives in this repository too.
|
|
12
|
+
It provides compiled **RS** components, live background solves, a Raystrack
|
|
13
|
+
ribbon icon, and a bundled Python runtime through Yak or a standalone ZIP.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pip install "raystrack==2.0.0"
|
|
19
|
+
pip install "raystrack[portable-gpu]==2.0.0" # optional Taichi Vulkan/Metal
|
|
20
|
+
pip install "raystrack[cuda]==2.0.0" # optional NVIDIA CUDA
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
In Rhino 8.35 or later on Windows, open **PackageManager**, search for
|
|
24
|
+
**raystrack**, and install version **2.0.0**. Restart Rhino, then open
|
|
25
|
+
Grasshopper's **Raystrack** tab. The Yak package includes its Python runtime.
|
|
26
|
+
The [Grasshopper guide](https://github.com/philip-ba/raystrack/blob/main/docs/grasshopper.md) covers components and examples.
|
|
27
|
+
|
|
28
|
+
To install from a source checkout:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
pip install .
|
|
32
|
+
pip install ".[portable-gpu]" # optional Taichi Vulkan/Metal
|
|
33
|
+
pip install ".[cuda]" # optional NVIDIA CUDA target for Numba
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The package requires Python 3.9–3.13, NumPy, and Numba. CPU tracing works without
|
|
37
|
+
optional GPU dependencies. `device="cpu"`, `"cuda"`, `"vulkan"`, or `"metal"`
|
|
38
|
+
chooses a backend explicitly; unavailable GPU requests raise availability errors.
|
|
39
|
+
`"gpu"` requires an available GPU, and `"taichi"` chooses a portable GPU runtime.
|
|
40
|
+
`"auto"` can select CPU or GPU. These are compute tracers with software BVHs;
|
|
41
|
+
they do not use hardware ray tracing extensions.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from raystrack import available_devices
|
|
45
|
+
print(available_devices())
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Vulkan has been tested on an AMD Radeon 860M. Metal, Intel GPUs, and physical
|
|
49
|
+
CUDA hardware need their own validation; CUDA simulator checks verify kernel
|
|
50
|
+
behavior rather than performance. Taichi reuses one compatible FP32 runtime per
|
|
51
|
+
process and serializes access. Set `TI_VISIBLE_DEVICE` before first GPU use when
|
|
52
|
+
choosing a Taichi adapter.
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import numpy as np
|
|
58
|
+
from raystrack import (
|
|
59
|
+
Mesh, Scene, Solver, Query, SolveOptions, Sampling, Accuracy,
|
|
60
|
+
Budget, Channel,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
vertices = np.array([[-1,-1,0], [1,-1,0], [1,1,0], [-1,1,0]], np.float32)
|
|
64
|
+
faces = np.array([[0,1,2], [0,2,3]], np.int32)
|
|
65
|
+
scene = Scene.from_meshes({
|
|
66
|
+
"lower": Mesh(vertices, faces),
|
|
67
|
+
"upper": Mesh(vertices + [0,0,1], faces[:, ::-1]),
|
|
68
|
+
})
|
|
69
|
+
options = SolveOptions(
|
|
70
|
+
sampling=Sampling(density=16, rays_per_cell=128, seed=11),
|
|
71
|
+
accuracy=Accuracy(max_replicates=20, min_replicates=5, tolerance=1e-4),
|
|
72
|
+
batch_size=65536,
|
|
73
|
+
)
|
|
74
|
+
with Solver(scene, device="cpu", bvh="builtin") as solver:
|
|
75
|
+
result = solver.solve(Query.row("lower", sky="merged"), options,
|
|
76
|
+
Budget(rays=65536))
|
|
77
|
+
|
|
78
|
+
print(result.value("lower", Channel("surface", "upper", "front")))
|
|
79
|
+
print(result.value("lower", Channel("sky")))
|
|
80
|
+
print(result.status, result.rays_used, result.cumulative_rays)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`Mesh` owns immutable float32 vertices and int32 triangle faces. Winding defines
|
|
84
|
+
the front side. Surface IDs are unique strings independent of display labels;
|
|
85
|
+
IDs containing `_front`, spaces, or Unicode stay unambiguous. `Scene` owns
|
|
86
|
+
ordered surfaces and rigid instance transforms. Editing a label leaves the
|
|
87
|
+
geometry revision unchanged.
|
|
88
|
+
|
|
89
|
+
## Choose outputs and sampling
|
|
90
|
+
|
|
91
|
+
`Query.matrix()` requests all surface rows. `Query.row("wall")` requests one
|
|
92
|
+
sender, and `Query.pair("wall", "roof")` selects one receiver. Receiver selection
|
|
93
|
+
filters outputs; every surface can still occlude rays. `receiver_sides` can
|
|
94
|
+
select `("front",)`, `("back",)`, or both.
|
|
95
|
+
|
|
96
|
+
Add `sky="merged"` or `sky="tregenza145"` to a scene query to trace sky with the
|
|
97
|
+
same rays. `Query.sky(senders=["wall"], discrete=True)` requests only sky outputs.
|
|
98
|
+
Sky patches are indexed `0..144` through `Channel("sky", patch=index)`.
|
|
99
|
+
|
|
100
|
+
`SolveOptions` holds one immutable configuration for a query:
|
|
101
|
+
|
|
102
|
+
- `Sampling`: emission density, rays per cell, seed, face reversal, fair/adaptive
|
|
103
|
+
allocation, and estimator strategy.
|
|
104
|
+
- `Accuracy`: replicate cap, minimum replicate/ray floors, tolerance, and
|
|
105
|
+
convergence check interval.
|
|
106
|
+
- `Postprocessing`: explicit reciprocity transformation after complete solves.
|
|
107
|
+
- `batch_size`: a cap for bounded trace chunks.
|
|
108
|
+
|
|
109
|
+
The default cosine strategy uses independently shifted Halton replicates.
|
|
110
|
+
`mode="fair"` serves each selected sender; `"adaptive"` gives more work to rows
|
|
111
|
+
with larger uncertainty after initial exploration. A minimum ray floor reduces
|
|
112
|
+
zero-hit early stopping but does not prove that rare receivers were observed.
|
|
113
|
+
|
|
114
|
+
For a small selected receiver, use `Sampling(strategy="area_pair",
|
|
115
|
+
pair_samples=8192, sequence="shifted_halton")` with `Query.pair(...)`.
|
|
116
|
+
Unsupported estimator/output combinations raise `CapabilityError`. Area-pair
|
|
117
|
+
sampling weights uniformly sampled surface pairs by their cosine and
|
|
118
|
+
inverse-distance factors and checks visibility against all occluders. It supports
|
|
119
|
+
CPU only; unsupported GPU, sky, or reciprocity combinations raise explicitly.
|
|
120
|
+
`sequence="random"` is also available for this estimator.
|
|
121
|
+
|
|
122
|
+
Meshes can see and occlude themselves: other faces of the emitting surface
|
|
123
|
+
participate in tracing, and `Query.pair("box", "box")` selects its self view
|
|
124
|
+
factor. Matrix/row queries include the same channels. For a closed box with
|
|
125
|
+
outward mesh normals, use `Sampling(flip_faces=True)` to emit into its interior;
|
|
126
|
+
the self factor is 1 on `Channel("surface", "box", "back")`, with zero escape.
|
|
127
|
+
With inward normals, use the front channel and leave `flip_faces=False`.
|
|
128
|
+
Receiver side labels always follow the stored mesh winding.
|
|
129
|
+
|
|
130
|
+
Reciprocity is opt-in through `Postprocessing("bidirectional")` or
|
|
131
|
+
`Postprocessing("shortcut")`, both requiring complete sender/receiver tables.
|
|
132
|
+
The shortcut transformation preserves the v1 upper-direction estimate but v2
|
|
133
|
+
still traces every requested row; it does not halve the workload. Transformed
|
|
134
|
+
uncertainties are unknown and the transformation is recorded in provenance.
|
|
135
|
+
`"rowsum"` additionally requires a closed enclosure without escape or sky.
|
|
136
|
+
Raw previews use `Postprocessing("none")`, the default.
|
|
137
|
+
|
|
138
|
+
## Moving scenes and continued work
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
with Solver(scene, device="cpu", acceleration="instanced") as solver:
|
|
142
|
+
query = Query.row("lower", sky="merged")
|
|
143
|
+
run = solver.start(query, options)
|
|
144
|
+
first = run.advance(Budget(rays=4096))
|
|
145
|
+
refined = run.advance(Budget(rays=12288)) # only additional rays
|
|
146
|
+
assert refined.cumulative_rays == 16384
|
|
147
|
+
|
|
148
|
+
transform = np.eye(4)
|
|
149
|
+
transform[0, 3] = 2
|
|
150
|
+
scene.update_transform("upper", transform)
|
|
151
|
+
new_frame = solver.start(query, options).advance(Budget(rays=4096))
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Transforms are absolute proper rotations and translations relative to the local
|
|
155
|
+
mesh; scale, shear, reflection, invalid matrices, and float32 overflow are
|
|
156
|
+
rejected. `scene.update_vertices(id, world_vertices)` retains topology and resets
|
|
157
|
+
the transform. `scene.update_mesh(id, Mesh(...))` replaces geometry and resets
|
|
158
|
+
the transform. Updates invalidate existing runs before waiting for active work.
|
|
159
|
+
A new run starts a new stream for the new `scene.revision`; old results remain
|
|
160
|
+
immutable snapshots labeled with their original revision.
|
|
161
|
+
|
|
162
|
+
Flat acceleration refits packed triangle bounds when topology is unchanged.
|
|
163
|
+
`acceleration="instanced"` keeps local object BVHs and updates instance transforms
|
|
164
|
+
and a top-level hierarchy. Its hierarchy refits and rebuilds when its measured
|
|
165
|
+
bound cost deteriorates. World emitter frames are prepared lazily; emitter work
|
|
166
|
+
can still scale with changed triangle counts. Share local geometry explicitly:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
prototype = Mesh(vertices, faces)
|
|
170
|
+
instances = Scene.from_instances({"panel": prototype}, [
|
|
171
|
+
("panel0", "panel", np.eye(4)),
|
|
172
|
+
("panel1", "panel", transform),
|
|
173
|
+
])
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Rigid updates retain local geometry/BVH buffers on CPU, Vulkan/Metal, and CUDA.
|
|
177
|
+
Deformation refreshes the affected object, and topology changes rebuild it.
|
|
178
|
+
Instancing reduces update/storage work; tracing speed depends on the scene.
|
|
179
|
+
|
|
180
|
+
`Run.advance` preserves exact ray offsets and completed replicate statistics.
|
|
181
|
+
Accuracy, postprocessing, and batch size can change on a stationary run; sampling
|
|
182
|
+
settings require a new run. Solver configuration and run queries are read-only;
|
|
183
|
+
create a new solver to change the backend. Use `advance(options=...)` to adjust
|
|
184
|
+
accuracy. Ray budgets are additional work. Time budgets are
|
|
185
|
+
soft deadlines checked between chunks and include preparation; cold compilation
|
|
186
|
+
can exceed them. `Budget(rays=0)` returns the current estimate without tracing.
|
|
187
|
+
|
|
188
|
+
`Run.submit(...)` returns a future. Repeated submissions queue additive advances
|
|
189
|
+
through one solver worker. Cancel an obsolete run explicitly before replacing
|
|
190
|
+
it. `Run.cancel()` is terminal: later advances return a cancelled snapshot with
|
|
191
|
+
zero additional rays. Cancellation retains partial estimates. A geometry change
|
|
192
|
+
marks the run `invalidated`; later advances raise and require a new run. Solver
|
|
193
|
+
and run context managers close resources.
|
|
194
|
+
|
|
195
|
+
## Read results and uncertainty
|
|
196
|
+
|
|
197
|
+
`Result.row(sender)` maps typed channels to estimates. Surface, sky, `rest`, and
|
|
198
|
+
`unrequested` channels avoid suffix parsing. Rest contains escaped rays outside
|
|
199
|
+
requested sky; unrequested contains hits omitted by receiver/side selection.
|
|
200
|
+
For cosine queries these channels preserve raw energy accounting.
|
|
201
|
+
|
|
202
|
+
`coverage` aligns with `sender_ids`: `0` means not started, `1` means sampled,
|
|
203
|
+
and `-1` means coverage is unknown in imported data. A requested zero in a
|
|
204
|
+
sampled row is known zero; an unsampled value is `None`. Sparse arrays hold
|
|
205
|
+
estimates and errors, while `.dense()` exports a mutable copy.
|
|
206
|
+
|
|
207
|
+
Estimates include partial replicates. `Result.error(...)` is `None` until there
|
|
208
|
+
are at least two complete randomized replicates and no pending partial replicate.
|
|
209
|
+
`statistics["emitters"]` retains completed-replicate standard errors, sample
|
|
210
|
+
counts, pending rays, and convergence details. Partial work cannot claim
|
|
211
|
+
convergence. Error estimates describe sampling variation, not mesh or floating
|
|
212
|
+
point error, and zero observed hits can underestimate rare-event uncertainty.
|
|
213
|
+
|
|
214
|
+
## Calibration and storage
|
|
215
|
+
|
|
216
|
+
`solver.warmup(query, options)` compiles and measures representative synchronized
|
|
217
|
+
CPU/GPU tracing. Probe rays are diagnostic and do not enter results or ray
|
|
218
|
+
budgets. An unrestricted `Solver.solve` may calibrate automatically; bounded
|
|
219
|
+
runs use cached/provisional plans. Resumed runs retain their numerical backend.
|
|
220
|
+
Warm up before interactive deadlines, or use an explicit device with
|
|
221
|
+
`auto_tune=False` for a fixed execution choice.
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from raystrack.io import save, load, import_v1_json
|
|
225
|
+
|
|
226
|
+
save("case.raystrack", scene, new_frame, metadata={"case": "moving wall"})
|
|
227
|
+
stored = load("case.raystrack")
|
|
228
|
+
legacy = import_v1_json("examples/street_canyon.json", "examples/vf_matrix.json")
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Stores are versioned directories with chunked arrays and a manifest published
|
|
232
|
+
last. Existing stores are never overwritten. V2 preserves stable IDs, shared
|
|
233
|
+
geometry, transforms, sparse channels, statistics, execution, and provenance.
|
|
234
|
+
`load` also reads v1 stores. Legacy imports preserve unknown sampling coverage
|
|
235
|
+
and uncertainty instead of inventing counts. V2 currently loads a full immutable
|
|
236
|
+
snapshot; selective on-disk row loading and appendable writers are unavailable.
|
|
237
|
+
|
|
238
|
+
Rhino conversion is isolated in `raystrack.integrations.rhino` through
|
|
239
|
+
`from_rhino_mesh` and `from_rhino_scene`; importing the numerical package does
|
|
240
|
+
not require Rhino. The converter triangulates quads and copies vertices.
|
|
241
|
+
|
|
242
|
+
## Examples and verification
|
|
243
|
+
|
|
244
|
+
Run examples directly from this checkout, beginning with
|
|
245
|
+
`python examples/ex00_street_canyon_geometry.py`. Examples also build the canyon
|
|
246
|
+
in memory, so generated geometry files are optional. `ex06_dynamic_preview.py`
|
|
247
|
+
accepts `--device`, `--acceleration`, `--budget`, `--frames`, and `--warmup`.
|
|
248
|
+
|
|
249
|
+
```sh
|
|
250
|
+
PYTHONPATH=src python -m pytest tests -q
|
|
251
|
+
python validation/validate_dynamic_backends.py --devices cpu vulkan --accelerations flat instanced
|
|
252
|
+
python validation/benchmark_v2_vs_v1.py --gpu-device vulkan
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The [v2 accuracy report](validation/results/v2_dynamic_accuracy.json) passes 72
|
|
256
|
+
closed-form comparisons across CPU/Vulkan and flat/instanced traversal. The
|
|
257
|
+
[validation guide](validation/readme.md) describes larger analytical checks,
|
|
258
|
+
optional benchmarks and their limits. Performance timing validation was skipped
|
|
259
|
+
at the user's request because background simulations made measurements
|
|
260
|
+
unreliable; this change makes no speedup claim. The [migration guide](https://github.com/philip-ba/raystrack/blob/main/docs/v2-migration.md)
|
|
261
|
+
shows v1-to-v2 replacements.
|
|
@@ -4,17 +4,17 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "raystrack"
|
|
7
|
-
version = "
|
|
8
|
-
description = "
|
|
7
|
+
version = "2.0.0"
|
|
8
|
+
description = "Reusable scene and solver API for Monte Carlo view factors on CPU, CUDA and portable GPUs"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
license = "
|
|
10
|
+
license = "MIT"
|
|
11
11
|
license-files = ["LICENSE"]
|
|
12
12
|
authors = [{ name = "Philip Balizki", email = "philip@metis.earth" }]
|
|
13
|
-
requires-python = ">=3.9,<3.
|
|
13
|
+
requires-python = ">=3.9,<3.14"
|
|
14
14
|
|
|
15
15
|
dependencies = [
|
|
16
16
|
"numpy>=1.24,<3.0",
|
|
17
|
-
"numba>=0.
|
|
17
|
+
"numba>=0.60,<0.65",
|
|
18
18
|
]
|
|
19
19
|
|
|
20
20
|
classifiers = [
|
|
@@ -24,6 +24,10 @@ classifiers = [
|
|
|
24
24
|
"Topic :: Scientific/Engineering",
|
|
25
25
|
]
|
|
26
26
|
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
portable-gpu = ["taichi>=1.7.4,<1.8"]
|
|
29
|
+
cuda = ["numba-cuda>=0.20"]
|
|
30
|
+
|
|
27
31
|
[project.urls]
|
|
28
32
|
Homepage = "https://github.com/philip-ba/raystrack"
|
|
29
33
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Raystrack v2: one scene, one solver, one execution pipeline."""
|
|
2
|
+
from .model import Mesh, Surface, Scene
|
|
3
|
+
from .solver import (Solver, Run, Query, SolveOptions, Sampling, Accuracy,
|
|
4
|
+
Postprocessing, Budget, Channel, SparseValues, Result)
|
|
5
|
+
from .backends import available_devices, ExecutionPlan, CapabilityError
|
|
6
|
+
from .io import save, load, StoredRun, import_v1_json
|
|
7
|
+
|
|
8
|
+
__all__ = ["Mesh", "Surface", "Scene", "Solver", "Run", "Query", "SolveOptions",
|
|
9
|
+
"Sampling", "Accuracy", "Postprocessing", "Budget", "Channel",
|
|
10
|
+
"SparseValues", "Result", "available_devices", "ExecutionPlan",
|
|
11
|
+
"CapabilityError", "save", "load", "StoredRun", "import_v1_json"]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Execution backends and capability reporting."""
|
|
2
|
+
from .devices import available_devices
|
|
3
|
+
from .calibration import ExecutionPlan
|
|
4
|
+
|
|
5
|
+
class CapabilityError(ValueError):
|
|
6
|
+
"""The requested estimator/output is unsupported by this backend."""
|
|
7
|
+
|
|
8
|
+
__all__ = ["available_devices", "ExecutionPlan", "CapabilityError"]
|