cuda: compiler_v2 module becomes compiler

The package already exposed it as CelerisLab.cuda.compiler; the module now
has that name and every tip caller, doc and current-tree test imports it
directly (no alias).

Historical-revision tooling is not edited: the Taylor-Green harness
(run_case.py, hash-pinned check_projected_pairs.py, assess receipts) only
runs against a135252, whose package has cuda/compiler_v2.py, and the J
campaign runners and dated docs describe past trees. Its two current-tree
tests now import that package from git (historical_package) instead of the
tip, and the kernel-root test first checks the accepted case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-10-26 19:15:50 +00:00
co-authored by Claude Opus 5.5
parent 18e8ef45f0
commit d318911221
21 changed files with 65 additions and 28 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ it is; it adds no framework. Line references are to candidate 0f8fdbc3.
| Class | Contents | How it changes | Owner rule |
|---|---|---|---|
| 1. Module and its globals | Compile-time (visible to the compiler, fixed per module): `config.h` macros incl. `NX`/`NY`, `COLLISION_MODEL`, `BLOCK_ROOT_BC`, `WALL_*`. Runtime per-module globals (not visible to the compiler; written by host code): `__constant__` `d_params`, `d_w`, `d_cx`, `d_cy`, `d_block_channel`, `d_block_wall`; plus bound kernel handles | Globals are written once by the owner right after `load_module`. Uniform grid: `Simulation._recompile` (construction, body-topology sync) loads a new module and re-uploads; `update_runtime_params` rewrites `d_params` (omega, force, n_objects) between runs. Steppers: never rewritten after construction, so they are immutable for the instance's lifetime although they are runtime values. The same quantity can be a macro in one route and a global in another (dense slot root: `WALL_SLOT_WIDTH`/`WALL_NORMAL_VELOCITY` macros; blocked slot root: `d_block_wall`), which can change generated arithmetic. | Every owner loads its own module (`compiler_v2.load_module` = `module_from_file`, no cache). A loaded module is never shared between live owners: globals are per module, so sharing would share mutable `d_params`. |
| 1. Module and its globals | Compile-time (visible to the compiler, fixed per module): `config.h` macros incl. `NX`/`NY`, `COLLISION_MODEL`, `BLOCK_ROOT_BC`, `WALL_*`. Runtime per-module globals (not visible to the compiler; written by host code): `__constant__` `d_params`, `d_w`, `d_cx`, `d_cy`, `d_block_channel`, `d_block_wall`; plus bound kernel handles | Globals are written once by the owner right after `load_module`. Uniform grid: `Simulation._recompile` (construction, body-topology sync) loads a new module and re-uploads; `update_runtime_params` rewrites `d_params` (omega, force, n_objects) between runs. Steppers: never rewritten after construction, so they are immutable for the instance's lifetime although they are runtime values. The same quantity can be a macro in one route and a global in another (dense slot root: `WALL_SLOT_WIDTH`/`WALL_NORMAL_VELOCITY` macros; blocked slot root: `d_block_wall`), which can change generated arithmetic. | Every owner loads its own module (`compiler.load_module` = `module_from_file`, no cache). A loaded module is never shared between live owners: globals are per module, so sharing would share mutable `d_params`. |
| 2. Geometry, layout, tables | flags, curved-link tables, block layouts and halo pairs, transfer (explode/coalesce) tables, block kinds | Immutable for the life of an instance. Uniform grid: rebuilt by body sync. `FixedPatchStepper`: a geometry change is a new instance (`remap_child`), committed only after a successful transfer. | No in-place table edits. |
| 3. Controls and dependent boundary columns | Jet amplitude -> per-link `surface_velocity`; body actions (uniform grid) | Between steps, without a table or module rebuild (`set_jet_amplitude`). | Host copy commits only after the device upload succeeded; a device error fails the instance. |
| 4. Per-step state | population double buffers and their parity, raw-exchange ledgers, observations | Device-only during `step(n)` / `run(n)`; buffer swaps are host-handle swaps. | Valid on the host only after the single wait at the end of the call. |
+2 -2
View File
@@ -11,7 +11,7 @@ Responsibilities:
Design::
Packed ``obs`` layout matches ``generate_config(..., n_objects=count)`` and
``config_obs.h`` macros. Host sizing uses :func:`~CelerisLab.cuda.compiler_v2.obs_layout`.
``config_obs.h`` macros. Host sizing uses :func:`~CelerisLab.cuda.compiler.obs_layout`.
Pending edits are lightweight: only the "intent" (add / remove) is stored.
All derived quantities (flags, compact lists, GPU buffers) are built at
@@ -28,7 +28,7 @@ import pycuda.driver as cuda
from .objects import SimObject
from .registry import BodyRegistry
from .preprocess.flag_overlay import merge_flag_masks, validate_cut_link_ownership
from ..cuda.compiler_v2 import obs_layout
from ..cuda.compiler import obs_layout
from ..cuda.obs_layout import ObsLayout
from ..lbm.descriptors import (
FLUID, SOLID,
+1 -1
View File
@@ -7,7 +7,7 @@ Two JSON files:
config_body.json – object list
LBMConfig / BodyConfig are plain dataclasses; they translate to
macro dicts consumed by `cuda/compiler_v2.py` for header generation.
macro dicts consumed by `cuda/compiler.py` for header generation.
"""
import json
+2 -2
View File
@@ -60,14 +60,14 @@ Python `config.py` 只负责读取和校验,不是配置位置。显式配置
**`lbm/kernels/config/*.h` 由编译器根据 `LBMConfig` 自动生成,请勿手改。**
仓库中检入的 `config/*.h` 可能是历史版本。`compiler_v2.generate_config` 会复制 kernel 源码到独立临时构建目录,并在副本中生成配置;不会改写包内头文件或 PTX。返回目录须显式传给 `compile_kernel(build_dir=...)`,编译结果与配置摘要均保存在该目录。`Simulation` 负责目录清理;直接使用编译器的调用者须调用 `cleanup_build_dir()`。跳过生成配置直接编译仓库中的 `uniform.cu` 属不受支持用法。
仓库中检入的 `config/*.h` 可能是历史版本。`compiler.generate_config` 会复制 kernel 源码到独立临时构建目录,并在副本中生成配置;不会改写包内头文件或 PTX。返回目录须显式传给 `compile_kernel(build_dir=...)`,编译结果与配置摘要均保存在该目录。`Simulation` 负责目录清理;直接使用编译器的调用者须调用 `cleanup_build_dir()`。跳过生成配置直接编译仓库中的 `uniform.cu` 属不受支持用法。
```
config_lbm.json
↓ load_lbm_config()
config.py:LBMConfig (读取 + 校验)
↓ to_macros()
cuda/compiler_v2.py (独立构建目录中生成 config/*.h)
cuda/compiler.py (独立构建目录中生成 config/*.h)
↓
config_grid.h → NX, NY, NZ, NT, LATTICE_MODEL;
DIM / NQ 由 LATTICE_MODEL 推导写入(与 grid.lattice_model 一致,非 JSON 顶层字段)
+1 -1
View File
@@ -1,4 +1,4 @@
"""CUDA build helpers (compilation itself does not need a live context)."""
from . import compiler_v2 as compiler
from . import compiler
__all__ = ["compiler"]
@@ -1,4 +1,4 @@
# CelerisLab/cuda/compiler_v2.py
# CelerisLab/cuda/compiler.py
"""
Kernel configuration, compilation, and module loading.
+1 -1
View File
@@ -2,7 +2,7 @@
"""Packed ``obs`` buffer layout for force / torque / sensor telemetry.
Usage::
Host code (e.g. ``ObjectManager``) and ``compiler_v2.generate_config`` must use
Host code (e.g. ``ObjectManager``) and ``compiler.generate_config`` must use
the same ``dim`` and ``n_objects`` so allocation sizes match device macros.
Design::
+1 -1
View File
@@ -17,7 +17,7 @@ from threading import RLock
import numpy as np
import pycuda.driver as cuda
from ..cuda import compiler_v2 as compiler
from ..cuda import compiler
from .blocks import (NQ, BlockLayout2D, blocked_link_tables, launch_curved_boundary,
launch_halo_copy, level_config)
from .descriptors import D2Q9_EX, D2Q9_EY, FLUID, SOLID
+1 -1
View File
@@ -14,7 +14,7 @@ from threading import RLock
import numpy as np
import pycuda.driver as cuda
from ..cuda import compiler_v2 as compiler
from ..cuda import compiler
from .blocks import BlockLayout2D, launch_curved_boundary, launch_halo_copy, level_config
from .channel import channel_masks, validate_channel_config, validate_channel_transfers
from ..body.jets import jet_link_velocity
+1 -1
View File
@@ -25,7 +25,7 @@ import pycuda.driver as cuda
from .config import LBMConfig, BodyConfig, load_lbm_config, load_body_config
from .cuda.context import CudaContext, context_bound
from .cuda import compiler_v2 as compiler
from .cuda import compiler
from .lbm.field import LBMField
from .lbm.stepper import LBMStepper
from .body.objects import SimObject
@@ -6,14 +6,14 @@ import pycuda.driver as cuda
import pytest
import CelerisLab
from CelerisLab.cuda import compiler_v2
from CelerisLab.cuda import compiler
from ._cases import make_sim
def test_source_provenance():
root = Path(__file__).resolve().parents[2] / "src/CelerisLab"
assert Path(CelerisLab.__file__).resolve().parent == root
assert Path(compiler_v2.kernel_path("uniform.cu")).resolve() == root / "lbm/kernels/uniform.cu"
assert Path(compiler.kernel_path("uniform.cu")).resolve() == root / "lbm/kernels/uniform.cu"
def test_two_contexts_interleaved_and_non_lifo_close():
+1 -1
View File
@@ -9,7 +9,7 @@ import pytest
import CelerisLab
from CelerisLab.config import LBMConfig, load_lbm_config
from CelerisLab.cuda import compiler_v2 as compiler
from CelerisLab.cuda import compiler
from CelerisLab.common.checkpoint import save_checkpoint, load_checkpoint
from .test_numerical_operators import compile_run
from .test_state_regressions import device, make_field, manager
@@ -21,7 +21,7 @@ import pycuda.driver as cuda
from CelerisLab.body.geometry.circle import CircleGeometry
from CelerisLab.body.objects import SimObject
from CelerisLab.config import LBMConfig
from CelerisLab.cuda import compiler_v2 as compiler
from CelerisLab.cuda import compiler
from CelerisLab.lbm import block_stepper as implementation
from CelerisLab.lbm.blocks import NO_DONOR, BlockLayout2D
from CelerisLab.lbm.block_stepper import PeriodicBlockStepper
@@ -11,7 +11,7 @@ import numpy as np
import pycuda.driver as cuda
from CelerisLab.config import LBMConfig
from CelerisLab.cuda import compiler_v2 as compiler
from CelerisLab.cuda import compiler
from CelerisLab.cuda.context import CudaContext
from CelerisLab.lbm.blocks import BlockLayout2D
from CelerisLab.lbm.descriptors import FLUID, SOLID, BC_WALL, BC_INLET, BC_OUTLET
@@ -18,7 +18,7 @@ import CelerisLab
from CelerisLab.body.geometry.circle import CircleGeometry
from CelerisLab.body.objects import SimObject
from CelerisLab.config import LBMConfig
from CelerisLab.cuda import compiler_v2 as compiler
from CelerisLab.cuda import compiler
from CelerisLab.cuda.context import CudaContext
from CelerisLab.lbm import refinement_stepper as implementation
from CelerisLab.lbm.descriptors import FLUID, SOLID
@@ -13,7 +13,7 @@ from unittest.mock import Mock, patch
import numpy as np
import pycuda.driver as cuda
from CelerisLab.cuda import compiler_v2 as compiler
from CelerisLab.cuda import compiler
from CelerisLab.lbm import refinement_stepper
from CelerisLab.lbm.blocks import BlockLayout2D
from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY
@@ -15,7 +15,7 @@ import pycuda.driver as cuda
from CelerisLab.body.geometry.circle import CircleGeometry
from CelerisLab.body.objects import SimObject, SurfaceActuation
from CelerisLab.config import LBMConfig
from CelerisLab.cuda import compiler_v2 as compiler
from CelerisLab.cuda import compiler
from CelerisLab.lbm import curved_links
from CelerisLab.lbm.blocks import NO_DONOR, BlockLayout2D, blocked_link_tables
from CelerisLab.lbm.descriptors import D2Q9_EX, D2Q9_EY
@@ -20,7 +20,7 @@ import pycuda.driver as cuda
import CelerisLab
from CelerisLab.config import LBMConfig
from CelerisLab.cuda import compiler_v2 as compiler
from CelerisLab.cuda import compiler
from CelerisLab.cuda.context import CudaContext
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper, PeriodicPatchStepper
@@ -16,7 +16,7 @@ from unittest.mock import Mock, patch
import numpy as np
import pycuda.driver as cuda
from CelerisLab.cuda import compiler_v2 as compiler
from CelerisLab.cuda import compiler
from CelerisLab.lbm.descriptors import SOLID
from CelerisLab.lbm.refinement import ACTIVE, FixedPatch2D, prepare_fine_boundary
from CelerisLab.lbm.refinement_stepper import FixedPatchStepper
@@ -52,7 +52,7 @@ def child_jet(speed=.002):
def mocked_child():
from CelerisLab.cuda import compiler_v2 as compiler
from CelerisLab.cuda import compiler
context = Mock(activate=nullcontext, sm_arch="sm_70")
module = Mock(get_function=Mock(side_effect=lambda name: Mock(name=name)))
module.get_global.return_value = (Mock(), 20)
@@ -1,6 +1,6 @@
"""Host geometry, receipt-corruption tests and mocked scheduling; no CUDA/flow."""
from contextlib import nullcontext, redirect_stdout
from contextlib import contextmanager, nullcontext, redirect_stdout
from copy import deepcopy
from dataclasses import asdict, replace
import hashlib
@@ -24,6 +24,37 @@ from oracle import LIMITS, TIMES, U, rectangles
REPO = Path(__file__).resolve().parents[3]
OLD_REVISION = "519473a61c7b319183aa54e55b9f097499dde0bf"
MOCK_REVISION = "f" * 40
@contextmanager
def historical_package():
"""Import CelerisLab from NUMERICAL_REVISION, the only tree run_case accepts.
The harness and its pinned build-capture helper are tooling for that revision
(cuda/compiler_v2.py); the current package is restored afterwards.
"""
import tarfile
archive = subprocess.run(["git", "-C", str(REPO), "archive", "--format=tar",
NUMERICAL_REVISION, "src/CelerisLab"],
capture_output=True, check=True).stdout
before = set(sys.modules)
saved = {k: v for k, v in sys.modules.items() if k == "CelerisLab" or k.startswith("CelerisLab.")}
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp).resolve()
with tarfile.open(fileobj=io.BytesIO(archive)) as tar:
tar.extractall(root)
for name in saved:
del sys.modules[name]
sys.path.insert(0, str(root / "src"))
try:
yield root
finally:
sys.path.remove(str(root / "src"))
for name in set(sys.modules) - before | set(saved): # nothing keeps the old tree
sys.modules.pop(name, None)
sys.modules.update(saved)
RUNTIME = dict(device=dict(name="host mock", pci_bus_id="mock", capability=[7, 0],
driver=12040, runtime=[12, 4, 0]), nvcc_path="/mock/nvcc",
nvcc_version="mock nvcc", python="mock Python", numpy=np.__version__, pycuda="mock")
@@ -125,6 +156,10 @@ class ProjectedPairsTests(unittest.TestCase):
frozen[name])
def test_actual_build_capture_rejects_tampered_ptx_and_sidecar(self):
with historical_package():
self._check_build_capture()
def _check_build_capture(self):
from CelerisLab.cuda import compiler_v2
from tests.validation.multigrid.check_projected_pairs import capture_builds
@@ -346,10 +381,12 @@ class ProjectedPairsTests(unittest.TestCase):
self.assertFalse(output.exists())
def test_runtime_rejects_installed_kernel_tree(self):
with historical_package() as repo:
from CelerisLab.cuda import compiler_v2
self.assertEqual(run_case.runtime_source(repo)["checkout"], str(repo))
with patch.object(compiler_v2, "_KERNEL_DIR", "/other/CelerisLab/lbm/kernels"):
with self.assertRaisesRegex(RuntimeError, "CUDA source/include root"):
run_case.runtime_source(REPO)
run_case.runtime_source(repo)
def test_existing_output_is_never_overwritten(self):
with tempfile.TemporaryDirectory() as tmp: